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 proper bearer token.
here is my configureServices code
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(
x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("A_VERY_SECRET_SECURITY_KEY_FOR_JWT_AUTH")),
ValidateAudience = false,
ValidateIssuer = false,
};
}
);
services.AddControllers();
services.AddDbContext<dingdogdbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("dingdogdbContext")));
}
and this is how I'm generating token
[AllowAnonymous]
[HttpPost("/Login")]
public ActionResult<User> Login(AuthModel auth)
{
var user = new User();
user.Email = auth.Email;
user.Password = auth.Password;
//var user = await _context.User.SingleOrDefaultAsync(u=> u.Email == auth.Email && u.Password==auth.Password);
//if(user==null) return NotFound("User not found with this creds");
//starting token generation...
var tokenHandler = new JwtSecurityTokenHandler();
var seckey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("A_VERY_SECRET_SECURITY_KEY_FOR_JWT_AUTH"));
var signingCreds = new SigningCredentials(seckey, SecurityAlgorithms.HmacSha256Signature);
var token = tokenHandler.CreateToken(new SecurityTokenDescriptor
{
Subject = new System.Security.Claims.ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Name, user.Id.ToString()) }),
SigningCredentials = signingCreds,
Expires = DateTime.UtcNow.AddDays(7),
});
user.Token = tokenHandler.WriteToken(token);
return user;
}
And I added app.useAuthorization() very after the app.useRouting(). when i'm sending POST request to /Login I'm getting the token. but when I'm using the token in for querying any other endpoint using postman(added the token in authorization/JWT in postman) getting 401 unauthorized every time. is there anything I'm missing still?
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)
Edit: the same code applies to .NET 5 & .NET 6
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
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"
}
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.
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