Використовуйте багаторазову автентифікацію JWT-носія


87

Чи можна підтримати кілька видавців JWT Token в ASP.NET Core 2? Я хочу надати API для зовнішньої служби, і мені потрібно використовувати два джерела токенів JWT - Firebase та власні видавці токенів JWT. В ядрі ASP.NET я можу встановити автентифікацію JWT для схеми автентифікації на носій, але лише для одного авторизації:

  services
        .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Authority = "https://securetoken.google.com/my-firebase-project"
            options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidIssuer = "my-firebase-project"
                    ValidateAudience = true,
                    ValidAudience = "my-firebase-project"
                    ValidateLifetime = true
                };
        }

Я можу мати декількох видавців та аудиторій, але не можу встановити кілька авторитетів.


1
AFAIK ви можете додати будь-яку кількість властивостей до JWT. Отже, ніщо не заважає вам записати два імена емітентів у JWT. Проблема полягає в тому, що вашій програмі потрібно було б знати обидва ключі, якщо кожен емітент використовував інший ключ для підписання.
Тім Бігелейзен

Відповіді:


186

Ви можете повністю досягти того, що хочете:

services
    .AddAuthentication()
    .AddJwtBearer("Firebase", options =>
    {
        options.Authority = "https://securetoken.google.com/my-firebase-project"
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = "my-firebase-project"
            ValidateAudience = true,
            ValidAudience = "my-firebase-project"
            ValidateLifetime = true
        };
    })
    .AddJwtBearer("Custom", options =>
    {
        // Configuration for your custom
        // JWT tokens here
    });

services
    .AddAuthorization(options =>
    {
        options.DefaultPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .AddAuthenticationSchemes("Firebase", "Custom")
            .Build();
    });

Давайте розглянемо відмінності між вашим кодом та цим.

AddAuthentication не має параметра

Якщо ви встановите схему автентифікації за замовчуванням, тоді на кожному окремому запиті проміжне програмне забезпечення для автентифікації намагатиметься запустити обробник автентифікації, пов’язаний із схемою автентифікації за замовчуванням. Оскільки зараз у нас є дві можливі схеми автентифікації, немає сенсу запускати одну з них.

Використовуйте ще одне перевантаження AddJwtBearer

Кожен AddXXXметод додавання автентифікації має кілька перевантажень:

Тепер, оскільки ви використовуєте один і той же метод автентифікації двічі, але схеми автентифікації повинні бути унікальними, вам потрібно використовувати другу перевантаження.

Оновіть політику за замовчуванням

Оскільки запити більше не будуть автентифіковані автоматично, введення [Authorize]атрибутів для деяких дій призведе до відхилення запитів та HTTP 401видачі.

Оскільки це не те, що ми хочемо, оскільки ми хочемо надати обробникам автентифікації можливість аутентифікації запиту, ми змінюємо політику за замовчуванням системи авторизації, вказуючи як схеми автентифікації, так Firebaseі Customсхеми автентифікації, які слід намагатися автентифікувати запит.

Це не заважає вам бути більш обмежувальними щодо деяких дій; [Authorize]атрибут має AuthenticationSchemesвластивість , яке дозволяє перевизначити які аутентифікації схеми є дійсними.

Якщо у вас складніші сценарії, ви можете скористатись авторизацією на основі політики . Я вважаю, що офіційна документація чудова.

Давайте уявимо, що деякі дії доступні лише для токенів JWT, виданих Firebase, і повинні мати претензію з певним значенням; Ви можете зробити це таким чином:

// Authentication code omitted for brevity

services
    .AddAuthorization(options =>
    {
        options.DefaultPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .AddAuthenticationSchemes("Firebase", "Custom")
            .Build();

        options.AddPolicy("FirebaseAdministrators", new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .AddAuthenticationSchemes("Firebase")
            .RequireClaim("role", "admin")
            .Build());
    });

Потім ви можете використовувати [Authorize(Policy = "FirebaseAdministrators")]деякі дії.

Останній момент: Якщо ви ловите AuthenticationFailedподії та використовуєте щось, крім першої AddJwtBearerполітики, ви можете побачити, що IDX10501: Signature validation failed. Unable to match key...це спричинено тим, що система перевіряє кожну AddJwtBearerпо черзі, поки не отримає збіг. Зазвичай помилку можна ігнорувати.


4
Чи потрібно для цього змінити значення заголовка з Firebase або спеціального рішення? тобто замість того, Authorization : Bearer <token>щоб заголовок був, Authorization : Firebase <token>наприклад? Спробувавши це рішення, я отримав помилку: "Жоден обробник автентифікації не зареєстрований для схеми" Носій "."
Rush Frisby

4
Ні, заголовки змінювати не потрібно. Повідомлення про помилку передбачає, що ви маєте на увазі неіснуючу схему автентифікації (носій). У наших прикладах двома зареєстрованими схемами є Firebase та Custom, які є першими аргументами .AddJwtBearerвикликів методу.
Міккаел Деррі

5
Привіт. Шукав саме цього рішення. На жаль, я отримую виняток "Не було вказано схему автентифікації, і не було знайдено DefaultChallengeScheme". options.DefaultPolicy встановлено нормально. Будь-які ідеї?
terjetyl

11
Це була надзвичайно корисна відповідь, і я зібрав багато того, що я бачив по частинах повсюдно.
Арон В.

2
@TylerOhlsen це неправильно; хоча це буде використано у випадку, який ви описуєте, це не єдине. Він також буде використаний, якщо ви не вкажете вимогу авторизації на рівні кінцевої точки, але прикрасите контролери та / або дії MVC порожнім [Authorize]атрибутом.
Міккаел Деррі

4

Це продовження відповіді Мікаела Деррі.

Наш додаток має спеціальну вимогу авторизації, яку ми вирішуємо з внутрішнього джерела. Ми використовували Auth0, але переходимо до автентифікації облікового запису Microsoft за допомогою OpenID. Ось трохи відредагований код нашого запуску ASP.Net Core 2.1. Для майбутніх читачів це працює з моменту написання для зазначених версій. Абонент використовує id_token з OpenID для вхідних запитів, переданих як маркер носія. Сподіваюся, це допоможе комусь іншому, хто намагається здійснити перетворення ідентичності, настільки, наскільки мені допомогло це питання та відповідь.

const string Auth0 = nameof(Auth0);
const string MsaOpenId = nameof(MsaOpenId);

string domain = "https://myAuth0App.auth0.com/";
services.AddAuthentication()
        .AddJwtBearer(Auth0, options =>
            {
                options.Authority = domain;
                options.Audience = "https://myAuth0Audience.com";
            })
        .AddJwtBearer(MsaOpenId, options =>
            {
                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                {
                    ValidateAudience = true,
                    ValidAudience = "00000000-0000-0000-0000-000000000000",

                    ValidateIssuer = true,
                    ValidIssuer = "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0",

                    ValidateIssuerSigningKey = true,
                    RequireExpirationTime = true,
                    ValidateLifetime = true,
                    RequireSignedTokens = true,
                    ClockSkew = TimeSpan.FromMinutes(10),
                };
                options.MetadataAddress = "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0/.well-known/openid-configuration";
            }
        );

services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes( Auth0, MsaOpenId )
        .Build();

    var approvedPolicyBuilder =  new AuthorizationPolicyBuilder()
           .RequireAuthenticatedUser()
           .AddAuthenticationSchemes(Auth0, MsaOpenId)
           ;

    approvedPolicyBuilder.Requirements.Add(new HasApprovedRequirement(domain));

    options.AddPolicy("approved", approvedPolicyBuilder.Build());
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.