24

Can someone please help me to solve this problem? I'm testing the API using Postman

I'm following a tutorial about asp.net core.

And I'm on its Authentication part now.

I don't really understand whats the reason for the error.

In the tutorial, it has a login and it returns token.

This is the code for login. Which is working. I know this is working because it returns a token. I also tried using an invalid login. and it returns 401 Unauthorized But when I use the correct login credentials which are found in the database. It returns token

[HttpPost("login")]
public async Task<IActionResult> Login(UserForLoginDto userForLoginDto)
    {
        var userFromRepo = await _repo.Login(userForLoginDto.Username.ToLower(), userForLoginDto.Password);

        if (userFromRepo == null)
            return Unauthorized();

        var claims = new[]
        {
            new Claim(ClaimTypes.NameIdentifier, userFromRepo.Id.ToString()),
            new Claim(ClaimTypes.Name, userFromRepo.Username)
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config.GetSection("AppSettings:Token").Value));

        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims),
            Expires = DateTime.Now.AddDays(1),
            SigningCredentials = creds
        };

        var tokenHandler = new JwtSecurityTokenHandler();

        var token = tokenHandler.CreateToken(tokenDescriptor);

        return Ok(new {
            token = tokenHandler.WriteToken(token)
        });
}

Then the next part of the tutorial is to limit the access. The user should be logged in first in order to view the content.

Below is the code

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>{
                options.TokenValidationParameters = new TokenValidationParameters{
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value)),
                    ValidateIssuer = false
                };
            });

Then enabled

app.UseAuthentication();

I also enabled the [Authorize] in the Values Controller

[Authorize]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase

This is the screenshot of postman

enter image description here

I followed the tutorial. I paste the token I received from login. But it gives me the error

WWW-Authenticate →Bearer error="invalid_token", error_description="The audience is invalid"

Why does the error give me invalid token if the token is from the login? How do I fix this? I've been searching for a while but I can't solve this my self. Thank you.

Austin T French
  • 5,022
  • 1
  • 22
  • 40
Ramon bihon
  • 385
  • 1
  • 5
  • 18

9 Answers9

30

I had this issue in dotnet 6 after update to Microsoft.AspNetCore.Authentication.JwtBearer v6.0.0+

As fix: Install nuget package System.IdentityModel.Tokens.Jwt Version="6.16.0"

Tyler2P
  • 2,324
  • 26
  • 22
  • 31
Павел К.
  • 441
  • 3
  • 5
11

I had a similar problem where a .net Core 3 API would not authenticate its own tokens.

The solution for me was in Startup/Configure(), to put app.UseAuthentication() before app.UseAuthorization().

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
 {
    app.UseAuthentication();

    app.UseAuthorization();
 }
7

I recently did similar thing using JWT token which is working fine with Postman. My approach for creating the JWT token is little different, In your case the problem can be due to not specifying the issuer and audience.

Can you try like following.

   var claims = new List<Claim>
    {
        new Claim(ClaimTypes.WindowsAccountName, this.User.Identity.Name)
    };
    Claim userIdClaim = new Claim("UserId", "12345");
    claims.Add(userIdClaim);
    //Avoid Replay attack
    claims.Add(new Claim(ClaimTypes.GivenName, "User GivenName"));
    claims.Add(new Claim(ClaimTypes.Surname, "UserSurname"));
    claims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));

    string[] roles = "Role1,Role2,Role23".Split(",");

    foreach (string role in roles)
    {
        claims.Add(new Claim(role, ""));
    }

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("veryVerySecretKey"));
    var key1 = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("ASEFRFDDWSDRGYHF")); 
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

    var encryptingCreds = new EncryptingCredentials(key1, SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
    var handler = new JwtSecurityTokenHandler();
    var t = handler.CreateJwtSecurityToken();
    var token = handler.CreateJwtSecurityToken("http://localhost:61768/", "http://localhost:61768/"
        , new ClaimsIdentity(claims)
        , expires: DateTime.Now.AddMinutes(1)
        , signingCredentials: creds
        , encryptingCredentials :encryptingCreds
        , notBefore:DateTime.Now
        ,  issuedAt:DateTime.Now);
    return new JwtSecurityTokenHandler().WriteToken(token);

And my ConfigureServices looks like

services.AddAuthentication()
            .AddJwtBearer(options =>
             {
                 options.RequireHttpsMetadata = false;
                 options.SaveToken = true;
                 options.TokenValidationParameters = new TokenValidationParameters
                 {
                     ValidateIssuer = true,
                     ValidateAudience = true,
                     ValidateLifetime = true,
                     ValidateIssuerSigningKey = true,
                     ValidIssuer = "http://localhost:61768/",
                     ValidAudience = "http://localhost:61768/",
                     TokenDecryptionKey= new SymmetricSecurityKey(Encoding.UTF8.GetBytes("ASEFRFDDWSDRGYHF")),
                     IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("veryVerySecretKey")),
                     ClockSkew = TimeSpan.Zero
                 };
             });

Note: Change the issuer and the key appropriately.

PSK
  • 17,547
  • 5
  • 32
  • 43
  • i haven't tried your solution but thank you for your time. The solution is ValidateAudience = false in my case. – Ramon bihon Jan 28 '19 at 06:04
2

The error that you received is related to audience, you should either include a ValidAudience or set ValidateAudience to false in your options.

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options => {
            options.TokenValidationParameters = new TokenValidationParameters{
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value)),
            ValidateIssuer = false,
            ValidateAudience = false
          };
        });
Ram Kumaran
  • 621
  • 6
  • 12
0

I had the same problem. Please note that the order in the Configure function.
app.usemvc (); should be at the bottom. Like this:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseAuthentication();
    app.UseMvc();
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
0

Ram Kumaran (https://stackoverflow.com/a/54396550/8210755) answer works for me, It could have happend after update to net core 3.1 or after update IdentityServer to 4.3.1

I've remplaced commented code with AddJwtBearer

 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            //.AddIdentityServerAuthentication(options =>
            //{
            //    options.Authority = Configuration.GetSection("IdentityServerUrl").Value;
            //    options.RequireHttpsMetadata = false;
            //    options.ApiName = "api1";
            //});
            .AddJwtBearer(o =>
             {
                 o.Authority = Configuration.GetSection("IdentityServerUrl").Value;
                 o.RequireHttpsMetadata = false;
                 o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                 {
                     ValidateAudience = false
                 };
             });

Usefull doc ref: https://docs.identityserver.io/_/downloads/en/latest/pdf/ Using ValidateAudience in false like proof of concept

Zaha
  • 846
  • 10
  • 21
0

In my case (with Keycloak) I added a new realm and forgot to change the urls for Authorization and Jwt bearer metadata. They were still pointing to the old realm. I changed the realm name from portal to test-portal and the token supplied was not correct because it was still coming from the portal realm.

Taken from my appsettings.json:

"AuthorizationUrl": "https://my.keycloak.id.provider/realms/test-portal/protocol/openid-connect/auth",
"JwtBearerMetadataAddress": "https://my.keycloak.id.provider/realms/test-portal/.well-known/openid-configuration"
Patrick Koorevaar
  • 1,219
  • 15
  • 24
0

I'm Looking for a similar issue but not this one,

in your appsettings.json you need to add a JWT Section and use them to validate the issuer and audience of the token to more safe tokens

if you look at the description of error : The audience is invalid

"BearerToken": {
"Issuer": "the api host",
"Audience": "who use the token",
"Key": "a key to validate your token",
"AccessTokenExpirationMinutes": 25,
"RefreshTokenExpirationMinutes": 50

},

and when you adding the token Descriptor you have to add the issuer and the audience in it

var descriptor = new SecurityTokenDescriptor
            {
                Issuer = _configuration.Value.Issuer,
                Audience = _configuration.Value.Audience,
                Expires = Clock.UtcNow.ExpiresInMinutes(_configuration.Value.AccessTokenExpirationMinutes),
                Subject = new ClaimsIdentity(userClaims),
                SigningCredentials = new SigningCredentials(authSignInKey, SecurityAlgorithms.HmacSha256),
            };

and in your startup class (.NET 5 or before) or Program (.NET 6)

you need to add authentication service like this to verify token

builder.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 TokenValidationParameters
      {
          ValidateIssuer = true,
          ValidateAudience = true,
          ValidateLifetime = true,
          ValidateIssuerSigningKey = true,
          ValidIssuer = builder.Configuration["BearerToken:Issuer"],
          ValidAudience = builder.Configuration["BearerToken:Audience"],
          IssuerSigningKey = new SymmetricSecurityKey(Convert.FromBase64String(builder.Configuration["BearerToken:Key"])),
      };
});
Javid_Leo
  • 1
  • 1
0

For me my parameters to the JwtSecurityToken constructor were not correct. There are some nullable parameters on the constructor so I needed to define which was which. For example I had...

new JwtSecurityToken(issuer, audience, claims, expires, signingCredentials: credentials);

This fixed it.

new JwtSecurityToken(issuer, audience, claims, expires: expires, signingCredentials: credentials);

Probably a long shot for others to run into this, but hey, it happened to me.

hanesjw
  • 2,356
  • 4
  • 26
  • 34