I have an ASP .NET Core web api and I generate a JWT token for authorization purposes but whenever I make a request with Postman with Bearer token header I get 401 Unauthorized. Same when I try from my front-end that's consuming the API. When I remove Authorize everything works fine
Tried changing Authorize in my header to
[Authorize(AuthenticationSchemes = "Bearer")]
Also visited jwt.io to ensure the JWT Token is valid which it is.
Function that generates JWT Token
public User AuthenticateAdmin(string username, string password) { var user = _context.User .FirstOrDefault(x => x.UserName == username && x.Password == password); //return null if user is not found if (user == null) return null; //authentication successful so generate jwt token var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes(_appSettings.Secret); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Name, user.Id.ToString()), new Claim(ClaimTypes.Role, user.Role) }), Expires = DateTime.UtcNow.AddDays(7), SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); user.Token = tokenHandler.WriteToken(token); user.Password = null; return user; }
Startup :
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.Configure<AuthMessengerOptions>(Configuration); var connection = @"Host=localhost;Database=PayArenaMock;Username=postgres;Password=tim"; services.AddDbContext<PayArenaMockContext>( options => options.UseNpgsql(connection)); services.AddTransient<IEmailSender, EmailSender>(); // services.AddAuthorization(auth => // { // auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder() // .AddAuthenticationSchemes( // JwtBearerDefaults.AuthenticationScheme) // .RequireAuthenticatedUser().Build()); // }); services.AddCors(); // Note - this is on the IMvcBuilder, not the service collection // services.AddMvcCore() // .AddAuthorization() // .AddJsonFormatters(options => options.ContractResolver = // new CamelCasePropertyNamesContractResolver()); // services.AddMvcCore() // .AddJsonFormatters(options => // options.ContractResolver = new CamelCasePropertyNamesContractResolver()); services.AddMvc() .SetCompatibilityVersion( CompatibilityVersion.Version_2_2); //configure strongly typed settings objects var appSettingsSection = Configuration.GetSection("AppSettings"); services.Configure<AppSettings>(appSettingsSection); //configure JWT authentication var appSettings = appSettingsSection.Get<AppSettings>(); var key = Encoding.ASCII.GetBytes(appSettings.Secret); 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(key), ValidateIssuer = false, ValidateAudience = false }; }); services.AddScoped<IUserService,UserService>(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseHsts(); } app.UseCors(x => x .AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader()); app.UseAuthentication(); app.UseHttpsRedirection(); app.UseMvc(); } }
Controller :
//[Authorize(AuthenticationSchemes = "Bearer")] [Authorize] [Route("api/[controller]")] [ApiController] public class BusinessListingsController: ControllerBase { private readonly PayArenaMockContext _context; public BusinessListingsController(PayArenaMockContext context) { _context = context; } // GET: api/BusinessListings [HttpGet] //[AllowAnonymous] //[Authorize(Roles = Role.Admin)] public async Task<ActionResult<IEnumerable<BusinessListing>>> GetBusinessListing() { //var businesslisting = _context.BusinessListing // .Include(b => b.CategoryNameNav); var businesslisting = await _context .BusinessListing .ToListAsync(); return Ok(businesslisting); } }
I fixes mine by changing the UseAuthentication() and order of UseAuthentication() and UseRouting() in the Configure method on Startup class.
Authorization refers to the process that determines what a user is able to do. For example, an administrative user is allowed to create a document library, add documents, edit documents, and delete them. A non-administrative user working with the library is only authorized to read the documents.
I had same issue, but after moveup
app.UseAuthentication();
to before line of
app.UseAuthorization();
in
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { .. app.UseAuthentication(); .. app.UseAuthorization(); ... }
it worked.
I had this issue with dotnet core 3.1, and I was flipping every switch trying to get this to work. Eventually what ended up getting this to run was tletle's answer. Execute app.UseAuthentication()
before app.UseAuthorization()
. To elaborate on tletle's answer, below is the relevant code.
In Startup.cs
:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { // ... app.UseHsts(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); // this one first app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
My authentication middleware is in an extension method I wrote that is called from the ConfigureServices()
method in Startup.cs
:
public static void ConfigureAuthentication(this IServiceCollection services, IConfiguration configuration) { string issuer = configuration.GetValue<string>("Jwt:Issuer"); string signingKey = configuration.GetValue<string>("Jwt:Key"); byte[] signingKeyBytes = System.Text.Encoding.UTF8.GetBytes(signingKey); services.AddAuthentication(opt=> { opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options=> { options.RequireHttpsMetadata = false; options.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters() { ValidateIssuer = true, ValidIssuer = issuer, ValidateAudience = true, ValidAudience = issuer, ValidateLifetime = true, ValidateIssuerSigningKey = true, ClockSkew = System.TimeSpan.Zero, IssuerSigningKey = new SymmetricSecurityKey(signingKeyBytes) }; }); }
and the token was generated using this extension method:
public static string GenerateApiUserToken(this ApiUser user, IConfiguration configuration) { string signingKey = configuration.GetValue<string>("Jwt:Key"); string issuer = configuration.GetValue<string>("Jwt:Issuer"); int hours = configuration.GetValue<int>("Jwt:HoursValid"); System.DateTime expireDateTime = System.DateTime.UtcNow.AddHours(hours); byte[] signingKeyBytes = System.Text.Encoding.UTF8.GetBytes(signingKey); SymmetricSecurityKey secKey = new SymmetricSecurityKey(signingKeyBytes); SigningCredentials creds = new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256); var authClaims = new List<Claim> { new Claim(ClaimTypes.Name, user.UserName), new Claim(ClaimTypes.Role, user.RoleName) }; JwtSecurityToken token = new JwtSecurityToken( issuer:issuer, audience: issuer, claims: authClaims, expires: System.DateTime.UtcNow.AddHours(hours), signingCredentials:creds ); JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); string writtenToken = handler.WriteToken(token); return writtenToken; }
My Controller class:
[Authorize] [ApiController] [Microsoft.AspNetCore.Mvc.Produces("application/json")] [Microsoft.AspNetCore.Mvc.Route("/[controller]/values", Name="MyController")] public class MyController : Microsoft.AspNetCore.Mvc.Controller
If the [Authorize]
tag is on the controller, you should remove any that are on member methods; I left one on the method I was testing and the fix wouldn't work until I removed it.
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