Search code examples
asp.net-coreauthorizationasp.net-core-webapi

ASP.NET Core Web API returns ERROR 401 UnAuthorized in postman if I use Authorize attribute. Why?


This is authentication scheme:

services.AddAuthentication(option =>
        {
            option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            option.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(options =>
        {
            options.SaveToken = true;
            options.RequireHttpsMetadata = false;
            options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                  {
                      ValidateIssuer = true,
                      ValidateAudience = true,
                      ValidIssuer = Configuration["JWT:ValidAudience"],
                      ValidAudience = Configuration["JWT:ValidIssuer"],
                      IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Secret"]))
                  };
        });

This is my pipeline:

 app.UseHttpsRedirection();

 app.UseRouting();
 app.UseCors(); //custom

 app.UseAuthentication(); //CUSTOM
 app.UseAuthorization();

 app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });

If I remove Authorize attribute on top of controller, then request is sent successfully but it doesn't work if I add it.

[Route("api/[controller]")]
[ApiController]
[Authorize]//(Roles = UserRoles.Admin)]
public class ValuesController : ControllerBase
{
    // GET: api/<ValuesController>
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

This code works if I remove the Authorize attribute. Even without any roles it doesn't work.

This is my appSettings.json:

"JWT": {
    "ValidAudience": "User",
    "ValidIssuer": "https://localhost:44336",
    "Secret": "ThisIsMySecretKEYadadasdasd939239" // MUST BE 16 CHARACTERS
}

This is the sign in code:

public async Task<TokenModel> SignIn(SignInModel model)
{
        var user = await _context.FindByNameAsync(model.UserName);

        if (user != null && await _context.CheckPasswordAsync(user, model.Password)) //Rocko@135
        {
            var authClaims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, user.UserName),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            };

            var userRoles = await _context.GetRolesAsync(user);

            foreach (var userRole in userRoles)
            {
                authClaims.Add(new Claim(ClaimTypes.Role, userRole));
            }

            var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
               issuer: _configuration["JWT:ValidIssuer"],
              audience: _configuration["JWT:ValidAudience"],
              expires: DateTime.Now.AddDays(2),
              claims: authClaims,
              signingCredentials: credentials);

            return new TokenModel
            {
                Token = new JwtSecurityTokenHandler().WriteToken(token),
                Expiration = token.ValidTo,
                Username = user.UserName
            };
        }
        else
        {
            return new TokenModel
            {
                Token = null,
                Expiration = null,
                Username = string.Empty
            };
        }
}

Here is my JWT

https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiUm9ja28iLCJqdGkiOiI0NTQ2MjhkMy1jMGM1LTQ1MTUtODQ0My1kMDQ4Y2ZhNzM1NWYiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbiIsImV4cCI6MTcxMTA1NDkzNSwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMzYiLCJhdWQiOiJVc2VyIn0.FQBlJENwQWzuvQ1NwzRBaTuk7I3h2YBPxhWo1vT7PUw

enter image description here


Solution

  • In the end, it was a simple silly typo. Check your .AddJwtBearer setup call:

        .AddJwtBearer(options =>
        {
            options.SaveToken = true;
            options.RequireHttpsMetadata = false;
            options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                  {
                      ValidateIssuer = true,
                      ValidateAudience = true,
                      ValidIssuer = Configuration["JWT:ValidAudience"],   // **HERE**
                      ValidAudience = Configuration["JWT:ValidIssuer"],   // **AND HERE**
                      IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Secret"]))
                  };
        });
    

    See the ValidIssuer and ValidAudience values in the TokenValidationParameters?

    They are being filled with the wrong values:

    ValidIssuer = Configuration["JWT:ValidAudience"],
    ValidAudience = Configuration["JWT:ValidIssuer"],
    

    This should really be:

    ValidAudience = Configuration["JWT:ValidAudience"],
    ValidIssuer = Configuration["JWT:ValidIssuer"],