Несколько уполномоченных / издателей JWT в Asp.Net Core - PullRequest
0 голосов
/ 23 октября 2018

Я пытаюсь получить аутентификацию канала-носителя JWT в шлюзе API ASP.Net, используя Ocelot для работы с несколькими полномочиями / эмитентами.Один эмитент - Auth0, а другой - внутренний сервер аутентификации на основе IdentityServer4;мы пытаемся перейти от Auth0, но у нас есть внешние клиенты, которые по-прежнему зависят от него, поэтому мы хотели бы поддерживать оба, пока все не будет полностью протестировано на их переключение.

Согласно этого сообщения в блоге MSDN можно использовать несколько прав доступа, установив TokenValidationParameters.ValidIssuers вместо JwtBearerOptions.Authority.Тем не менее, я проверил это с Ocelot и без него, и никакая аутентификация не происходит, если Орган не настроен на орган, выдавший токен, независимо от содержимого TokenValidationParameters.ValidIssuers.

Кто-нибудь знает, как получить этоза работой?Вот как я настраиваю аутентификацию.Это работает, только если закомментированная строка не закомментирована (и только для токенов, выпущенных этим единственным органом).Я ожидаю, что Ocelot или ASP.Net Core получат ключ от сервера выдачи;оба обеспечивают JWK с помощью .well-known / openid-configuration, которая работает с промежуточным программным обеспечением ASP.Net Core.

    public static void AddJwtBearerAuthentication(this IServiceCollection services, IConfiguration configuration)
    {
        services
            .AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
            {
                //options.Authority = configuration["Jwt:Authority"];
                options.Audience  = configuration["Jwt:Audience"];
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer           = true,
                    ValidateIssuerSigningKey = true,
                    ValidateAudience         = true,
                    ValidAudience            = configuration["Jwt:Audience"],
                    ValidIssuers             = configuration
                        .GetSection("Jwt:Authorities")
                        .AsEnumerable()
                        .Select(kv => kv.Value)
                        .Where(s => !string.IsNullOrEmpty(s))
                        .ToArray()
                };
            });
    }

Вывод Ocelot, когда клиент имеет неверного эмитента (или когда мы используемTokenValidationParameters.ValidIssuer / ValidIssuers) connects:

[16:35:37 WRN] requestId: _____, previousRequestId: no previous request id, message: Error Code: UnauthenticatedError Message: Request for authenticated route _____ by  was unauthenticated errors found in ResponderMiddleware. Setting error response for request path:_____, request method: POST

Это аутентификация client_credentials, отсюда и отсутствие имени пользователя после «by».Как видите, Оцелот не говорит, в чем именно заключается проблема.Ядро промежуточного программного обеспечения JWT ASP.Net Core (без Ocelot) просто говорит, что подпись недействительна.Я подозреваю, что он либо не смотрит на TokenValidationParameters, либо я неправильно понял их назначение.

1 Ответ

0 голосов
/ 29 октября 2018

Я понял, как это сделать:

  1. Создайте конструктор аутентификации с помощью services.AddAuthentication().Вы можете установить схему по умолчанию («Bearer»), если хотите, но это не обязательно.

  2. Добавьте столько разных конфигураций JWT Bearer, сколько вы хотите, с помощью authenticationBuilder.AddJwtBearer(), каждая со своимсобственный ключ (например, «Auth0», «IS4», ...).Я использовал цикл над массивом в appsettings.json

  3. Создайте схему политики с authenticationBuilder.AddPolicyScheme и присвойте ей имя схемы "Носитель" (используйте JwtBearerDefaults.AuthenticationScheme, чтобы избежать появления магических строкв вашем коде) и установите options.ForwardDefaultSelector в обратном вызове функции, которая возвращает одно из имен других схем ("Auth0", "IS4" или что вы добавили) в зависимости от некоторого критерия.В моем случае он просто ищет имя схемы в издателе JWT (если издатель содержит «auth0», то используется схема Auth0).

Код:

public static void AddMultiSchemeJwtBearerAuthentication(
    this IServiceCollection services,
    IConfiguration configuration
)
{
    // Create JWT Bearer schemes.
    var schemes = configuration
        .GetSection("Jwt")
        .GetChildren()
        .Select(s => s.Key)
        .ToList()
    ;
    var authenticationBuilder = services.AddAuthentication();
    foreach (var scheme in schemes)
    {
        authenticationBuilder.AddJwtBearer(scheme, options =>
        {
            options.Audience  = configuration[$"Jwt:{scheme}:Audience"];
            options.Authority = configuration[$"Jwt:{scheme}:Authority"];
        });
    }

    // Add scheme selector.
    authenticationBuilder.AddPolicyScheme(
        JwtBearerDefaults.AuthenticationScheme,
        "Selector",
        options =>
        {
            options.ForwardDefaultSelector = context =>
            {
                // Find the first authentication header with a JWT Bearer token whose issuer
                // contains one of the scheme names and return the found scheme name.
                var authHeaderNames = new[] {
                    HeaderNames.Authorization,
                    HeaderNames.WWWAuthenticate
                };
                StringValues headers;
                foreach (var headerName in authHeaderNames)
                {
                    if (context.Request.Headers.TryGetValue(headerName, out headers) && !StringValues.IsNullOrEmpty(headers))
                    {
                        break;
                    }
                }

                if (StringValues.IsNullOrEmpty(headers))
                {
                    // Handle error. You can set context.Response.StatusCode and write a
                    // response body. Returning null invokes default scheme which will raise
                    // an exception; not sure how to fix this so the request is rejected.
                    return null;
                }

                foreach (var header in headers)
                {
                    var encodedToken = header.Substring(JwtBearerDefaults.AuthenticationScheme.Length + 1);
                    var jwtHandler = new JwtSecurityTokenHandler();
                    var decodedToken = jwtHandler.ReadJwtToken(encodedToken);
                    var issuer = decodedToken?.Issuer?.ToLower();
                    foreach (var scheme in schemes)
                    {
                        if (issuer?.Contains(scheme.ToLower()) == true)
                        {
                            // Found the scheme.
                            return scheme;
                        }
                    }
                }
                // Handle error.
                return null;
            };
        }
    );
}

Ничего особенного не требуется, чтобы Ocelot поддерживал это, просто используйте «Носитель» в качестве ключа поставщика аутентификации, и политика выбора схемы будет автоматически активирована.

...