Invalid_request, unauthorized_client с использованием OpenId, OAuth2, Cognito,. NET Core 3.1 и Swagger - PullRequest
0 голосов
/ 27 мая 2020

Я пытаюсь добавить уровень аутентификации в свой API с помощью OpenId и OAuth2, но когда я делаю вызов, передавая токен в заголовке, я продолжаю получать

Microsoft. IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolException: сообщение содержит ошибку: 'invalid_request', error_description: 'unauthorized_client', error_uri: 'error_uri is null'.

Я настроил AWS Cognito, startup.cs , и я могу успешно получить JWT token от Swagger или Postman.

Вы видите, что что-то плохо настроено или отсутствует в моем startup.cs?

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            services.AddAuthentication(c =>
                {
                    c.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    c.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    c.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
                })
                .AddCookie()
                .AddOpenIdConnect(c =>
                {
                    c.ResponseType = Configuration["Authentication:Cognito:ResponseType"];
                    c.MetadataAddress = Configuration["Authentication:Cognito:MetadataAddress"];
                    c.ClientId = Configuration["Authentication:Cognito:ClientId"];
                    c.Authority = "https://auth.myauthserver.com";

                    c.Scope.Add("myscope");
                    c.GetClaimsFromUserInfoEndpoint = true;
                });

            // Configure named auth policies that map directly to OAuth2.0 scopes
            services.AddAuthorization(c =>
            {
                c.AddPolicy("myscope", p => p.RequireClaim("scope", "myscope"));
            });

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo
                {
                    Title = "MyAPI", Version = "v1"
                });
                c.OperationFilter<AddAuthHeaderOperationFilter>();

                c.AddSecurityDefinition("bearer", //Name the security scheme
                    new OpenApiSecurityScheme{
                        Flows = new OpenApiOAuthFlows()
                        {
                            ClientCredentials = new OpenApiOAuthFlow()
                            {
                                TokenUrl = new Uri("https://auth.myauthserver.com/oauth2/token"),
                                Scopes = new Dictionary<string, string>(){ {"myscope", "Access API"}},
                                AuthorizationUrl = new Uri("https://auth.myautherver.com/oauth2/authorize")
                            }
                        }, 
                        Type = SecuritySchemeType.OAuth2,
                        OpenIdConnectUrl = new Uri("https://cognito-idp-url.../.well-known/openid-configuration"),
                        BearerFormat = "JWT",
                        In = ParameterLocation.Header,
                        Scheme = "bearer"
                    });

                c.AddSecurityRequirement(new OpenApiSecurityRequirement{ 
                    {
                        new OpenApiSecurityScheme{
                            Reference = new OpenApiReference{
                                Id = "Bearer",
                                Type = ReferenceType.SecurityScheme
                            },
                            OpenIdConnectUrl = new Uri("https://cognito-idp-url.../.well-known/openid-configuration")
                        },new List<string>(){"myscope"}
                    }
                });
            });

            services.AddOptions();

            services.AddCors(options => options.AddPolicy("CorsPolicy",
                builder =>
                {
                    builder
                        .AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .AllowCredentials();
                }));
        }

        // 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();
            else
                app.UseHsts();

            app.UseCors(policy => 
                 policy
                     .SetIsOriginAllowedToAllowWildcardSubdomains()
                     .AllowAnyOrigin()
                     .SetPreflightMaxAge(TimeSpan.FromDays(1))
             );
            app.UseCors("CorsPolicy");

            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
            app.UseAuthentication();

            app.UseSwagger();
            app.UseSwaggerUI(c => { 
                c.SwaggerEndpoint($"v1/swagger.json", "MyAPI v1");
                c.OAuth2RedirectUrl("https://auth.myauthserver.com/signin-oidc");
            });
        }

Контроллер имеет [Authorize] и [Produces("application/json")] атрибуты на нем.

Это AddAuthHeaderOperationFilter:

public class AddAuthHeaderOperationFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            var isAuthorized = (context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any()
                                || context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any())
                               && !context.MethodInfo.GetCustomAttributes(true).OfType<AllowAnonymousAttribute>().Any(); // this excludes methods with AllowAnonymous attribute

            if (!isAuthorized) return;

            operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" });
            operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" });

            var jwtbearerScheme = new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearer" }
            };

            operation.Security = new List<OpenApiSecurityRequirement>
            {
                new OpenApiSecurityRequirement { [jwtbearerScheme] = new string []{} }
            };
        }
    }

Это часть appsettings.json:

"Authentication": {
    "Cognito": {
      "ClientId": "47...",
      "IncludeErrorDetails": true,
      "MetadataAddress": "https://cognito-idp-url.../.well-known/openid-configuration",
      "RequireHttpsMetadata": false,
      "ResponseType": "code",
      "SaveToken": true,
      "TokenValidationParameters": {
        "ValidateIssuer": true
      }
    }
  },

Cognito настроен для приема потока OAuth для учетных данных клиента и выбрана разрешенная область аутентификации myscope.

А это пример curl:

curl -X GET "https://localhost:5001/v1/MyController/2" -H "accept: application/json" -H "Authorization: Bearer eyJraWQiOiI2dGFPTW..."

Спасибо

1 Ответ

0 голосов
/ 28 мая 2020

Я наконец нашел решение своего вопроса. Не все было настроено правильно, я оставлю здесь startup.cs, который работает с потоком учетных данных клиента и разрешает аутентификацию из Swagger и OpenAPI.

Authentication:Cognito:Authority настроен в appsettings.json как "Authority": "https://cognito-idp.eu-west-1.amazonaws.com/your_region-id"

 public void ConfigureServices(IServiceCollection services)
        {
            IdentityModelEventSource.ShowPII = true;

            services.AddControllers();

            Initializer.RegisterServices(services);

            services.AddAuthentication(o =>
                {
                    o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
                        {
                            var json = new WebClient().DownloadString(
                                parameters.ValidIssuer + "/.well-known/jwks.json");
                            var keys = JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
                            return (IEnumerable<SecurityKey>) keys;
                        },

                        ValidIssuer = Configuration["Authentication:Cognito:Authority"],
                        ValidateIssuerSigningKey = true,
                        ValidateIssuer = false,
                        ValidateLifetime = true,
                        ValidateAudience = false,
                    };
                    options.IncludeErrorDetails = true;
                    options.SaveToken = true;
                    options.Authority = Configuration["Authentication:Cognito:Authority"];
                    options.RequireHttpsMetadata = true;
                })
                .AddOpenIdConnect(options =>
                {
                    options.Authority = Configuration["Authentication:Cognito:Authority"];
                    options.RequireHttpsMetadata = false;
                    options.ClientId = Configuration["Authentication:Cognito:ClientId"];
                    options.Scope.Add("myscope");
                });;

            services.AddAuthorization(options => 
            {
                options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
                    .RequireAuthenticatedUser()
                    .Build();
            });

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc(ApiVersion, new OpenApiInfo
                {
                    Title = "MyAPI", Version = "v1"
                });
                c.OperationFilter<AddAuthHeaderOperationFilter>();

                c.AddSecurityDefinition("bearer", //Name the security scheme
                    new OpenApiSecurityScheme
                    {
                        Flows = new OpenApiOAuthFlows
                        {
                            ClientCredentials = new OpenApiOAuthFlow
                            {
                                TokenUrl = new Uri("https://auth.myauthserver.com/oauth2/token"),
                                Scopes = new Dictionary<string, string> {{"myscope", "Access API"}},
                                AuthorizationUrl = new Uri("https://auth.myauthserver.com/oauth2/authorize")
                            }
                        },
                        Type = SecuritySchemeType.OAuth2,
                        OpenIdConnectUrl =
                            new Uri(
                                "https://cognito-idp.eu-west-1.amazonaws.com/your_region-id/.well-known/openid-configuration"),
                        BearerFormat = "JWT",
                        In = ParameterLocation.Header,
                        Scheme = "bearer"
                    });
            });

            services.AddOptions();
        }

        // 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();
            else
                app.UseHsts();

            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
            app.UseAuthentication();

            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint($"{ApiVersion}/swagger.json", "MyAPI v1");
                c.OAuth2RedirectUrl("https://auth.myauthserver.com/signin-oidc");
                c.OAuthConfigObject = new OAuthConfigObject
                {
                    ClientId = Configuration["Authentication:Cognito:ClientId"],
                    UsePkceWithAuthorizationCodeGrant = true
                };
            });
        }

Надеюсь, поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...