10

I am building an API with .NET 6 but I am stuck with this error. I get a token with login then I added that token to header but there is always getting 401: Unauthorized error.

NOTE: I am getting Bearer error="invalid_token" but there is no description.

This is my code;

Program.cs:

using Autofac; using Autofac.Extensions.DependencyInjection; using Business.DependencyResolvers.Autofac; using Core.Utilities.Security.Encryption; using Core.Utilities.Security.Jwt; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Autofac builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); builder.Host.ConfigureContainer<ContainerBuilder>(builder => builder.RegisterModule(new AutofacBusinessModule())); builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "OzkanOner", Version = "v1" }); c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme() { Name = "Authorization", Type = SecuritySchemeType.ApiKey, Scheme = "Bearer", BearerFormat = "JWT", In = ParameterLocation.Header, Description = "JWT Authorization header using the Bearer scheme." }); c.AddSecurityRequirement(new OpenApiSecurityRequirement{ { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] {} } }); }); //CORS builder.Services.AddCors(options => { options.AddPolicy("AllowOrigin", builder => builder.WithOrigins("http://localhost:4200")); }); var tokenOptions = builder.Configuration.GetSection("TokenOptions").Get<TokenOptions>(); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidIssuer = tokenOptions.Issuer, ValidAudience = tokenOptions.Audience, ValidateIssuerSigningKey = true, IssuerSigningKey = SecurityKeyHelper.CreateSecurityKey(tokenOptions.SecurityKey) }; }); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseCors(builder => builder.WithOrigins("http://localhost:4200").AllowAnyHeader()); app.UseHttpsRedirection(); // Authentication & Authorization app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run(); 

JwtHelper.cs:

using Core.Entities.Concrete; using Core.Extensions; using Core.Utilities.Security.Encryption; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; namespace Core.Utilities.Security.Jwt { public class JwtHelper : ITokenHelper { public IConfiguration Configuration { get; } private TokenOptions _tokenOptions; private DateTime _accessTokenExpiration; public JwtHelper(IConfiguration configuration) { Configuration = configuration; _tokenOptions = Configuration.GetSection("TokenOptions").Get<TokenOptions>(); } public AccessToken CreateToken(User user, List<OperationClaim> operationClaims) { _accessTokenExpiration = DateTime.Now.AddMinutes(_tokenOptions.AccessTokenExpiration); var securityKey = SecurityKeyHelper.CreateSecurityKey(_tokenOptions.SecurityKey); var signingCredentials = SigningCredentialsHelper.CreateSigningCredentials(securityKey); var jwt = CreateJwtSecurityToken(_tokenOptions, user, signingCredentials, operationClaims); var jwtSecurityTokenHandler = new JwtSecurityTokenHandler(); var token = jwtSecurityTokenHandler.WriteToken(jwt); return new AccessToken { Token = token, Expiration = _accessTokenExpiration }; } public JwtSecurityToken CreateJwtSecurityToken(TokenOptions tokenOptions, User user, SigningCredentials signingCredentials, List<OperationClaim> operationClaims) { var jwt = new JwtSecurityToken( issuer: tokenOptions.Issuer, audience: tokenOptions.Audience, expires: _accessTokenExpiration, notBefore: DateTime.Now, claims: SetClaims(user, operationClaims), signingCredentials: signingCredentials ); return jwt; } private IEnumerable<Claim> SetClaims(User user, List<OperationClaim> operationClaims) { var claims = new List<Claim>(); claims.AddNameIdentifier(user.Id.ToString()); claims.AddEmail(user.Email); claims.AddName($"{user.FirstName} {user.LastName}"); claims.AddRoles(operationClaims.Select(c => c.Name).ToArray()); return claims; } } } 

appsettings.json:

{ "TokenOptions": { "Audience": "https://localhost:7047", "Issuer": "https://localhost:7047", "AccessTokenExpiration": 60, "SecurityKey": "ASD89ays7fhHASDF09ua0sdu6d32ghbjlka" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" } 
1
  • I am on the same boat. First time trying dotnet 6, and after 7 different tutorials, the token validation still doesn't work. Commented Mar 16, 2022 at 2:41

4 Answers 4

32

In my case, I was missing System.IdentityModel.Tokens.Jwt nuget package.

Everything compiles and run Ok but throws invalid token while testing the api. Once installed System.IdentityModel.Tokens.Jwt it started working without changing a line of code.

Sign up to request clarification or add additional context in comments.

11 Comments

I was so frustrated with the issue... but I applied your advice and wow it worked. Thank you very much.
Happy to help someone! Thats why I added this answer to an old thread.
Thank you for your help! I wasted many hours trying to solve this problem!
No way! This was actually it! Thank you. Very difficult to figure out this one.
I skimmed this article 3 times before finally trying this and it worked.
|
2

On Program.cs Change:

c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme() { Name = "Authorization", Type = SecuritySchemeType.Http, Scheme = "Bearer", BearerFormat = "JWT", In = ParameterLocation.Header, Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter your token in the text input below.\r\n\r\nExample: \"12345abcdef\"" }); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = tokenOptions.Issuer, ValidAudience = tokenOptions.Audience, IssuerSigningKey = SecurityKeyHelper.CreateSecurityKey(tokenOptions.SecurityKey), ClockSkew = TimeSpan.Zero }; }); 

It works fine using net 6. Download an example at https://drive.google.com/file/d/16VYtX5klxJF1jlnM4nFbz1rFPyCz-TZL/view?usp=sharing , use url: /swagger

Comments

1

In case of .Net 6, getting right version of nuget package is important. Try with these version of packages:

<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" /> <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.15.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.15.0" /> 

1 Comment

Anything after the Microsoft.IdentityModel.Tokens 6.15.0 doesn't work for me either. Don't understand why. For now i'm keeping this version
-3

I was having the same issue in loosely following this tutorial (though I had upgraded to .NET 6). I found two solutions:

  1. Downgrade from .NET 6 to .NET 5
  2. Keep .NET 6 and when creating the JWT Token to return to the user, sign it using the HmacSha256Signature algorithm, rather than the HmacSha256 algorithm

In looking over this tutorial that targets .NET 6, I noticed the different signing algorithm. I suspect v6.0 has incompatibilities.

You have a couple helper classes that you haven't included in your question, nor am I an auth wizard yet, so I can't say 100% what the issue is for you. Given my experience, I suspect the issue would be in your SigningCredentialsHelper.CreateSigningCredentials method.

This would have been a comment on your post rather than a non-authoritative answer, but I don't have the rep to do that.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.