.NET Core OpenId Connect Server: общий доступ к нескольким приложениям

У меня есть два API, написанные на.NET Core и нацелены на 4.6.1:

  1. myAuthApi ( http://localhost:8496/): проверяет учетные данные и выдает токены клиентам. У него также есть конечная точка / api / values ​​/1 (с атрибутом Authorize в этом действии, используемом для проверки токенов)
  2. myPublicApi ( http://localhost:8497/): который получает токены от клиента в / api / values ​​/1 (с атрибутом Authorize для этого действия, также используется для проверки токенов). myPublicApi не имеет конечной точки токенов и предназначен для использования в качестве сервера ресурсов.

Я использую AspNet.Security.OpenIdConnect.Server 1.0.0. Оба API являются сервисами Service.Fabric Stateless

Я могу успешно получить токен со следующим форматом запроса на http://localhost:8496/connect/token

client_id=XX&client_secret=XXX&grant_type=password&username=XXX&password=XXX

При проверке токена по myAuthApi ( http://localhost:8496/api/values/1) он работает. Однако при использовании этого же токена против myPublicApi ( http://localhost:8497/api/values/1) это не так.

В обоих API, в Startup.cs, у меня есть

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Connect to Redis database.
        var redis = ConnectionMultiplexer.Connect(ConnectionHelper.GetRedisConnectionString(Configuration));
        services.AddDataProtection()
            .PersistKeysToRedis(redis, "DataProtection-Keys")
            .ProtectKeysWithCertificate(CertificateHandler.GetX509Certificate2(Configuration));

        // Add framework services.
        services.AddMvc().AddJsonOptions(opts =>
        {
            // we set the json serializer to follow camelCaseConventions when 
            // receiving /replying to JSON requests
            opts.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        });
        // we add authentication for the oAuth middleware to be registered in the DI container
        services.AddAuthentication();
    }

В myPublicApi у меня есть:

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {

        // Add a new middleware validating access tokens.
        app.UseOAuthValidation(options =>
        {
            // Automatic authentication must be enabled
            // for SignalR to receive the access token.
            options.AutomaticAuthenticate = true;
            options.Events = new OAuthValidationEvents
            {
                // Note: for SignalR connections, the default Authorization header does not work,
                // because the WebSockets JS API doesn't allow setting custom parameters.
                // To work around this limitation, the access token is retrieved from the query string.
                OnRetrieveToken = context =>
                {
                    // Note: when the token is missing from the query string,
                    // context.Token is null and the JWT bearer middleware will
                    // automatically try to retrieve it from the Authorization header.
                    context.Token = context.Request.Query["access_token"];

                    return Task.FromResult(0);
                }
            };
        });

        app.UseMvc();
    }

В myAuthApi у меня есть:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        // Add a new middleware validating access tokens.
        app.UseOAuthValidation(options =>
        {
            // Automatic authentication must be enabled
            // for SignalR to receive the access token.
            options.AutomaticAuthenticate = true;
            options.Events = new OAuthValidationEvents
            {
                // Note: for SignalR connections, the default Authorization header does not work,
                // because the WebSockets JS API doesn't allow setting custom parameters.
                // To work around this limitation, the access token is retrieved from the query string.
                OnRetrieveToken = context =>
                {
                    // Note: when the token is missing from the query string,
                    // context.Token is null and the JWT bearer middleware will
                    // automatically try to retrieve it from the Authorization header.
                    context.Token = context.Request.Query["access_token"];

                    return Task.FromResult(0);
                }
            };
        });

        // Add a new middleware issuing access tokens.
        app.UseOpenIdConnectServer(options =>
        {
            options.Provider = new AuthenticationProvider();
            // Enable the logout, token and userinfo endpoints.
            options.LogoutEndpointPath = "/connect/logout";
            options.TokenEndpointPath = "/connect/token";
            options.UserinfoEndpointPath = "/connect/userinfo";
            CertificateHandler.SetupCommonAuthServerOptions(options, Configuration);
        });

        app.UseMvc();
    }

Как видите, мой провайдер защиты данных хранит ключи в Redis, а я защищаю ключи с помощью сертификата, которым я делюсь между двумя приложениями. На сервере ресурсов не настроен ни один поставщик проверки подлинности, и при запуске не используется UseOpenIdConnectServer. В asp.net Web API 2 для расшифровки токенов в приложениях использовались общие машинные ключи.

Как я могу успешно проверить токен, выданный myAuthApi для всех других приложений, с помощью oAuthValidation?

РЕДАКТИРОВАТЬ, некоторые журналы можно увидеть здесь: https://pastebin.com/ACDz1fam

РЕДАКТИРОВАТЬ 2: После тщательного чтения журналов, я увидел, что для защиты токена использовался тот же поставщик защиты данных, но для разных целей:

"Performing unprotect operation to key {4406cfa7-a588-44ba-b73a-e25817d982d9} with purposes ('C:\SfDevCluster\Data\_App\_Node_4\TestMicroServicesType_App22\PublicApiPkg.Code.1.0.1', 'OpenIdConnectServerHandler', 'AccessTokenFormat', 'ASOS')."
"Performing unprotect operation to key {4406cfa7-a588-44ba-b73a-e25817d982d9} with purposes ('C:\SfDevCluster\Data\_App\_Node_3\TestMicroServicesType_App22\AuthApiPkg.Code.1.0.1', 'OpenIdConnectServerHandler', 'AccessTokenFormat', 'ASOS')."

Чтобы исправить это, @PinpointTownes предложил настроить провайдера защиты данных следующим образом:

    var redis = ConnectionMultiplexer.Connect(ConnectionHelper.GetRedisConnectionString(Configuration));
    services.AddDataProtection()
             // set the application name to a common value in all apps 
             // to have the same purpose and share the token across apps
            .SetApplicationName("MyTestMicroServices")
            .PersistKeysToRedis(redis, "DataProtection-Keys")
            .ProtectKeysWithCertificate(CertificateHandler.GetX509Certificate2(Configuration)); 

1 ответ

Решение

Вызов services.AddDataProtection().SetApplicationName("[your application name]") чтобы убедиться, что ваши два API используют один и тот же дискриминатор (используется для получения ключей шифрования), и он должен работать.

Другие вопросы по тегам