I've been looking around and trying to do more research on .NET Core Identity (https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.1&tabs=visual-studio%2Caspnetcore2x) and Jwt (json web tokens). I've been rolling with the default Identity as authentication/authorization in my .NET Core 2.0 app and it has been working well so far.
I'm running into a roadblock and I think it's the way of my understanding of .NET Core identity and jwt. My application has MVC and an web api. I would ideally like to secure the web api, but I hear the best way to do that now is through jwt. Good - cool.
I can go ahead and configure jwt and then use it as my authentication/authorization (https://blogs.msdn.microsoft.com/webdev/2017/04/06/jwt-validation-and-authorization-in-asp-net-core/), but - do I need to go ahead and spin up a new server to serve as the authorization server for jwt? If so, I'm not going to do that (too expensive).
What about my .NET Core identity code if I do go with jwt? Does that have to go away then? If it can co-exist, how might I authorize my MVC pages with Identity and my api endpoints with jwt?
I realize this is an open-ended question, but the core of it is:
Can .NET Core Identity and JWT co-exist? Or do I have to choose one or the other? I have MVC and an web api and would like to secure both.
Yes, you can. The logic process is in this method:
Step 1: GetUserClaims
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
Into GetClaimsIdentity you will
private async Task<ClaimsIdentity> GetClaimsIdentity(string userName, string password)
{
if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
return await Task.FromResult<ClaimsIdentity>(null);
var userToVerify = await _userManager.FindByNameAsync(userName);
if (userToVerify == null) {
userToVerify = await _userManager.FindByEmailAsync(userName);
if (userToVerify == null) {
return await Task.FromResult<ClaimsIdentity>(null);
}
}
// check the credentials
if (await _userManager.CheckPasswordAsync(userToVerify, password))
{
_claims = await _userManager.GetClaimsAsync(userToVerify);
return await Task.FromResult(_jwtFactory.GenerateClaimsIdentity(userToVerify.UserName, userToVerify.Id, _claims));
}
// Credentials are invalid, or account doesn't exist
return await Task.FromResult<ClaimsIdentity>(null);
}
Step 2: Group all user claims you need add to the token - Use System.Security.Claims
public ClaimsIdentity GenerateClaimsIdentity(string userName, string id, IList<Claim> claims)
{
claims.Add(new Claim(Helpers.Constants.Strings.JwtClaimIdentifiers.Id, id));
// If your security is role based you can get then with the RoleManager and add then here as claims
// Ask here for all claims your app need to validate later
return new ClaimsIdentity(new GenericIdentity(userName, "Token"), claims);
}
Step 3: Then back on your method you have to generate and return the JWT Token
jwt = await jwtFactory.GenerateEncodedToken(userName, identity);
return new OkObjectResult(jwt);
To generate token do something like this:
public async Task<string> GenerateEncodedToken(string userName, ClaimsIdentity identity)
{
List<Claim> claims = new List<Claim>();
//Config claims
claims.Add(new Claim(JwtRegisteredClaimNames.Sub, userName));
claims.Add(new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()));
claims.Add(new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64));
//End Config claims
claims.AddRange(identity.FindAll(Helpers.Constants.Strings.JwtClaimIdentifiers.Roles));
claims.AddRange(identity.FindAll("EspecificClaimName"));
// Create the JWT security token and encode it.
var jwt = new JwtSecurityToken(
issuer: _jwtOptions.Issuer,
audience: _jwtOptions.Audience,
claims: claims,
notBefore: _jwtOptions.NotBefore,
expires: _jwtOptions.Expiration,
signingCredentials: _jwtOptions.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
return encodedJwt;
}
There are many ways to do this. The most common is: Validate Identity User --> Get User identifiers --> Generate and Return Token Based on Identifiers --> Use Authorization for endpoints
Hope this help
You can validate the username and password and generate the Jwt.
First, make sure your API has the following default identity set up in the startup.cs:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Secondly, you can validate the login with something like this:
You can set up an API controller something like this:
[ApiController, Route("check")]
public class TokenController : ControllerBase
{
private readonly SignInManager<IdentityUser> signin;
public TokenController(SignInManager<IdentityUser> signin)
{
this.signin = signin;
}
[HttpGet]
public async Task<string> Get(string user, string pass)
{
var result = await signin.PasswordSignInAsync(user, pass, true, false);
if (result.Succeeded)
{
string token = "";
return token;
}
return null;
}
}
Within your get function, you can now generate your Jwt.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With