ASP.Net Core API always returns 401 unauthorized whenever I send a request with Bearer token included
Solution 1
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.
Solution 2
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.
Solution 3
Once I had the same problem after I switched from HTTP to HTTPS. It was working fine in POSTMAN with HTTP but when I switched to HTTPS it began to return 401 unauthorized
The problem was solved as soon as I added
app.UseAuthentication();
inside
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
}
within the
public class Startup
{
}
Solution 4
It works finally, not a 100% sure why but I did headers: { Authorization: "Bearer" + " " + Bearer }
in my react app. I do the equivalent in Postman and it doesn't work.
Solution 5
I had this same issue and this was what i did on postman, in the header section using bulk edit.
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImRhbmllbEBtZ2ljLmNvbToxMTExMTE6IiwibmJmIjoxNTcwNjIxMDE5LCJleHAiOjE1NzMyOTk0MTksImlhdCI6MTU3MDYyMTAxOX0.cTjhhHqf75VN0RwQvly6nppeNUkKbzQ5_ZVPFyruuKPBQ
Also remember to include the authentication filter at the top of your endpoints, which is [Authorize] in your case.
Timothy
Updated on January 30, 2020Comments
-
Timothy about 4 years
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 went to jwt.io to ensure the JWT Token is valid which it is.//function where I generate JWT 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; }
//my startup.cs using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.EntityFrameworkCore; using TheBackend.Models; using TheBackend.Helpers; using Microsoft.IdentityModel.Tokens; using Microsoft.AspNetCore.Authentication.JwtBearer; using System.Text; using TheBackend.Services; using Microsoft.AspNetCore.Identity.UI.Services; using Newtonsoft.Json.Serialization; using Microsoft.AspNetCore.Authorization; namespace TheBackend { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. 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(); //services.AddMvcCore() // .AddAuthorization() // Note - this is on the IMvcBuilder, not the service collection // .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>(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 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); }
-
ashlar64 over 3 yearsArgh...spent like a day and a half and when I did your fix it worked. I sometimes feel like I spend more time figuring out stupid issues like this than actual programming.
-
Admin over 3 yearsI share your pain, it was about the same for me haha. Great to hear I'm helping other devs - thanks!
-
Robooto over 3 yearsThis was exactly my issue!
-
abovetempo over 3 yearsThank you for this response it fixed my problem too!
-
Gabe over 2 yearsOr in my case, pay attention and don't put app.UseAuthorization twice. lol Thanks for drawing my attention to my goof!