多种身份验证方案 ASPNET Core 3

Don*_*tty 6 c# authentication jwt azure-ad-b2c asp.net-core

请参阅下面的更新

我正在使用 Azure AD B2C,希望我的用户能够通过我的 Web 应用程序登录,并能够利用 JWT 不记名令牌并从移动应用程序调用 Web API 方法。

我可以让任一身份验证方案自行工作。例如,在我的startup.cs中我可以执行以下操作:

        services
            .AddAuthentication(AzureADB2CDefaults.AuthenticationScheme)
            .AddAzureADB2C(options => Configuration.Bind("AzureAdB2C", options));
Run Code Online (Sandbox Code Playgroud)

其按预期工作(用户可以登录网站,但 JWT 不起作用)。

或者,我可以使用以下内容,然后只有 JWT 不记名令牌才可以工作:

        services
            .AddAuthentication(AzureADB2CDefaults.JwtBearerAuthenticationScheme)
            .AddAzureADB2CBearer(options => Configuration.Bind("AzureAdB2C", options));
Run Code Online (Sandbox Code Playgroud)

如果我想要其中任何一个工作,我可以执行以下操作(在/sf/answers/3479447331/的帮助下)

        services
            .AddAuthentication()
            .AddAzureADB2C(options => Configuration.Bind("AzureAdB2C", options))
            .AddAzureADB2CBearer(options => Configuration.Bind("AzureAdB2C", options));

        services.AddAuthorization(options =>
        {
            options.DefaultPolicy = new AuthorizationPolicyBuilder()
                                    .RequireAuthenticatedUser()
                                    .AddAuthenticationSchemes(AzureADB2CDefaults.AuthenticationScheme, AzureADB2CDefaults.JwtBearerAuthenticationScheme)
                                    .Build();
        }
Run Code Online (Sandbox Code Playgroud)

现在两者都可以工作。(编辑:实际上,它们并不能完全工作)

但是,我也有这个代码:

        app.UseAuthentication();
        app.UseMiddleware<MyAfterAuthenticatedMiddleware>();
        app.UseAuthorization();
Run Code Online (Sandbox Code Playgroud)

问题是,当我使用任一身份验证的组合时,当我的中间件代码运行时,我的用户未经过身份验证(在中间件代码中)并且没有声明等,但稍后在管道中获取它们。

这里发生了什么事?如何解决这个问题?

看来当我不指定默认身份验证方案时(为了拥有多个方案),直到管道中的授权步骤时,身份验证才会发生。

我需要我的中间件在身份验证之后和授权之前运行。如何通过多种身份验证方案实现这一点?

更新——解决了!但一定有更好的方法!

首先,我要向那些创建 .NET 安全产品的人们表示敬意。这很重要,也很困难。我确实认为可能还有很大的改进空间。

大多数开发人员在必要时都会涉足安全领域,然后又回到他们的“常规”工作中。除非你每天都使用它,否则很难保持领先。每次你回到它时,一切都会再次改变。

这一定是一个常见的场景:我希望我的用户能够登录我的网站并与各种 Web API 方法进行交互。我希望他们也能够通过其他方式访问这些相同的 API 方法,例如移动应用程序 - 我将在其中使用 JWT 令牌或等效方法。

这应该不难做。

然而,我却陷入了为此创建处理程序和政策的困境中。一件事会起作用,但另一件事不会。然后,当我认为事情是正确的时,我发现一些挑战和禁止逻辑并没有按预期工作

内置的授权中间件具有进行身份验证的能力——这是我早期走的路之一,结果发现它并没有完全发挥作用——并且它给我带来了其他问题,如上所述。

我认为,授权期间应该进行身份验证。身份验证应该在预期的地方进行——在身份验证中间件中。(我的猜测是,它是在授权中添加的,以便解决多年前出现的其他一些问题 - 也许今天仍然存在)

不管怎样——这就是我最终让事情开始工作的方法。它可能会更干净、更光滑、更灵活,但它可以满足我的需求。而且它比我见过的任何其他东西都不像黑客。但是有没有一个更好的内置类可以为我完成这个任务呢?

我的新问题是:有没有比我下面描述的更好的方法来完成这项工作?。很难相信这是最好的方法。

在 Startup.ConfigureServices 中,我现在有以下内容:

services
   .AddAuthentication(AzureADB2CDefaults.AuthenticationScheme)
   .AddAzureADB2C(options => Configuration.Bind("AzureAdB2C", options))
   .AddAzureADB2CBearer(options => Configuration.Bind("AzureAdB2C", options));
Run Code Online (Sandbox Code Playgroud)

然后我还有:

services.AddHttpContextAccessor();
services.AddSingleton<IAuthenticationSchemeProvider, MyAuthenticationSchemeProvider>();
Run Code Online (Sandbox Code Playgroud)

最后,我有一个新课程:

public class MyAuthenticationSchemeProvider : AuthenticationSchemeProvider
{
    public MyAuthenticationSchemeProvider(IOptions<AuthenticationOptions> options, IHttpContextAccessor httpContextAccessor) : base(options)
    {
        HttpContextAccessor = httpContextAccessor;
    }

    protected MyAuthenticationSchemeProvider(IOptions<AuthenticationOptions> options, IDictionary<string, AuthenticationScheme> schemes, IHttpContextAccessor httpContextAccessor) : base(options, schemes)
    {
        HttpContextAccessor = httpContextAccessor;
    }

    private IHttpContextAccessor HttpContextAccessor { get; }

    private bool IsBearerRequest()
    {
        var httpContext = HttpContextAccessor.HttpContext;
        return httpContext.Request.Headers.ContainsKey("Authorization")
                && httpContext.Request.Headers["Authorization"].Any(x => x.ToLower().Contains("bearer"));
    }

    public async Task<AuthenticationScheme> GetMySchemeAsync()
    {
        return IsBearerRequest()
            ? await GetSchemeAsync(AzureADB2CDefaults.BearerAuthenticationScheme)
            : await base.GetDefaultAuthenticateSchemeAsync();
    }

    public override async Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync()
    {
        return await GetMySchemeAsync();
    }

    public override Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync()
    {
        return GetMySchemeAsync();
    }

    public override Task<AuthenticationScheme> GetDefaultForbidSchemeAsync()
    {
        return GetMySchemeAsync();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我可以使用两种身份验证、质询和禁止按预期工作。为什么没有一个允许在身份验证方案之间切换的内置类?为什么授权中间件尝试使用多个方案进行身份验证(我说它根本不应该这样做),而不是身份验证中间件?

现在,这适用于遇到类似问题的其他人。