. NET Core 3.1 IdentityServer4: получение недопустимого токена доступа при использовании предоставления учетных данных пароля владельца ресурса - PullRequest
0 голосов
/ 11 февраля 2020

Я пытаюсь получить токен доступа от провайдера идентификации, используя тип предоставления учетных данных пароля владельца ресурса. Та же конфигурация работала для. NET Core 2.2, но больше не работает для. NET Core 3.1. Вот конфигурация Identity Provider:

public class Startup
{
    public IConfiguration Configuration { get; }
    private readonly string _MyAllowSpecificOrigins = "fooorigin";
    private readonly IWebHostEnvironment _env;

    public Startup(IConfiguration configuration, IWebHostEnvironment env)
    {
        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        IdentityModelEventSource.ShowPII = true;
        _env = env;
        Configuration = configuration;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });

        services.AddPersistence(Configuration); //Custom extension
        services.AddAutoMapper(Assembly.GetAssembly(typeof(BaseMappingProfile)));

        #region Options
        services.Configure<IdentityServerOptions>(Configuration.GetSection("IdentityServerOptions"));
        services.Configure<Settings>(Configuration.GetSection("Settings"));
        #endregion

        #region Configurations
        services.AddTransient<IdentityServerOptions>();
        #endregion

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddScoped<ITokenManagerHelper, TokenManagerHelper>();

        services.AddScoped<IUserService, UserService>();

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

        services.AddMvc().AddFluentValidation(fv =>
        {
            fv.RegisterValidatorsFromAssemblyContaining<CommonValidator>();
            fv.ImplicitlyValidateChildProperties = true;
        })
        .SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

        var identityServerDataDBConnectionString = Configuration.GetConnectionString("IdentityServerConfigDatabase");
        var migrationsAssembly = typeof(UsersDbContext).GetTypeInfo().Assembly.GetName().Name;
        var identityAuthority = Configuration.GetValue<string>("IdentityServerOptions:Authority");

        // Add Authentication
        services.AddAuthentication(options =>
           {
               options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               options.DefaultChallengeScheme = "oidc";
           })
          .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
          .AddOpenIdConnect("oidc", options =>
          {
              options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
              options.Authority = Configuration.GetValue<string>("IdentityServerOptions:Authority");
              options.ClientId = Configuration.GetValue<string>("IdentityServerOptions:ClientName");
              options.ClientSecret = Configuration.GetValue<string>("IdentityServerOptions:ClientSecret");
              options.ResponseType = Configuration.GetValue<string>("IdentityServerOptions:ResponseType");

              options.Scope.Add("openid");
              options.Scope.Add("profile");
              options.Scope.Add("roles");
              options.Scope.Add("fooapi");
              options.Scope.Add("fooidentityapi");
              options.Scope.Add("offline_access");
              options.SaveTokens = true;

              options.GetClaimsFromUserInfoEndpoint = true;
              options.ClaimActions.Remove("amr");
              options.ClaimActions.DeleteClaim("sid");

              options.TokenValidationParameters = new TokenValidationParameters
              {
                  NameClaimType = JwtClaimTypes.GivenName,
                  RoleClaimType = JwtClaimTypes.Role,
              };
          });

        services.AddTransient<IPersistedGrantStore, PersistedGrantStore>();
        services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();

        // Add Identity Server
        // Add Signing Certificate
        // Add Users Store
        // Add Configurations Store
        // Add Operational Stores
        if (_env.IsDevelopment() || _env.IsStaging())
        {
            services.AddIdentityServer()
            .AddDeveloperSigningCredential()
            .AddUserStore()
            .AddConfigurationStore(options =>
            {
                options.ConfigureDbContext = builder =>
                {
                    builder.UseSqlServer(identityServerDataDBConnectionString,
                        sql => sql.MigrationsAssembly(migrationsAssembly));
                };
            })
            .AddOperationalStore(options =>
            {
                options.ConfigureDbContext = builder =>
                {
                    builder.UseSqlServer(identityServerDataDBConnectionString,
                        sql => sql.MigrationsAssembly(migrationsAssembly));
                };
            })
            .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();
        }
        else
        {
            //Todo: add certificate
        }
    }

    public void Configure(
        IApplicationBuilder app, 
        IWebHostEnvironment env, 
        IOptions<Settings> settingOptions)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseCors(_MyAllowSpecificOrigins);

        app.UseCookiePolicy();
        var forwardOptions = new ForwardedHeadersOptions
        {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
            RequireHeaderSymmetry = false
        };

        forwardOptions.KnownNetworks.Clear();
        forwardOptions.KnownProxies.Clear();

        app.UseForwardedHeaders(forwardOptions);

        app.UseAuthentication();
        app.UseRouting();
        app.UseIdentityServer();
    }
}

А вот конфигурация API:

public class Startup
{
    #region Private Fields
    private readonly string _allowedOrigins = "fooorigin";
    #endregion

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddAuthorization();
        services.AddAuthentication("Bearer")
            .AddIdentityServerAuthentication(options =>
            {
                options.Authority = Configuration.GetValue<string>("IdentityServerOptions:Authority");
                options.RequireHttpsMetadata = false;
                options.ApiName = Configuration.GetValue<string>("IdentityServerOptions:ApiName");
                options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
            });
        services.Configure<CookiePolicyOptions>(options =>
        {
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });

        #region Options
        services.AddOptions();
        #endregion

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

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

        services.AddAntiforgery(options =>
        {
            options.HeaderName = "X-XSRF-TOKEN";
        });
        services.AddMvc(o =>
        {
            o.EnableEndpointRouting = false;
            o.Conventions.Add(new ApiExplorerGroupPerVersionConvention());
            o.Filters.Add(new ModelStateFilter());
        }).AddFluentValidation(fv =>
        {
            fv.RegisterValidatorsFromAssemblyContaining<CommonValidator>();
            fv.ImplicitlyValidateChildProperties = true;
        })

        .SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

        #region Customise default API behavour
        services.Configure<ApiBehaviorOptions>(options =>
        {
            options.SuppressModelStateInvalidFilter = true;
        });
        #endregion

        #region Versioning
        services.AddApiVersioning(o =>
        {
            o.ApiVersionReader = new HeaderApiVersionReader("api-version");
            o.DefaultApiVersion = new ApiVersion(1, 0);
            o.AssumeDefaultVersionWhenUnspecified = true;
            o.ReportApiVersions = true;
        });
        #endregion

        #region Register the Swagger generator, defining 1 or more Swagger documents
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1.0", new OpenApiInfo
            {
                Title = Configuration.GetValue<string>("SwaggerDocOptions:Title"),
                Version = Configuration.GetValue<string>("SwaggerDocOptions:Version"),
                Description = Configuration.GetValue<string>("SwaggerDocOptions:Description")
            });

            c.OperationFilter<RemoveApiVersionFromParamsOperationFilter>();

            var basePath = PlatformServices.Default.Application.ApplicationBasePath;
            var xmlPath = Path.Combine(basePath, "foo.xml");
            c.IncludeXmlComments(xmlPath);

            var scopes = Configuration.GetValue<string>("IdentityServerOptions:RequiredScopes").Split(',').ToList();
            var scopesDictionary = new Dictionary<string, string>();
            foreach (var scope in scopes)
            {
                scopesDictionary.Add(scope, scope);
            }

            c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
            {
                Name = "Authorization",
                Type = SecuritySchemeType.OAuth2,
                Scheme = "Bearer",
                Flows = new OpenApiOAuthFlows
                {
                    Password = new OpenApiOAuthFlow
                    {
                        TokenUrl = new Uri(Configuration.GetValue<string>("IdentityServerOptions:TokenEndpoint")),
                        Scopes = scopesDictionary
                    }
                },
                In = ParameterLocation.Header
            });

            c.AddSecurityRequirement(new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "Bearer"
                        },
                        Type = SecuritySchemeType.Http,
                        Scheme = "Bearer",
                        Name = "Bearer",
                        In = ParameterLocation.Header
                    },
                    new List<string>()
                }
            });
        });
        #endregion
    }

    /// <summary>
    /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    /// </summary>
    /// <param name="app">Application builder</param>
    /// <param name="env">Web host environment</param>
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }


        app.UseCors(_allowedOrigins);
        app.UseAuthentication();
        app.UseSerilogRequestLogging();
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });

        app.UseSwagger(
            o =>
            {
                o.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
                {
                    var paths = new OpenApiPaths();
                    foreach (var x in swaggerDoc.Paths)
                    {
                        var key = x.Key.Contains("{version}") ? x.Key.Replace("{version}", swaggerDoc.Info.Version) : x.Key;
                        paths.Add(key, x.Value);
                    }
                    swaggerDoc.Paths = paths;
                    swaggerDoc.Extensions.Add(
                        new KeyValuePair<string,
                        IOpenApiExtension>("x-identity-authority",
                        new OpenApiString(Configuration.GetValue<string>("IdentityServerOptions:Authority"))));
                });
                o.RouteTemplate = "docs/{documentName}/swagger.json";
            });
        app.UseSwaggerUI(
            c =>
            {
                c.SwaggerEndpoint("/docs/v1.0/swagger.json", "Foo API");
                c.OAuthClientId(Configuration.GetValue<string>("IdentityServerOptions:ClientName"));
                c.OAuthClientSecret(Configuration.GetValue<string>("IdentityServerOptions:ClientSecret"));
            }
        );
    }
}

Теперь давайте посмотрим на процесс получения токена доступа: enter image description here

Когда я нажимаю «Авторизовать», он проверяется и получает токен:

enter image description here

, но когда я пытаюсь ресурс API доступа, требующий авторизации, возвращает ошибку 401:

enter image description here

Я пытался проверить то же самое в Почтальоне и при попытке получить доступ к токену конечная точка возвращает токен доступа следующим образом:

enter image description here

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

1 Ответ

0 голосов
/ 11 февраля 2020

После исследования я обнаружил, что ApiName должно совпадать с именем аудитории, также мы должны настраивать клиенты для токенов JWT, а не эталонных токенов.

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