Asked by:
ASP.Net Core 3.0 : How to validate JWT Bearer Tokens

Question
-
User-247644627 posted
Hello,
I am working for a non-profit video-learning platform and try to use JWT bearer tokens.
I am using a ASP.Net Core 3.0 API with EntityFramework Core as UserStorage.
Here is the code I already have:
Startup.cs:
public void ConfigureServices(IServiceCollection services) { using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.SpaServices.AngularCli; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; . . . //Add Identity Provider with EntityFramework services.AddIdentity<User, IdentityRole>() .AddEntityFrameworkStores<ApplicationDBContext>() .AddDefaultTokenProviders(); //Initialize EntityFramework services.AddDbContext<ApplicationDBContext>(options => options.UseSqlite(Configuration.GetConnectionString("localDB"))); //Initialize JWT Authentication services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(jwtBearerOptions => { jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters() { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = "http://localhost:44352", ValidAudience = "http://localhost:44352", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration.GetSection("Secrets")["jwt"])) }; } ); services.AddMvc(options => options.EnableEndpointRouting = false) .AddNewtonsoftJson(); // In production, the Angular files will be served from this directory services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/dist"; }); } . . . app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseSpaStaticFiles(); //Enable Authentication app.UseAuthentication(); app.UseAuthorization(); . . . app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action=Index}/{id?}"); }); . . .
This is my code issuing a JWT token:
public async Task<IActionResult> Login() { using (var reader = new StreamReader(Request.Body)) { var body = await reader.ReadToEndAsync(); var cred = JsonConvert.DeserializeObject<Credentials>(body); var result = (await userService.LoginUser(cred.userName, cred.password)); if (result == 200) { var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration.GetSection("Secrets")["jwt"])); var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256Signature); var roles = await userService.GetRoleFromUsername(cred.userName); var rolesString = JsonConvert.SerializeObject(roles); var tokeOptions = new JwtSecurityToken( issuer: "http://localhost:44352", audience: "http://localhost:44352", claims: new List<Claim>(new List<Claim> { new Claim("userName",cred.userName), new Claim("roles", rolesString) }), expires: DateTime.Now.AddHours(1), signingCredentials: signinCredentials );
This is my API call:
[Route("api/videos/add")] [Authorize(Roles = "Admin")] [HttpPost] public async Task<IActionResult> AddVideo() { using (var reader = new StreamReader(Request.Body)) { var body = await reader.ReadToEndAsync(); var video = JsonConvert.DeserializeObject<Video>(body); await videoService.AddVideo(video); return Ok(); } }
My NuGet Packages are:
- Microsoft.EntityFrameworkCore {3.0.0-preview5.19227.1}
- Microsoft.EntityFrameworkCore.Sqlite {3.0.0-preview5.19227.1}
- Microsoft.AspNetCore.Authentication.JwtBearer {3.0.0-preview4-19216-03}
- Microsoft.EntityFrameworkCore.Sqlite.Core {3.0.0-preview5.19227.1}
- Microsoft.NETCore.Platforms {3.0.0-preview4.19212.13}
- Microsoft.AspNetCore.Mvc.NewtonsoftJson {3.0.0-preview5-19227-01}
- Microsoft.AspNetCore.SpaServices.Extensions {3.0.0-preview5-19227-01}
- Microsoft.AspNetCore.Identity.EntityFrameworkCore {3.0.0-preview5-19227-01}
- runtime.win-x64.Microsoft.NETCore.DotNetAppHost {3.0.0-preview4-27615-11}
The problem I have, is that if I call that API part, I get the error:
Information: Bearer was not authenticated. Failure message: No SecurityTokenValidator available for token:
Any help would be much appreciated, since I can't find the error
Friday, June 7, 2019 1:38 PM
All replies
-
User-2054057000 posted
Apply the breakpoint on your code line then move forward by pressing F10 key.
Once you reach the line which gives the error, then update your question by providing the line which is giving the error and also the error text.
This way we can assist you in a good way.
Thank You..
Saturday, June 8, 2019 9:31 AM -
User-247644627 posted
Thank you for your answer.
Unfortunatelly, I cannot use breakpoints, since I get the error inside of ASP.Net code, not code written by me.
The only info I can give you is:
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Information: Route matched with {action = "AddVideo", controller = "Video", page = ""}. Executing action L_Tube.Controllers.VideoController.AddVideo (L-Tube)
The error happens when I call VideoController.AddVideo() and it only happens, if I check for Authorization
Saturday, June 8, 2019 4:13 PM -
User-1764593085 posted
Hi AtzederSerbe,
Do you get a 403 error? Try to use `ClaimTypes.Role` instead of `roles` if you want to add a Role as claim.
var tokeOptions = new JwtSecurityToken( issuer: "http://localhost:44352", audience: "http://localhost:44352", claims: new List<Claim>(new List<Claim> { new Claim("userName",cred.userName),
new Claim(ClaimTypes.Role, "Admin") }), expires: DateTime.Now.AddHours(1), signingCredentials: signinCredentials );Api:
[Route("api/videos/add")] [Authorize(Roles = "Admin")] [HttpPost] public async Task<IActionResult> AddVideo()
Refer to;
Best Regards,
Xing
Monday, June 10, 2019 5:46 AM -
User-247644627 posted
Dear Xing Zou,
thank you for your answer. Unfortunatelly, that did not do it. I got still the same error.
Failed to load resource: the server responded with a status of 401 () [https://localhost:44352/api/videos/add]
Best regards
Monday, June 10, 2019 8:45 PM -
User-1764593085 posted
Hi Atzederserbe,
How do you call the [Authorize] api after you receiving the token? Do you send the token in request header?if you use postman, you could choose Authorization->Type(Bearer Token)-> Input your token to test the api.
Besides, do you have the same problem in asp.net core 2.2?
Best Regards,
Xing
Tuesday, June 11, 2019 1:21 AM -
User-247644627 posted
Hello,
I am using an Angular7 SPA with a HTTP Interceptor. If the authService gets a HTTP 200 back with the bearer token, it saves it in the LocalStorage, as well as the decrypted userName. If there are those entries in the LocalStorage, the HTTP Interceptor adds an authorization header to every HTTP request.
Following is the request I am sending via Angular
Using Postman, I think I found the error. It's the quotation marks at the bearer token. If I use Postman, I get a 403 Error, but I do not get the original error in the API.
So, the 401 error got a 403. But I guess this is because of an old bearer token.
I will reply again after some testing.
Best Regards
Tuesday, June 11, 2019 1:43 PM -
User-247644627 posted
After some testing, it is not an old bearer token which causes this problem.
Erasing the quotation marks worked, so that the token gets successfully validated, but there is still a 403 error.
This is what the API puts out
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 POST https://localhost:44352/api/videos/add text/plain 133
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Information: Successfully validated the token.
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Information: Route matched with {action = "AddVideo", controller = "Video", page = ""}. Executing controller action with signature System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Mvc.IActionResult] AddVideo() on controller L_Tube.Controllers.VideoController (L-Tube).
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: Information: Authorization failed.
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker: Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ForbidResult: Information: Executing ForbidResult with authentication schemes ().
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Information: AuthenticationScheme: Bearer was forbidden.Tuesday, June 11, 2019 1:59 PM -
User-247644627 posted
I found the issue concerning error 403. It seems that you are only allowed to send ONE role in the bearer token for ASP.Net to validate it. The return type of userManager.GetRolesAsync suggests that a user can have multiple roles which can be included in a JWT bearer token.
That means my issue is fixed.
I want to thank Xing Zou for his/her answers. I would not have gotten it without you!
Tuesday, June 11, 2019 9:13 PM -
User475983607 posted
I found the issue concerning error 403. It seems that you are only allowed to send ONE role in the bearer token for ASP.Net to validate it.I'm pretty sure all you need to do is add multiple Role types.
new Claim(ClaimTypes.Role, rolesString1), new Claim(ClaimTypes.Role, rolesString2), new Claim(ClaimTypes.Role, rolesString3)
Tuesday, June 11, 2019 9:20 PM -
User-247644627 posted
Thank you for your answer mgebhard, I will implement this if needed
Wednesday, June 12, 2019 9:07 PM