Home > Software design >  .NET 6.0 API jwt-auth | API returns "invalid_token" error
.NET 6.0 API jwt-auth | API returns "invalid_token" error

Time:09-06

I'm trying to implement jwt to my project. And it was working before i changed the code. This changes doesn't affect the authorization but somehow tokens doesn't work anymore.

When I check the token from jwt.io, it says "invalid signature". And when I try to execute a request using Swagger, responds says

content-length: 0
date: Fri,02 Sep 2022 10:09:12 GMT
server: Kestrel
www-authenticate: Bearer error="invalid_token"
x-firefox-spdy: h2 

Secret : tx9mcA4gnbigtP7ZRECnfhk9tWsHM9ZtXqbuFYWM23D3PMdRKwh74e24swqvrTh5

API/Programs.cs

using System.Text;
using BlogProject.Business.MapperProfile;
using BlogProject.Business.Services.AuthenticationService;
using BlogProject.Business.Services.CommentService;
using BlogProject.Business.Services.PostService;
using BlogProject.Business.Services.TagService;
using BlogProject.Business.Services.UserService;
using BlogProject.DataAccess.Data;
using BlogProject.DataAccess.Repositories.Base;
using BlogProject.DataAccess.Repositories.Base.Interfaces;
using BlogProject.DataAccess.Repositories.Relations;
using BlogProject.DataAccess.Repositories.Relations.Interfaces;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters;

var builder = WebApplication.CreateBuilder(args);

var isDevelopment = builder.Environment.IsDevelopment();

builder.Services.AddControllers();
builder.Services.AddCors();

builder.Services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        var key = builder.Configuration["JsonWebTokenKeys:IssuerSigningKey"];
        var encodedKey = Encoding.UTF8.GetBytes(key);
        var signingKey = new SymmetricSecurityKey(encodedKey);

        options.RequireHttpsMetadata = false;
        options.SaveToken = true;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = signingKey,
            ValidateIssuer = false,
            ValidateAudience = false,
        };
    });
builder.Services.AddScoped<IJwtAuthenticationManager, JwtAuthenticationManager>();


builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    var openApiSecurityScheme = new OpenApiSecurityScheme
    {
        Description = "Standard Authorization header using the Bearer scheme (\"bearer {token}\")",
        In = ParameterLocation.Header,
        Name = "Authorization",
        Type = SecuritySchemeType.ApiKey
    };

    options.AddSecurityDefinition("oauth2", openApiSecurityScheme);
    options.OperationFilter<SecurityRequirementsOperationFilter>();
});

builder.Services.AddAutoMapper(typeof(MapProfile));

builder.Services.AddDbContext<BlogProjectDbContext>(
    optionsBuilder =>
    {
        var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
        optionsBuilder.UseNpgsql(connectionString);

        optionsBuilder.EnableDetailedErrors(isDevelopment);
        optionsBuilder.EnableSensitiveDataLogging(isDevelopment);
    });


builder.Services.AddScoped<IPostRepository, EFPostRepository>();
builder.Services.AddScoped<ICommentRepository, EFCommentRepository>();
builder.Services.AddScoped<IUserRepository, EFUserRepository>();
builder.Services.AddScoped<ITagRepository, EFTagRepository>();
builder.Services.AddScoped<ICategoryRepository, EFCategoryRepository>();

builder.Services.AddScoped<IPostsEditorsRepository, EFPostsEditorsRepository>();
builder.Services.AddScoped<IPostsTagsRepository, EFPostsTagsRepository>();
builder.Services.AddScoped<IUsersCommentReactionsRepository, EFUsersCommentReactionsRepository>();
builder.Services.AddScoped<IUsersPostReactionsRepository, EFUsersPostReactionsRepository>();


builder.Services.AddScoped<IPostService, PostService>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<ICommentService, CommentService>();
builder.Services.AddScoped<ITagService, TagService>();
builder.Services.AddScoped<ITagService, TagService>();

var app = builder.Build();

if (isDevelopment)
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseCors(policyBuilder => policyBuilder.AllowAnyHeader().AllowAnyMethod().WithOrigins("http://localhost:3000"));

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

Business/Services/AuthenticationService/JwtAuthenticationManager.cs

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using BlogProject.DataAccess.Repositories.Base.Interfaces;
using BlogProject.Entities.Base;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames;

namespace BlogProject.Business.Services.AuthenticationService;

public class JwtAuthenticationManager : IJwtAuthenticationManager
{
    private readonly IUserRepository _userRepository;
    private readonly string _jwtTokenSecret;
    private readonly string _jwtTokenSubject;

    public JwtAuthenticationManager(
        IConfiguration configuration,
        IUserRepository userRepository)
    {
        _jwtTokenSecret = configuration["JsonWebTokenKeys:IssuerSigningKey"];
        _jwtTokenSubject = configuration["JsonWebTokenKeys:Subject"];
        _userRepository = userRepository;
    }

    public async Task<string?> GetJwtTokenAsync(string username)
    {
        var user = await _userRepository.ValidateUserAsync(username);
        if(user == null) return null;

        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtTokenSecret));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
        var expireTime = DateTime.UtcNow.AddDays(1);

        var token = new JwtSecurityToken(
            claims : GetClaim(user),
            expires : new DateTimeOffset(expireTime).DateTime,
            signingCredentials : credentials);

        var tokenHandler = new JwtSecurityTokenHandler();

        var jwtString = tokenHandler.WriteToken(token);

        return jwtString;
    }

    private IEnumerable<Claim> GetClaim(User user)
    {
        return new Claim[] 
        {
            new Claim(JwtRegisteredClaimNames.Sub, _jwtTokenSubject),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
            new Claim("Id", user.Id.ToString()),
            new Claim("UserName", user.Username),
            new Claim("Email", user.Email),
            new Claim("Role", user.Role)
        };
    }
}

API/Controller/UserController.cs : Login

[HttpGet("Login")]
public async Task<IActionResult> Login(string username, string password)
{
    var response = await _userService.ValidateUserAsync(username, password);

    if (response == null) return NotFound();

    var tokenResponse = await _jwtAuthenticationManager.GetJwtTokenAsync(response.UserName);

    if (tokenResponse == null) throw new Exception("Token is null");

    response.Token = tokenResponse;

     Ok(response);
}

CodePudding user response:

This is very embarrasing. Recently I deleted some of the packages from API. Looks like deleting "System.IdentityModel.Tokens.Jwt" somehow breaks the code. But there is something I don't understand. It still says "invalid signature" at jwt.io but the token works.

And Yes I pasted the secret to the field.

  • Related