I have implemented a custom UserStore
, it implements IUserStore<DatabaseLogin, int>
and IUserPasswordStore<DatabaseLogin, int>
.
My Login action method is as below:
if (ModelState.IsValid)
{
if (Authentication.Login(user.Username, user.Password))
{
DatabaseLogin x = await UserManager.FindAsync(user.Username, user.Password);
DatabaseLogin Login = Authentication.FindByName(user.Username);
if (Login != null)
{
ClaimsIdentity ident = await UserManager.CreateIdentityAsync(Login,
DefaultAuthenticationTypes.ApplicationCookie);
AuthManager.SignOut();
AuthManager.SignIn(new AuthenticationProperties
{
IsPersistent = false
}, ident);
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "Invalid Login");
}
}
return View();
In the custom authentication class that I wrote, Authentication
, I have a Login method that works fine, also FindByName
method returns an app user. But if I try to SignIn
with that login, the user isn't recognized as authenticated and HttpContext.User.Identity
is always null, so I imagine that I have to try UserManager.FindAsync
.
This method calls FindByNameAsync
and GetPasswordHashAsync
, and it always return null.
public Task<DatabaseLogin> FindByNameAsync(string userName)
{
if (string.IsNullOrEmpty(userName))
throw new ArgumentNullException("userName");
return Task.FromResult<DatabaseLogin>(Authentication.FindByName(userName));
}
public Task<string> GetPasswordHashAsync(DatabaseLogin user)
{
if (user == null)
throw new ArgumentNullException("user");
return Task.FromResult<string>(user.Password);
}
And the Authentication.FindByName
public static DatabaseLogin FindByName(string name)
{
string GetUserQuery = string.Format(
"USE db;SELECT principal_id AS id, name as userName, create_date AS CreateDate, modify_date AS modifyDate FROM sys.database_principals WHERE type='S' AND authentication_type = 1 AND name = '{0}'"
, name);
DatabaseLogin user;
using (var db = new EFDbContext())
{
user = db.Database.SqlQuery<DatabaseLogin>(GetUserQuery).FirstOrDefault();
}
user.Password = Convert.ToBase64String(Encoding.ASCII.GetBytes("pass"));
return user;
}
As you can see I'm using database users, I'm not sure how I can retrieve a hashed password for them. For now, I'm just storing the Base65 of the correct password!
I have no idea where I'm going wrong, any guidance is welcome.
Short answer: nothing's wrong. User is authenticated in other action methods, but apparently not in the current action method.
This is the process that I followed, maybe it will help you debug your app.
After reading the source code, FindAsync
first calls FindByNameAsync
, followed by CheckPasswordAsync
which references VerifyPasswordAsync
. So it should be fine If I could override VerifyPasswordAsync
.
I created a custom password hasher
that implements IPasswordHasher
, and registered it in the create
method of my UserManager
like this:
manager.PasswordHasher = new DbPasswordHasher();
So by now, I can get my user from UserManager.FindAsync
, but it turned out that it doesn't matter where you get the user since HttpContext.User.Identity
is still null! My mistake was that I didn't notice the user isn't authenticated in the current action, in other action methods it works as expected!