在 WebApi 中优先配置多个备用身份验证选项

Configuring multiple alternate authentication options with precedence in WebApi

我不清楚如何优先实现多个备用身份验证选项 -

我可以单独实现成功

我不清楚的地方 - 如何配置它以便在 API 键 header 存在且 API 键有效时处理请求;也就是说,如果您有一个有效的 API-Key,则不需要 SSO。 如果没有显示 API-Key,那么您应该期望通过 SSO。

默认应该是 SSO - 因此我在 Program.cs

中配置了以下内容
builder.Services
        .AddAuthentication(
            options =>
            {
                // If an authentication cookie is present, use it to get authentication information
                options.DefaultAuthenticateScheme =
                    CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                // If authentication is required, and no cookie is present, 
                // use OAuth to sign in
                options.DefaultChallengeScheme = "OAuth";
            }
        )
        .AddCookie(
            options =>
            {
                options.AccessDeniedPath = "/accessdenied";
            }
        )
        .AddOAuth(
            "OAuth",
            (OAuthOptions options) =>
            {
             // abstracted out but takes care of claims, etc...   
             WebApp.Utils.OAuthHelper.Process(options, OAuthConfig);
            }
        );

但我的问题是 - 我该如何配置它来说明 - 如果 API 密钥 Header 存在,请不要理会 OAuth。

AuthenticationSchemeOptions class has ForwardDefaultSelector that allows any AuthenticationHandler 将身份验证事件转发给另一个处理程序。

因此,如果 API 密钥 header 不在 HTTP [=31] 中,那么您可以将 ApiKeyAuthenticationHandler 设置为默认值并使其将身份验证事件转发给 OAuth 处理程序=]s.

例如:假设您实现 ApiKeyAuthenticationHandler 如下:

// API Key options
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
    public string ApiKeyHeaderName { get; set; } = "X-MY-API-KEY";

    // Add more options here if needed
}

// API Key Authentication handler
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
    // Inject any additional services needed by your key handler here
    public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
    {
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        // Validation logic here
        // Check if the header exists
        if (!Context.Request.Headers.TryGetValue(Options.ApiKeyHeaderName, out var headerValues))
        {
            // Header is not present fail
            return AuthenticateResult.Fail("Invalid API key.");
        }

        // Header is present validate user
        var user = await GetApiKeyUserAsync(headerValues.First());
        if (user == null)
        {
            return AuthenticateResult.Fail("Invalid API key.");
        }

        // API Key is valid, generate a ClaimIdentity and Principal for the authenticated user
        var identity = new ClaimsIdentity(new Claim[]
        {
            new Claim(ClaimTypes.Name, user)
        }, Scheme.Name);

        var principal = new ClaimsPrincipal(identity);
        return AuthenticateResult.Success(new AuthenticationTicket(principal, null, Scheme.Name));
    }

    // Key validation logic here and you can map an API key to a user
    private async Task<string?> GetApiKeyUserAsync(string key)
    {
        // Add your implementation here
        if (key == "Alice's Key")
        {
            return "Alice";
        }

        if (key == "Bob's Key")
        {
            return "Bob";
        }

        return null;
    }
}

现在在启动 class 中调用 AddAuthentication 并将 "ApiKey" 设置为默认值,并添加一个选择器函数来检查 HTTP Headers 是否存在密钥,如果密钥存在,则 return null 并让 Api 密钥处理程序处理身份验证,否则转发到“OAuth”。

builder.Services
    .AddAuthentication(
        options =>
        {
            options.DefaultAuthenticateScheme =
                "ApiKey";
        }
    )
    .AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>("ApiKey", apiKeyOptions =>
    {
        // This scheme will forward Authentication to OAUTH if API Key header is not present
        apiKeyOptions.ForwardDefaultSelector = (context) =>
        {
            // Check the HTTP Request for the presence of Header
            if (!context.Request.Headers.TryGetValue(apiKeyOptions.ApiKeyHeaderName, out var headerValues))
            {
                // Header is not present, forward to OAuth
                return "OAuth"; // "OAuth" is the scheme name you specified when you called "AddOAuth()
            }

            // Do not forward the authentication
            return null;
        };
    })
    // Add other schemes here