Sử dụng nhiều Xác thực mang JWT


87

Có thể hỗ trợ nhiều nhà phát hành Mã thông báo JWT trong ASP.NET Core 2 không? Tôi muốn cung cấp một API cho dịch vụ bên ngoài và tôi cần sử dụng hai nguồn mã thông báo JWT - Firebase và nhà phát hành mã thông báo JWT tùy chỉnh. Trong lõi ASP.NET, tôi có thể đặt xác thực JWT cho lược đồ xác thực Bearer, nhưng chỉ cho một Cơ quan:

  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
                };
        }

Tôi có thể có nhiều công ty phát hành và khán giả, nhưng tôi không thể đặt nhiều Tổ chức phát hành.


1
AFAIK bạn có thể thêm bất kỳ số lượng thuộc tính nào vào JWT. Vì vậy, không có gì ngăn cản bạn ghi hai tên công ty phát hành trong một JWT. Vấn đề nằm ở chỗ ứng dụng của bạn sẽ cần biết cả hai khóa, nếu mỗi nhà phát hành sử dụng một khóa khác nhau để ký.
Tim Biegeleisen

Câu trả lời:


186

Bạn hoàn toàn có thể đạt được điều mình muốn:

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();
    });

Hãy xem xét sự khác biệt giữa mã của bạn và mã đó.

AddAuthentication không có tham số

Nếu bạn đặt một sơ đồ xác thực mặc định, thì trên mọi yêu cầu, phần mềm trung gian xác thực sẽ cố gắng chạy trình xử lý xác thực được liên kết với sơ đồ xác thực mặc định. Vì bây giờ chúng ta có hai chương trình xác thực có thể hiển thị, nên chẳng ích gì khi chạy một trong số chúng.

Sử dụng quá tải khác của AddJwtBearer

Mỗi AddXXXphương pháp đơn để thêm xác thực đều có một số quá tải:

  • Một nơi mà lược đồ xác thực mặc định được liên kết với phương thức xác thực được sử dụng, như bạn có thể thấy ở đây để xác thực cookie
  • Một nơi bạn vượt qua, ngoài cấu hình của các tùy chọn, tên của lược đồ xác thực, như trên quá tải này

Bây giờ, vì bạn sử dụng cùng một phương pháp xác thực hai lần nhưng các lược đồ xác thực phải là duy nhất, bạn cần sử dụng quá tải thứ hai.

Cập nhật chính sách mặc định

Vì các yêu cầu sẽ không được xác thực tự động nữa, nên việc đặt [Authorize]các thuộc tính trên một số hành động sẽ dẫn đến việc các yêu cầu bị từ chối và HTTP 401sẽ được cấp.

Vì đó không phải là những gì chúng tôi muốn vì chúng tôi muốn cung cấp cho các trình xử lý xác thực cơ hội xác thực yêu cầu, chúng tôi thay đổi chính sách mặc định của hệ thống ủy quyền bằng cách chỉ ra cả hai lược đồ xác thực FirebaseCustomnên được thử để xác thực yêu cầu.

Điều đó không ngăn cản bạn hạn chế hơn đối với một số hành động; các [Authorize]thuộc tính có một AuthenticationSchemestài sản đó cho phép bạn ghi đè lên mà xác thực các chương trình có giá trị.

Nếu bạn có nhiều tình huống phức tạp hơn, bạn có thể sử dụng ủy quyền dựa trên chính sách . Tôi thấy tài liệu chính thức là tuyệt vời.

Hãy tưởng tượng một số hành động chỉ khả dụng đối với mã thông báo JWT do Firebase phát hành và phải có xác nhận quyền sở hữu với giá trị cụ thể; bạn có thể làm theo cách này:

// 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());
    });

Sau đó, bạn có thể sử dụng [Authorize(Policy = "FirebaseAdministrators")]trên một số hành động.

Một điểm cuối cùng cần lưu ý: Nếu bạn đang nắm bắt AuthenticationFailedcác sự kiện và sử dụng bất kỳ thứ gì ngoại trừ AddJwtBearerchính sách đầu tiên , bạn có thể thấy IDX10501: Signature validation failed. Unable to match key...Điều này là do hệ thống kiểm tra từng AddJwtBearerlượt một cho đến khi khớp. Lỗi này thường có thể được bỏ qua.


4
Điều này có yêu cầu thay đổi giá trị tiêu đề từ firebase hoặc giải pháp tùy chỉnh không? I E. thay vì Authorization : Bearer <token>đó là tiêu đề Authorization : Firebase <token>chẳng hạn? Khi thử giải pháp này, tôi gặp lỗi: "Không có trình xử lý xác thực nào được đăng ký cho lược đồ 'Bearer'."
Rush Frisby

4
Không, tiêu đề không cần thay đổi. Thông báo lỗi cho thấy rằng bạn đang đề cập đến một lược đồ xác thực không tồn tại (Bearer). Trong các ví dụ của chúng tôi, hai lược đồ đã đăng ký là Firebase và Custom, là các đối số đầu tiên của các .AddJwtBearerlệnh gọi phương thức.
Mickaël Derriey

6
Chào. Chỉ tìm kiếm giải pháp này. Rất tiếc, tôi nhận được thông báo "Không có xác thực nào được chỉ định và không tìm thấy ngoại lệ DefaultChallengeScheme". tùy chọn.DefaultPolicy được thiết lập ok. Có ý kiến ​​gì không?
terjetyl

11
Đây là một câu trả lời cực kỳ hữu ích và tổng hợp rất nhiều điều tôi đã thấy ở khắp nơi.
Aron W.

2
@TylerOhlsen không đúng; trong khi nó sẽ được sử dụng trong trường hợp bạn mô tả, nó không phải là trường hợp duy nhất. Nó cũng sẽ được sử dụng nếu bạn không chỉ định yêu cầu ủy quyền ở cấp điểm cuối, nhưng trang trí bộ điều khiển MVC và / hoặc hành động bằng một [Authorize]thuộc tính trống .
Mickaël Derriey

4

Đây là phần mở rộng của câu trả lời của Mickaël Derriey.

Ứng dụng của chúng tôi có yêu cầu ủy quyền tùy chỉnh mà chúng tôi giải quyết từ nguồn nội bộ. Chúng tôi đang sử dụng Auth0 nhưng đang chuyển sang xác thực Tài khoản Microsoft bằng OpenID. Đây là đoạn mã được chỉnh sửa một chút từ Khởi động ASP.Net Core 2.1 của chúng tôi. Đối với độc giả trong tương lai, điều này hoạt động kể từ khi viết này cho các phiên bản được chỉ định. Người gọi sử dụng id_token từ OpenID trên các yêu cầu đến được chuyển dưới dạng mã thông báo Bearer. Hy vọng nó sẽ giúp ai đó khác đang cố gắng thực hiện chuyển đổi cơ quan nhận dạng nhiều như câu hỏi và câu trả lời này đã giúp tôi.

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());
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.