AspNet Identity Core - Custom Claims on Login
up vote
2
down vote
favorite
I'm trying to extend my identity user by adding a logical 'deleted' column in the database.
I then want to use this value to add a claim to user using a custom UserClaimsPrincipalFactory.
I want to check the 'Deleted' claim on login and reject the user if their account has been deleted.
The problem: When I try and access the claims through User.Claims the user has no claims.
The only was I can make it work is by overwriting the httpcontext user
public class ApplicationClaimsIdentityFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
private readonly IHttpContextAccessor _httpContext;
public ApplicationClaimsIdentityFactory(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> options, IHttpContextAccessor httpContext) : base(userManager, roleManager, options)
{
_httpContext = httpContext;
}
public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
ClaimsPrincipal principal = await base.CreateAsync(user);
ClaimsIdentity claimsIdentity = (ClaimsIdentity) principal.Identity;
claimsIdentity.AddClaim(new Claim("Deleted", user.Deleted.ToString().ToLower()));
//I DON'T WANT TO HAVE TO DO THIS
_httpContext.HttpContext.User = principal;
return principal;
}
}
Login Action:
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
SignInResult result = await _signInManager.PasswordSignInAsync(model.Email, model.Password,
model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
//No claims exists at this point unless I force the HttpContext user (See above)
if (User.Claims.First(x => x.Type == "Deleted").Value.Equals("true", StringComparison.CurrentCultureIgnoreCase);)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
await _signInManager.SignOutAsync();
return View(model);
}
.... Continue login code...
My ApplicationUser class
public class ApplicationUser : IdentityUser
{
public bool Deleted { get; set; }
}
And finally my startup registration
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<DbContext>()
.AddClaimsPrincipalFactory<ApplicationClaimsIdentityFactory>()
.AddDefaultTokenProviders();
Thanks
c# asp.net-core identity claims-based-identity
add a comment |
up vote
2
down vote
favorite
I'm trying to extend my identity user by adding a logical 'deleted' column in the database.
I then want to use this value to add a claim to user using a custom UserClaimsPrincipalFactory.
I want to check the 'Deleted' claim on login and reject the user if their account has been deleted.
The problem: When I try and access the claims through User.Claims the user has no claims.
The only was I can make it work is by overwriting the httpcontext user
public class ApplicationClaimsIdentityFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
private readonly IHttpContextAccessor _httpContext;
public ApplicationClaimsIdentityFactory(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> options, IHttpContextAccessor httpContext) : base(userManager, roleManager, options)
{
_httpContext = httpContext;
}
public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
ClaimsPrincipal principal = await base.CreateAsync(user);
ClaimsIdentity claimsIdentity = (ClaimsIdentity) principal.Identity;
claimsIdentity.AddClaim(new Claim("Deleted", user.Deleted.ToString().ToLower()));
//I DON'T WANT TO HAVE TO DO THIS
_httpContext.HttpContext.User = principal;
return principal;
}
}
Login Action:
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
SignInResult result = await _signInManager.PasswordSignInAsync(model.Email, model.Password,
model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
//No claims exists at this point unless I force the HttpContext user (See above)
if (User.Claims.First(x => x.Type == "Deleted").Value.Equals("true", StringComparison.CurrentCultureIgnoreCase);)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
await _signInManager.SignOutAsync();
return View(model);
}
.... Continue login code...
My ApplicationUser class
public class ApplicationUser : IdentityUser
{
public bool Deleted { get; set; }
}
And finally my startup registration
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<DbContext>()
.AddClaimsPrincipalFactory<ApplicationClaimsIdentityFactory>()
.AddDefaultTokenProviders();
Thanks
c# asp.net-core identity claims-based-identity
add a comment |
up vote
2
down vote
favorite
up vote
2
down vote
favorite
I'm trying to extend my identity user by adding a logical 'deleted' column in the database.
I then want to use this value to add a claim to user using a custom UserClaimsPrincipalFactory.
I want to check the 'Deleted' claim on login and reject the user if their account has been deleted.
The problem: When I try and access the claims through User.Claims the user has no claims.
The only was I can make it work is by overwriting the httpcontext user
public class ApplicationClaimsIdentityFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
private readonly IHttpContextAccessor _httpContext;
public ApplicationClaimsIdentityFactory(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> options, IHttpContextAccessor httpContext) : base(userManager, roleManager, options)
{
_httpContext = httpContext;
}
public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
ClaimsPrincipal principal = await base.CreateAsync(user);
ClaimsIdentity claimsIdentity = (ClaimsIdentity) principal.Identity;
claimsIdentity.AddClaim(new Claim("Deleted", user.Deleted.ToString().ToLower()));
//I DON'T WANT TO HAVE TO DO THIS
_httpContext.HttpContext.User = principal;
return principal;
}
}
Login Action:
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
SignInResult result = await _signInManager.PasswordSignInAsync(model.Email, model.Password,
model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
//No claims exists at this point unless I force the HttpContext user (See above)
if (User.Claims.First(x => x.Type == "Deleted").Value.Equals("true", StringComparison.CurrentCultureIgnoreCase);)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
await _signInManager.SignOutAsync();
return View(model);
}
.... Continue login code...
My ApplicationUser class
public class ApplicationUser : IdentityUser
{
public bool Deleted { get; set; }
}
And finally my startup registration
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<DbContext>()
.AddClaimsPrincipalFactory<ApplicationClaimsIdentityFactory>()
.AddDefaultTokenProviders();
Thanks
c# asp.net-core identity claims-based-identity
I'm trying to extend my identity user by adding a logical 'deleted' column in the database.
I then want to use this value to add a claim to user using a custom UserClaimsPrincipalFactory.
I want to check the 'Deleted' claim on login and reject the user if their account has been deleted.
The problem: When I try and access the claims through User.Claims the user has no claims.
The only was I can make it work is by overwriting the httpcontext user
public class ApplicationClaimsIdentityFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
private readonly IHttpContextAccessor _httpContext;
public ApplicationClaimsIdentityFactory(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> options, IHttpContextAccessor httpContext) : base(userManager, roleManager, options)
{
_httpContext = httpContext;
}
public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
ClaimsPrincipal principal = await base.CreateAsync(user);
ClaimsIdentity claimsIdentity = (ClaimsIdentity) principal.Identity;
claimsIdentity.AddClaim(new Claim("Deleted", user.Deleted.ToString().ToLower()));
//I DON'T WANT TO HAVE TO DO THIS
_httpContext.HttpContext.User = principal;
return principal;
}
}
Login Action:
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
SignInResult result = await _signInManager.PasswordSignInAsync(model.Email, model.Password,
model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
//No claims exists at this point unless I force the HttpContext user (See above)
if (User.Claims.First(x => x.Type == "Deleted").Value.Equals("true", StringComparison.CurrentCultureIgnoreCase);)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
await _signInManager.SignOutAsync();
return View(model);
}
.... Continue login code...
My ApplicationUser class
public class ApplicationUser : IdentityUser
{
public bool Deleted { get; set; }
}
And finally my startup registration
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<DbContext>()
.AddClaimsPrincipalFactory<ApplicationClaimsIdentityFactory>()
.AddDefaultTokenProviders();
Thanks
c# asp.net-core identity claims-based-identity
c# asp.net-core identity claims-based-identity
asked Nov 19 at 17:02
SkelDave
6861619
6861619
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
up vote
3
down vote
accepted
I think the problem is you are trying to do it all in one request when the login action is posted.
The User you have in that method is a claimsprincipal but is not authenticated, it was deserialized from the request by the auth middleware before the code to SignIn was invoked and before your claimsprincipal factory method was called.
The signin method did create the new authenticated claimsprincipal and should have serialized it into the auth cookie, so on the next request the User would be deserialized from the cookie and would be authenticated, but that deserialization already happened for the current request. So the only way to change it for the current request is to reset the User on the current httpcontext as you have found.
I think it would be better to reject the user a different way not after login success, it would be better to check that at a lower level and make login fail, rather than succeed and then signout. I did this in my project in a custom userstore.
Thanks - this really helped show where I was going wrong. I've implemented my own SignInManager CanSignInAsync method and handled it at that level.
– SkelDave
Nov 20 at 9:42
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
accepted
I think the problem is you are trying to do it all in one request when the login action is posted.
The User you have in that method is a claimsprincipal but is not authenticated, it was deserialized from the request by the auth middleware before the code to SignIn was invoked and before your claimsprincipal factory method was called.
The signin method did create the new authenticated claimsprincipal and should have serialized it into the auth cookie, so on the next request the User would be deserialized from the cookie and would be authenticated, but that deserialization already happened for the current request. So the only way to change it for the current request is to reset the User on the current httpcontext as you have found.
I think it would be better to reject the user a different way not after login success, it would be better to check that at a lower level and make login fail, rather than succeed and then signout. I did this in my project in a custom userstore.
Thanks - this really helped show where I was going wrong. I've implemented my own SignInManager CanSignInAsync method and handled it at that level.
– SkelDave
Nov 20 at 9:42
add a comment |
up vote
3
down vote
accepted
I think the problem is you are trying to do it all in one request when the login action is posted.
The User you have in that method is a claimsprincipal but is not authenticated, it was deserialized from the request by the auth middleware before the code to SignIn was invoked and before your claimsprincipal factory method was called.
The signin method did create the new authenticated claimsprincipal and should have serialized it into the auth cookie, so on the next request the User would be deserialized from the cookie and would be authenticated, but that deserialization already happened for the current request. So the only way to change it for the current request is to reset the User on the current httpcontext as you have found.
I think it would be better to reject the user a different way not after login success, it would be better to check that at a lower level and make login fail, rather than succeed and then signout. I did this in my project in a custom userstore.
Thanks - this really helped show where I was going wrong. I've implemented my own SignInManager CanSignInAsync method and handled it at that level.
– SkelDave
Nov 20 at 9:42
add a comment |
up vote
3
down vote
accepted
up vote
3
down vote
accepted
I think the problem is you are trying to do it all in one request when the login action is posted.
The User you have in that method is a claimsprincipal but is not authenticated, it was deserialized from the request by the auth middleware before the code to SignIn was invoked and before your claimsprincipal factory method was called.
The signin method did create the new authenticated claimsprincipal and should have serialized it into the auth cookie, so on the next request the User would be deserialized from the cookie and would be authenticated, but that deserialization already happened for the current request. So the only way to change it for the current request is to reset the User on the current httpcontext as you have found.
I think it would be better to reject the user a different way not after login success, it would be better to check that at a lower level and make login fail, rather than succeed and then signout. I did this in my project in a custom userstore.
I think the problem is you are trying to do it all in one request when the login action is posted.
The User you have in that method is a claimsprincipal but is not authenticated, it was deserialized from the request by the auth middleware before the code to SignIn was invoked and before your claimsprincipal factory method was called.
The signin method did create the new authenticated claimsprincipal and should have serialized it into the auth cookie, so on the next request the User would be deserialized from the cookie and would be authenticated, but that deserialization already happened for the current request. So the only way to change it for the current request is to reset the User on the current httpcontext as you have found.
I think it would be better to reject the user a different way not after login success, it would be better to check that at a lower level and make login fail, rather than succeed and then signout. I did this in my project in a custom userstore.
answered Nov 19 at 17:30
Joe Audette
16k55775
16k55775
Thanks - this really helped show where I was going wrong. I've implemented my own SignInManager CanSignInAsync method and handled it at that level.
– SkelDave
Nov 20 at 9:42
add a comment |
Thanks - this really helped show where I was going wrong. I've implemented my own SignInManager CanSignInAsync method and handled it at that level.
– SkelDave
Nov 20 at 9:42
Thanks - this really helped show where I was going wrong. I've implemented my own SignInManager CanSignInAsync method and handled it at that level.
– SkelDave
Nov 20 at 9:42
Thanks - this really helped show where I was going wrong. I've implemented my own SignInManager CanSignInAsync method and handled it at that level.
– SkelDave
Nov 20 at 9:42
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53379463%2faspnet-identity-core-custom-claims-on-login%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown