Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.Net Core API always returns 401 unauthorized whenever I send a request with Bearer token included

Tags:

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);     }      } 
like image 794
Timothy Avatar asked May 17 '19 11:05

Timothy


People also ask

How do I fix the 401 unauthorized error in asp net core?

I fixes mine by changing the UseAuthentication() and order of UseAuthentication() and UseRouting() in the Configure method on Startup class.

What is authorization in asp net core?

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.


2 Answers

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.

like image 158
tletle Avatar answered Sep 19 '22 10:09

tletle


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.

like image 40
jamesstap Avatar answered Sep 20 '22 10:09

jamesstap