Search code examples
c#jwtwebapi

Why do I get unauthorized despite of valid Token?


The token is generated through this method:

private string GenerateJwtToken(User user)
{
    var tokenHandler = new JwtSecurityTokenHandler();

    var base64Key = _configuration["Jwt:Secret"];

    try
    {
        var key = Convert.FromBase64String(base64Key);

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Issuer = _configuration["Jwt:Issuer"],
            Audience = _configuration["Jwt:Audience"],
            Subject = new ClaimsIdentity(
                new Claim[]
                {
                    new Claim(ClaimTypes.Name, user.Email),
                    new Claim("UserId", user.ID.ToString())
                }
            ),
            Expires = DateTime.UtcNow.AddDays(14),
            SigningCredentials = new SigningCredentials(
                new SymmetricSecurityKey(key),
                SecurityAlgorithms.HmacSha256Signature
            )
        };

        var token = tokenHandler.CreateToken(tokenDescriptor);
        return tokenHandler.WriteToken(token);
    }
    catch (FormatException ex)
    {
        Console.WriteLine($"error converting Base64 string: {ex.Message}");
        return null;
    }
}

I checked on jwt.io and it says the token generated is valid.

When I use thunderclient to my /user endpoint to get the user by passing the JWT token in the header then I get Status: 401 Unauthorized.

Here is my post method:

[HttpPost]
[Authorize]
public async Task<ActionResult<UserDTO>> GetUserDTO()
{
    Console.WriteLine("I GET USER");
    try
    {
        var jwt = HttpContext.Request.Headers["Authorization"]
                .ToString()
                .Replace("Bearer ", string.Empty);

        Console.WriteLine(jwt);
        if (string.IsNullOrWhiteSpace(jwt))
        {
            return BadRequest("JWT token is missing.");
        }
        var loggedInUser = _userService.GetByJWT(jwt);

        if (loggedInUser == null)
        {
            return BadRequest("Failed to get user.");
        }

        return Ok(loggedInUser.FirstName);
    }
    catch (Exception e)
    {
        return StatusCode(StatusCodes.Status500InternalServerError, e.Message);
    }
}

When I remove [Authorize] then it works and it can resolve the id from the JWT bearer.

In program.cs I have my authorize schema and tried to change it and see if it's something wrong with my issuer or audience? I set my issuer value as my application name and the audience value as API since it's the API that will use it. Is that correct? Here is the schema:

builder.Services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"])
            ),
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"]
        };
    });

builder.Services.AddAuthorization();

Why do I get unauthorized?

EDITED Here is how I have tried setting the header / authorizarion

enter image description here

enter image description here


Solution

  • The problem is that you use two different keys, see the following lines:

    var key = Convert.FromBase64String(base64Key); //used to generate a jwt
    
    IssuerSigningKey = new SymmetricSecurityKey(
                    Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"])
                ) // used to check a jwt
    

    Basically Encoding.UTF8.GetBytes and Convert.FromBase64String(base64Key) don't return the same byte[] for the same string

    Change the line to the following in your .AddJwtBearer method call

    IssuerSigningKey = new SymmetricSecurityKey(
                    Convert.FromBase64String(builder.Configuration["Jwt:Secret"])
                ) // used to check a jwt