ASP.NET Core 3.0 Identity Server 4 (4.0.0) SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'empty'
Asked Answered
J

3

13

I keep getting the following error between postman and IdentityServer 4

Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'empty'. Did not match: validationParameters.ValidAudience: 'MyNumberV2Api' or validationParameters.ValidAudiences: 'null'.
   at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateAudience(IEnumerable`1 audiences, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Information: BearerIdentityServerAuthenticationJwt was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'empty'. Did not match: validationParameters.ValidAudience: 'MyNumberV2Api' or validationParameters.ValidAudiences: 'null'.
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler: Information: Bearer was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'empty'. Did not match: validationParameters.ValidAudience: 'MyNumberV2Api' or validationParameters.ValidAudiences: 'null'.
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: Information: Authorization failed.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Information: AuthenticationScheme: BearerIdentityServerAuthenticationJwt was challenged.
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler: Information: AuthenticationScheme: Bearer was challenged.
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 1140.9671ms 401 
The program '[12792] iisexpress.exe: Program Trace' has exited with code 0 (0x0).
The program '[12792] iisexpress.exe' has exited with code -1 (0xffffffff)..
  1. In my Identity Server Startup.cs
 public void ConfigureServices(IServiceCollection services)
        {
            services.AddIdentityServer(
                options => { 
                    options.Events.RaiseErrorEvents = true;
                    options.Events.RaiseInformationEvents = true;
                    options.Events.RaiseFailureEvents = true;
                    options.Events.RaiseSuccessEvents = true;
                    options.IssuerUri = "http://localhost:5000";
                }
            )
                .AddDeveloperSigningCredential()
                .AddInMemoryApiResources(Config.GetAllApiResources())
                .AddInMemoryClients(Config.GetClients())
                //.AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddInMemoryApiScopes(Config.GetApiScopes());
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseIdentityServer();
            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/", async context =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });
            });
        }
  1. And here's the core for my API's startup.cs
 public void ConfigureServices(IServiceCollection services)
        {

            services.AddAuthentication("Bearer")
                .AddIdentityServerAuthentication(options =>
                {
                    options.Authority = "http://localhost:5000";
                    options.RequireHttpsMetadata = false;
                    options.ApiName = "MyNumberV2Api";
                });
            #region AddAuthentication
           
            services.AddDbContext<MyNumberV2.Data.MyNumberV2Context>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            
            IdentityModelEventSource.ShowPII = true;
           
            services.AddScoped<IAdminUserRepository, AdminUserRepository>();

            services.AddCors(options =>
            {
                options.AddPolicy("Open", builder => builder.AllowAnyOrigin().AllowAnyHeader());
            });
            services.AddMvcCore();
            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseAuthentication();

            app.UseCors("Open");

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
  1. Here is my public repo for the full solution code: https://github.com/zachion/blazor-auth

  2. Here is how I generate an auth token in postman: enter image description here

  3. And here is the body section of my request to get the token where i add the Grant type and Scope. enter image description here

  4. I get the token from the response and add it to the follow up requests for trying to get to the actual api's controllers.

  5. Here is how I add the auth token in postman. Issuing the token works fine enter image description here

  6. Here's the complete post man collection I use:

{
    "info": {
        "_postman_id": "089a85df-ae4b-41c3-8d1e-9d2e4ff8f7c8",
        "name": "MYNumberV2.Api Copy",
        "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
    },
    "item": [
        {
            "name": "Generate Tokent API One",
            "request": {
                "auth": {
                    "type": "basic",
                    "basic": [
                        {
                            "key": "password",
                            "value": "secret",
                            "type": "string"
                        },
                        {
                            "key": "username",
                            "value": "client",
                            "type": "string"
                        }
                    ]
                },
                "method": "POST",
                "header": [],
                "body": {
                    "mode": "urlencoded",
                    "urlencoded": [
                        {
                            "key": "grant_type",
                            "value": "client_credentials",
                            "type": "text"
                        },
                        {
                            "key": "scope",
                            "value": "MyNumberV2Api",
                            "type": "text"
                        }
                    ],
                    "options": {
                        "urlencoded": {}
                    }
                },
                "url": {
                    "raw": "http://localhost:5000/connect/token",
                    "protocol": "http",
                    "host": [
                        "localhost"
                    ],
                    "port": "5000",
                    "path": [
                        "connect",
                        "token"
                    ]
                }
            },
            "response": []
        },
        {
            "name": "api/adminuser",
            "protocolProfileBehavior": {
                "disableBodyPruning": true
            },
            "request": {
                "auth": {
                    "type": "noauth"
                },
                "method": "GET",
                "header": [
                    {
                        "key": "Authorization",
                        "type": "text",
                        "value": "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjNENDZERDNFQ0NGNTNCNkMyNEZEMjlFOUEzQzE2RjVDIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE1OTM0NTQyNTQsImV4cCI6MTU5MzQ1Nzg1NCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiY2xpZW50X2lkIjoiY2xpZW50IiwianRpIjoiQ0QxQzY5QzVGRkI0MTA0RDU5MTUwNERFQkI0MkI3NjgiLCJpYXQiOjE1OTM0NTQyNTQsInNjb3BlIjpbIk15TnVtYmVyVjJBcGkiXX0.xvAs-IYh_sh8RmpNOcy4Rl78Jv2L2-vPE7FYeEVqxES2HBoTEEgPT7uV5MiZrVeK1OaLOrkERzZ4druHrBtKgaeJ-BoC_IUt5Lp_otnJVbmCgGtrPXk8RMKcZguvxQsJdD5rqHLNZaN07kMNQEmmAprSAPpixtErzMK5DEmaAee2PNi430AyiZnObYbUBm_07Un5_6cjpOSFltjzsABBOzsbWfXIbXwvynCUVEiN5_mHhhjgocPcvlzrHdDtUi_PbdBk_hhtouTlveIaCTyNGdhfR4JCTJjO069hVVCXHScrekjNPeRSC4eOFEesmdG-4IbPKWBLsKldc1SrC1DE-w"
                    }
                ],
                "body": {
                    "mode": "urlencoded",
                    "urlencoded": [
                        {
                            "key": "grant_type",
                            "value": "client_credentials",
                            "type": "text",
                            "disabled": true
                        },
                        {
                            "key": "scope",
                            "value": "MyNumberV2Api",
                            "type": "text",
                            "disabled": true
                        }
                    ],
                    "options": {
                        "urlencoded": {}
                    }
                },
                "url": {
                    "raw": "http://localhost:44340/api/adminuser",
                    "protocol": "http",
                    "host": [
                        "localhost"
                    ],
                    "port": "44340",
                    "path": [
                        "api",
                        "adminuser"
                    ]
                },
                "description": "https://localhost:44340/api/adminuser"
            },
            "response": []
        },
        {
            "name": "api/adminuserdetail/1",
            "request": {
                "method": "GET",
                "header": [],
                "url": {
                    "raw": "http://localhost:44340/api/adminuserdetail/1",
                    "protocol": "http",
                    "host": [
                        "localhost"
                    ],
                    "port": "44340",
                    "path": [
                        "api",
                        "adminuserdetail",
                        "1"
                    ]
                },
                "description": "https://localhost:44340/api/adminuser"
            },
            "response": []
        }
    ],
    "protocolProfileBehavior": {}
}
Jocular answered 29/6, 2020 at 19:43 Comment(4)
How/where do you get this token from? I couldnt find it in your repoLecky
I get the token from the identity server project running as console using also postman and is at: github.com/zachion/blazor-auth/tree/master/…Jocular
how exactly you get that? feel free to post me snapshots - Reason I'm asking is that error you get is for using identity token instead of access token, I want to verify if this is the case for u as well or notLecky
Hello Nahidf I have updated the original post with all requested snapshots.Jocular
L
35

There is 2 issues in your code, I start from easy one to fix:

  1. On your API's startup class, move app.UseAuthentication(); to be before app.UseAuthorization();. Proper order is critical for security. Read more here. Similar issue here

  2. Second issue is that on API you are asking for audience = MyNumberV2Api but if you check your current token on https://jwt.ms/ there is no aud as MyNumberV2Api in the token. Read more here. To fix this we have two options:

    2.1. Change API to remove audience validation. To do this on API startup class use AddJwtBearer instead of AddIdentityServerAuthentication and set ValidateAudience = false. After change code would be like this:

    services.AddAuthentication("Bearer").AddJwtBearer("Bearer",
                 options =>
                 {
                     options.Authority = "http://localhost:5000";
                     options.Audience = "MyNumberV2Api";
                     options.RequireHttpsMetadata = false;
    
                     options.TokenValidationParameters = new 
    TokenValidationParameters()
                     {
                         ValidateAudience = false
                     };
                 });
    

    2.2. Add the audience to token. On IdentityServer - Config.cs, add the scopes to API resource:

    return new List<ApiResource>()
             {
                 new ApiResource("MyNumberV2Api","Customer API for MyNumberV2")
                 {
                     Scopes = new []{ "MyNumberV2Api" }
                 },
                 new ApiResource("ApiOne","Customer API for MyNumberV2"),
                 new ApiResource("ApiTwo","Customer API for MyNumberV2")
             };
    
    

    After this change, if you regenerate the token there will be a property as aud with value as MyNumberV2Api. Check the token on https://jwt.ms/

I suggest you to test every thing on http first and then try on https. For http you may need to remove app.UseHttpsRedirection(); in your code and also clean up the launchSettings.json to remove https URLS and make sure "sslPort": 0.

Lecky answered 30/6, 2020 at 19:9 Comment(1)
My ApiResource was missing the Scope, Thanks!!Piscatory
R
5

Just to add to the accepted answer. Instead of changing AddIdentityServerAuthentication, to use AddJwtBearer, IdentityServer 4 has support for older Validation. What helped me solve my issue was adding options.LegacyAudienceValidation = true; within my AddIdentityServerAuthentication.

Example:

services.AddAuthentication()
            .AddIdentityServerAuthentication("token", options =>
            {
                options.RequireHttpsMetadata = false;
                options.Authority = "https://localhost:5001";
                options.ApiName = "API name";
                options.LegacyAudienceValidation = true;
            });

The "token" as the first argument in my method allows me to use "Bearer" Authentication. It also specifies the Authentication Scheme that you are going to be using. However, you will need to update your [Authorize] attribute to handle the "token" to [Authorize(AuthenticationSchemes = "token")]. The setup on the client-side won't change at all and is useful when co-hosting your WebApi with IdentityServer 4. More info here: IdentityServer 4 Release Docs

Reseat answered 14/7, 2021 at 8:1 Comment(0)
A
1

Accepted answer is correct however the provided solution did not add the audience claim to my token. So I solved it different.

The bearer token you used does not have the aud claim as already mentioned: Here is your token at https://jwt.ms without the aud claim

To add the aud claim using in memory providers:
Make sure you add in memory identity resources that contains OpenId:

Startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentityServer(/** your options **/)
            .AddDeveloperSigningCredential()
            .AddInMemoryApiResources(Config.GetAllApiResources())
            .AddInMemoryClients(Config.GetClients())
            .AddInMemoryIdentityResources(Config.GetIdentityResources()) // !! ADDED
            .AddInMemoryApiScopes(Config.GetApiScopes());
    }

Config.GetIdentityResources()

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    yield return new IdentityServer4.Models.IdentityResources.OpenId();
}

This will add the aud claim to your token and enables validation.

Anglophobe answered 22/4, 2021 at 9:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.