ใช้ JWT Bearer Authentication หลายตัว


89

เป็นไปได้ไหมที่จะรองรับผู้ออกโทเค็น JWT หลายตัวใน ASP.NET Core 2 ฉันต้องการจัดเตรียม API สำหรับบริการภายนอกและฉันต้องใช้แหล่งที่มาของโทเค็น JWT สองแหล่งนั่นคือ Firebase และผู้ออกโทเค็น JWT ที่กำหนดเอง ใน ASP.NET core ฉันสามารถตั้งค่าการพิสูจน์ตัวตน JWT สำหรับ Bearer auth Scheme ได้ แต่สำหรับหน่วยงานเดียวเท่านั้น:

  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 ปัญหามาจากการที่แอปพลิเคชันของคุณจำเป็นต้องทราบคีย์ทั้งสองหากผู้ออกแต่ละรายใช้คีย์อื่นในการลงนาม
Tim Biegeleisen

คำตอบ:


196

คุณสามารถบรรลุสิ่งที่คุณต้องการได้ทั้งหมด:

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
ไม่ส่วนหัวไม่จำเป็นต้องเปลี่ยน ข้อความแสดงข้อผิดพลาดแสดงให้เห็นว่าคุณกำลังอ้างถึงรูปแบบการพิสูจน์ตัวตนที่ไม่มีอยู่จริง (Bearer) ในตัวอย่างของเราทั้งสองรูปแบบที่ลงทะเบียนคือ Firebase และ Custom ซึ่งเป็นอาร์กิวเมนต์แรกของ.AddJwtBearerการเรียกใช้เมธอด
Mickaël Derriey

6
สวัสดี. กำลังมองหาวิธีแก้ปัญหานี้ น่าเสียดายที่ฉันได้รับข้อยกเว้น "ไม่มีการระบุ AuthenticationScheme และไม่พบ DefaultChallengeScheme" options.DefaultPolicy ถูกตั้งค่า ok ความคิดใด ๆ ?
terjetyl

13
นี่เป็นคำตอบที่มีประโยชน์อย่างยิ่งและรวบรวมสิ่งที่ฉันเคยเห็นมาเป็นชิ้น ๆ ไว้ทั่วทุกแห่ง
Aron W.

2
@TylerOhlsen ที่ไม่ถูกต้อง; ในขณะที่จะใช้ในกรณีที่คุณอธิบายมันไม่ใช่เพียงอย่างเดียว นอกจากนี้ยังจะใช้หากคุณไม่ได้ระบุข้อกำหนดการอนุญาตที่ระดับปลายทาง แต่ตกแต่งตัวควบคุม MVC และ / หรือการดำเนินการด้วย[Authorize]แอตทริบิวต์ว่างเปล่า
Mickaël Derriey

5

นี่เป็นส่วนเสริมของคำตอบของMickaël Derriey

แอปของเรามีข้อกำหนดการอนุญาตที่กำหนดเองซึ่งเราแก้ไขจากแหล่งภายใน เราใช้ 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());
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.