I'm working on .NET Core MVC project with Identity. I have a working project currently with normal cookie-based authentication, excerpt from Identity config:
public class IdentityHostingStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) => {
services.AddDbContext<TauManagerIdentityDbContext>(options =>
options.UseNpgsql(
context.Configuration.GetConnectionString("TauManagerIdentityDbContextConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddRoles<IdentityRole>()
.AddRoleManager<ApplicationRoleManager>()
.AddEntityFrameworkStores<TauManagerIdentityDbContext>()
.AddUserManager<ApplicationIdentityUserManager>()
.AddDefaultUI()
.AddDefaultTokenProviders();
});
}
}
I'm using AuthorizeAttribute
to control access of different roles to different actions in my web application.
Now I am now facing a situation where I have to use some kind of token-based authentication for one specific action only. I have read several articles and questions at SO about JWT setup with .NET Core Identity, the closest one I've found to my case is Using Identity with token and cookie authentication.
However, I have two questions:
new Claim(JwtRegisteredClaimNames.Sub, user.Email)
to the list of claims when generating tokens. I do NOT collect users' emails at all, and this is a deliberate decision I'd like to keep. Is there any way to use e.g. username instead?Thanks in advance!
Is this indeed the simplest method of generating auth tokens for such scenario?
I assume this is in reference to the code in the question Using Identity with token and cookie authentication :
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Tokens:Issuer"],
_config["Tokens:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
When Creating a JWT the two piece of information you have to provide as a programmer are the Claims and how the Signature is created. In the previous code, the claims
as the claims you need and the key
and creds
are required to create the signature. To create a full token, you use the JwtSecurityToken
class to create the token. I don't know how this can be any simpler.
Is there any way to use e.g. username instead?
Claims are whatever you want them to be. The System.IdentityModel.Tokens.Jwt Namespace has few built-in claim names you can use from JwtRegisteredClaimNames Struct.
So you could use one of the following:
JwtRegisteredClaimNames.NameId
JwtRegisteredClaimNames.Sid
JwtRegisteredClaimNames.UniqueName
or you can create your own.
I think the underlying question is, how do I make sure my JWT authorizes automatically against ASP.Net Identity. If you're using cookies, you should be able to look at the ClaimsPrinciple
and determine what claims are being used by Identity to Authenticate a request and put those same claims in your JWT.
Update 1
I'm currently only using JWT with Angular for a project. My code won't be complete because some of it will be specific to only using JWT/Bearer but it should help. I believe the most important part is the AddJwtBearer
which if I'm not mistaken, will look for a bearer
header valid/decode and populate the httpcontext.user
with a ClaimsPrincipal
with all the associated claims. These claims can be used with the AuthorizeAttribute
to do authorization (claims and/or policies).
StartUp.cs
public void ConfigureServices(IServiceCollection services)
{
services
.Configure<JwtIssuerOptions>(jwtIssuerOptionsConfig)
.Configure<JwtIssuerOptions>(options =>
{
options.SigningCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha512);
});
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.IncludeErrorDetails = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ClockSkew = TimeSpan.FromMinutes(5),
IssuerSigningKey = symmetricSecurityKey,
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateLifetime = true,
ValidAudience = jwtIssuerOptions.Audience,
ValidateIssuer = true,
ValidIssuer = jwtIssuerOptions.Issuer
};
if (_isDevelopment)
{
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = c =>
{
Debug.WriteLine(c.Exception.Message);
return Task.CompletedTask;
},
};
}
});