ASP.NER Core 3.0 Identity Server 4 (4.0.0) SecurityTokenInvalidAudienceException: IDX10214: Error de validación de la audiencia. Audiencias: 'vacías'

2020-06-30 identityserver4 asp.net-core-3.0

Sigo recibiendo el siguiente error entre cartero e 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. En mi 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. Y aquí está el núcleo de startup.cs de mi API
 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. Aquí está mi repositorio público para el código de la solución completa: https://github.com/zachion/blazor-auth

  2. Así es como genero un token de autenticación en cartero: ingrese la descripción de la imagen aquí

  3. Y aquí está la sección del cuerpo de mi solicitud para obtener el token donde agrego el tipo Grant y Alcance ingrese la descripción de la imagen aquí

  4. Obtengo el token de la respuesta y lo agrego a las solicitudes de seguimiento para tratar de llegar a los controladores de la API real.

  5. Así es como agrego el token de autenticación en cartero. Emitir el token funciona bien ingrese la descripción de la imagen aquí

  6. Aquí está la colección completa de post man que uso:

{
    "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": {}
}

Answers

Hay 2 problemas en su código, empiezo con uno fácil de solucionar:

  1. En la clase de inicio de su API , mueva app.UseAuthentication(); estar antes de la app.UseAuthorization(); . El orden correcto es crítico para la seguridad. Lee más aquí . Problema similar aquí

  2. El segundo problema es que en la API está pidiendo audiencia = MyNumberV2Api pero si verifica su token actual en https://jwt.ms/ no hay aud como MyNumberV2Api en el token. Lee más aquí . Para arreglar esto tenemos dos opciones:

    2.1. Cambiar API para eliminar la validación de audiencia. Para hacer esto en la clase de inicio API, use AddJwtBearer lugar de AddIdentityServerAuthentication y establezca ValidateAudience = false . Después de cambiar el código sería así:

    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. Agregue la audiencia al token. En IdentityServer - Config.cs , agregue los ámbitos al recurso API:

    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")
             };
    
    

    Después de este cambio, si regenera el token, habrá una propiedad como aud con valor como MyNumberV2Api . Verifique el token en https://jwt.ms/

Le sugiero que pruebe todo en http primero y luego intente en https. Para http, es posible que deba eliminar app.UseHttpsRedirection(); en su código y también limpie launchSettings.json para eliminar https URLS y asegúrese de "sslPort": 0 .

Related