动态更改 asp.net 核心的授权方法质询方案

Dynamically change the authorization method challenge scheme for asp.net core

我有一个 IdentityServer 和其他几个 OpenID Connect 提供商。当我将我的 ASP.Net 核心应用程序连接到我的 IdentityServer 时,我将此代码包含在我的 Startup.ConfigureServices 方法中:

services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
    options.Authority = "https://myIdentityServer";
    options.ClientId = "myClientId";
    options.ClientSecret = "myClientSecret";
    options.ResponseType = "code";
    options.UsePkce = true;
    options.SaveTokens = true;
});

到目前为止,如此标准。问题是我的应用程序将在几个不同的 URL 上 运行。例如,我可以在 https://foo.example.com, and another one under https://bar.example.com 下有一个实例 运行 。对于其中的每一个,我都想使用不同的客户端。所以我开始在我的 ConfigureServices 方法中添加另一个 OIDC 注册:

services.AddAuthentication(...
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", ...)
.AddOpenIdConnect("anotherregistration", options =>
{
    options.Authority = "https://myIdentityServer";
    options.ClientId = "mySecondClientId";
    options.ClientSecret = "anotherClientSecret";
    options.ResponseType = "code";
    options.UsePkce = true;
    options.SaveTokens = true;
});

我无法找到如何让我的 ASP.Net 核心应用程序根据 URL 应用程序 运行 决定使用哪个 OIDC 注册。我该怎么做?

一个问题是您需要为每个 OpenIDConnect 条目设置不同的回调路径。对于来自 IdentityServer 的传入请求,这是每个人都在监听的唯一 URL。

像这个例子:

.AddOpenIdConnect(options =>
{
  //...
 options.CallbackPath = "/app1-signin-oidc";

} 
.AddOpenIdConnect(options =>
{
  //...
 options.CallbackPath = "/app2-signin-oidc";

} 

您需要配置的三个路径如下(定义在OpenIdConnectOptions class)

public OpenIdConnectOptions()
{
    CallbackPath = new PathString("/signin-oidc");
    SignedOutCallbackPath = new PathString("/signout-callback-oidc");
    RemoteSignOutPath = new PathString("/signout-oidc");

当用户点击“sign-in”按钮时,您可以select使用什么sign-in方案,像这样:

HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, user, authProperties);

我自己想出来了。有一个名为 IAuthenticationSchemeProvider 的接口,您可以覆盖它。我的代码如下。 GetRequestSchemeAsync() 是大部分工作的内容。此代码假定您的 OIDC 注册名称与子域相同。因此对于子域 foo,您需要注册 AddOpenIdConnect("foo", ....)

public class SubdomainAuthenticationSchemeProvider : AuthenticationSchemeProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CustomAuthenticationSchemeProvider(
        IHttpContextAccessor httpContextAccessor,
        IOptions<AuthenticationOptions> options)
        : base(options)
    {
        this._httpContextAccessor = httpContextAccessor;
    }

    private Task<AuthenticationScheme> GetRequestSchemeAsync()
    {
        // Get the subdomain for the host the request came from. Assumes no WWW in the string.
        var subdomainName = _httpContextAccessor.HttpContext.Request.Host.Host.Split(new char[] { '.' })[0];

        return GetSchemeAsync(subdomainName);
    }

    public override async Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync() => await GetRequestSchemeAsync() ;

    public override async Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync() =>
        await GetRequestSchemeAsync() ??
        await base.GetDefaultChallengeSchemeAsync();

    public override async Task<AuthenticationScheme> GetDefaultForbidSchemeAsync() =>
        await GetRequestSchemeAsync() ??
        await base.GetDefaultForbidSchemeAsync();

    public override async Task<AuthenticationScheme> GetDefaultSignInSchemeAsync() =>
        await GetRequestSchemeAsync() ??
        await base.GetDefaultSignInSchemeAsync();

    public override async Task<AuthenticationScheme> GetDefaultSignOutSchemeAsync() =>
        await GetRequestSchemeAsync() ??
        await base.GetDefaultSignOutSchemeAsync();

}

在此之后,将其添加到 ConfigureServices 方法中。

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IAuthenticationSchemeProvider, SubdomainAuthenticationSchemeProvider>();