I have an existing database with some login information where the passwords are stored using LibSodium
Using a custom-built SignInManager<>
I want to verify passwords and sign customers in. Later, after a successful sign-in, I plan to transition passwords to a native .NET password hasher.
I use new
and Iden
to replace the standard SignInResult
with an enum
with extra return values (like SignInResult.NotFound
)
public class CustomSignInManager : SignInManager<CustomAccount>
{
// REMOVED FOR BREVITY
public new async Task<Iden.SignInResult> PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure)
{
if (userName == null) throw new ArgumentNullException(nameof(userName));
if (password == null) throw new ArgumentNullException(nameof(password));
var user = await UserManager.FindByEmailAsync(userName);
if (user == null) return Iden.SignInResult.NotFound;
if (user.Status != AccountStatus.Verified) return Iden.SignInResult.NotAllowed;
if (user.EncryptionType == AccountEncryptionType.LibSodium)
if (Sodium.PasswordHash.ScryptHashStringVerify(user.EncryptedPassword, password))
return Iden.SignInResult.Success;
return (await base.PasswordSignInAsync(userName, password, isPersistent, lockoutOnFailure)).Convert();
//.Convert() => converts from Microsoft.AspNetCore.Identity.SignInResult to my enum
}
}
public async Task<IActionResult> OnPostAsync(string? returnUrl = null)
{
_userHistory = new LoginHistory(_config, Request);
ReturnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
switch (await _signInManager.PasswordSignInAsync(Email, Password, RememberMe, lockoutOnFailure: true))
{
case Iden.SignInResult.Success:
// REMOVED FOR BREVITY
return LocalRedirect(ReturnUrl);
// REMOVED FROM BREVITY
}
}
// If we got this far, something failed, redisplay form
return Page();
}
services
.AddIdentity<CustomerAccount, IdentityRole>(options => {
})
.AddEntityFrameworkStores<SiteContext>()
.AddUserStore<CustomerAccountStore>()
.AddSignInManager<CustomerSignInManager>()
.AddDefaultTokenProviders();
Even though I get a SignInResult.Success
after the "Sign In Call", I can't see no set-cookie
in the response. Plus, on the next page _signInManager.IsSignedIn(User)
is false
!
I thought it might have been because the domain requires signing in (windows user auth) to gain access (which messed with User.Identity?.IsAuthenticated
!)
First of all 🤦♂️, I'm a numpty.
Before returning my Success
response I needed to call await SignInAsync(user, isPersistent);
. It's within this method that it does the necessary work to save the data (cookie) that I needed.
There's also SignInOrTwoFactorAsync
for those with TFA (soon to be me, if I stop wasting time on stupidity that is)
if (Sodium.PasswordHash.ScryptHashStringVerify(user.EncryptedPassword, password))
{
// TODO: TFA setup and switch to SignInOrTwoFactorAsync
await SignInAsync(user, isPersistent);
return Iden.SignInResult.Success;
}