I\'m trying to implement JWT authentication on my asp.net core webAPI as simply as possible.
I don\'t know what i\'m missing but it\'s always returning 401 even with the
First you need to check that the JWT token generated using your configureServices code is valid or not.To validate JWT token you can use JWT debugger. It will parse the JWT token value into each parameter by which you can verify that which of the parameter values assigned incorrectly and JWT debugger also provide you JWT valid or invalid. Once you figure this out you can work on identified errors or next course of action.
Step 1 : First make sure the order of the configure method in the stratup.cs class :
below i have given the valid order form for asp.net core 3.1
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
If Step one not working then try Step 2: make sure the token validation parameter and the token generation parameter & algorithm are same for that go to the ConfigureServices method of the startup.cs class and also go to the class or method where you have generated the token in my case it was UserService class
ConfigureServices method code :
public void ConfigureServices(IServiceCollection services)
{
var connectionString = Configuration.GetConnectionString("mySQLConnectionString");
services.AddDbContext<ApplicationDbContext>(options => options.UseMySql(connectionString));
services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequiredLength = 5;
}).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = Configuration["AuthSettings:Audience"],
ValidIssuer = Configuration["AuthSettings:Issuer"],
RequireExpirationTime = true,
IssuerSigningKey =
new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(Configuration["AuthSettings:key"])),
ValidateIssuerSigningKey = true,
};
});
services.AddScoped<IUserService, UserService>();
services.AddControllers();
}
Token Generation code :
public async Task<UserManagerResponse> LoginUserAsync(LoginVIewModel model)
{
var user = await _userManager.FindByEmailAsync(model.Email);
if(user == null)
{
return new UserManagerResponse
{
Message = "There is no user with that email",
iSSuccess= false
};
}
var result = await _userManager.CheckPasswordAsync(user, model.Password);
if(! result)
{
return new UserManagerResponse
{
Message = "Your Provided password not match eith our system ",
iSSuccess = false
};
}
var clims = new[]
{
new Claim("Email", model.Email),
new Claim(ClaimTypes.NameIdentifier, user.Id)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["AuthSettings:key"]));
var token = new JwtSecurityToken(
issuer: _configuration["AuthSettings:Issuer"],
audience: _configuration["AuthSettings:Audience"],
claims: clims,
expires: DateTime.Now.AddDays(30),
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
);
string tokenAsString = new JwtSecurityTokenHandler().WriteToken(token);
return new UserManagerResponse
{
Message = tokenAsString,
iSSuccess = true,
ExpireDate = token.ValidTo
};
}
}
also please note that , In my case I have some spelling mistake in appsetting.json For example in the token generate code i have called the Audince but in the appSetting.json it was Audience . thats why both Audience not match .
audience: _configuration["AuthSettings:Audince"]
Appsetting.json code :
"AllowedHosts": "*",
"AuthSettings": {
"key": "TThis is mfw sec test token",
"Audience": "www.mfw.com",
"Issuer": "www.mfw.com"
}
There are some other issues here, that you may want to take a look at and potentially improve. The login mechanism currently contains a token that has a 7 days expiry. That means exposed tokens will still allow an attacker to access and impersonate the user for 7 days. In general it would be better to:
This gives the user the ability to "log out" of all sessions in case something is compromised. Specifically these sorts of functionality and more are available by most authentication providers such as Auth0 or authorization providers such as Authress.
Keep in mind that the UseAuthentication
, UseRouting
and UseAuthorization
middleware must in correct in order for the ASP framework properly inject the identity context to http request.
It should look like this: (.NET Core 3.1)
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});