Introduction
I've been working for the last couple of days on small pet-project with a goal of learning .NET Core 2.0 with Identity backed by Entity Framework Core. It is a typical "WebAPI" type project with cookie based authentication and claims based authorization. It is utilized by some client application (SPA).
Code
Authorization & Authentication flow is configured this way in Startup.cs
services
.AddIdentity<ApplicationUser, IdentityRole> ()
.AddEntityFrameworkStores<ApplicationDbContext> ()
.AddDefaultTokenProviders ();
services
.AddAuthentication (sharedOptions => {
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie ();
My login controller action looks like this:
[HttpPost]
[Route ("login")]
public async Task<IActionResult> Login ([FromBody] LogInCredentialsModel credentials) {
// Get User for given UserName
var user = await userManager.Users.FirstOrDefaultAsync (p => p.UserName == credentials.UserName);
//User not found
if (user == default (ApplicationUser))
return StatusCode (400);
// Check if password is correct
var result = await signInManager.PasswordSignInAsync (user, credentials.Password, true, false);
if (result.Succeeded) {
//Basic claims with Name and Email
List<Claim> claims = new List<Claim> {
new Claim (ClaimTypes.Name, user.UserName),
new Claim (ClaimTypes.Email, user.Email)
};
var userRoles = await this.GetUserRoles (user); // Custom helper method to get list of user roles
// Add Role claims
foreach (var role in userRoles) {
claims.Add (new Claim (ClaimTypes.Role, role));
}
ClaimsIdentity identity = new ClaimsIdentity (claims, CookieAuthenticationDefaults.AuthenticationScheme);
ClaimsPrincipal principal = new ClaimsPrincipal (identity);
// Sign in using cookie scheme
await HttpContext.SignInAsync (CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties {
IsPersistent = true,
});
return Ok ();
} else {
return StatusCode (400);
}
}
Problems
- These claims will be stored in encrypted user cookie. This means, that if I remove some claim from user and he does not re-log, he will still have old claims assigned. How do I prevent that? Or did I misunderstand the design?
- User passes UserName and Password to login route which is then used to sign him in. In my code, I have to first find user with given UserName (1st db hit), then try to sigin in with password using SignInManager (2nd db hit), read roles (3rd db hit) to build ClaimsPrincipal and then use HttpContext.SignInAsync so user cookie with correct claims is created. I personally feel like I'm missing something and in the result my code is overcomplicated, also atleast one of the database queries could be saved here. How to improve this part?
ClaimsPrincipal
by hand this way? that should be handled for you by the framework. – trailmax