我了解可以使用自定义 JWT 提供程序在一个应用程序中添加多个身份验证方案。在以下文档中:https://github.com/AzureAD/microsoft-identity-web/wiki/Multiple-Authentication-Schemes,指出“Microsoft Identity Web 现在支持多种身份验证方案,从v. 1.11.0。”
我的问题如下:我想使用承载令牌作为对一个租户中的 Azure AD 资源和另一个租户中的 Azure AD B2C 资源进行身份验证的方法。
我尝试过以下方法:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
configuration.Bind("AzureAdB2C", options);
}, options =>
{
configuration.Bind("AzureAdB2C", options);
}, Constants.Bearer, true);
builder.Services.AddAuthentication()
.AddMicrosoftIdentityWebApi(options =>
{
configuration.Bind("AzureAd", options);
options.TokenValidationParameters.ValidAudiences = ["some-valid-audience-value-i-am-hiding-from-stack-overflow"];
}, options => { configuration.Bind("AzureAd", options); }, Constants.Bearer, true );
这会引发以下异常:
System.InvalidOperationException:“方案已存在:承载者”
接下来,我可以将
Constants.Bearer
的名称更改为辅助值,以避免出现此异常:IE:" "Bearer2"。现在,只有第一个 AzureAdB2C 令牌可以工作,而辅助 azure 广告令牌此时会失败.
有没有办法让
AddMicrosoftIdentityWebApi
尝试从两个单独的资源解码两个单独的承载令牌?
我最终通过以下方式使其工作:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
configuration.Bind("AzureAd", options);
options.TokenValidationParameters.ValidAudiences = [configuration["AzureAd:ClientId"]];
}, options => { configuration.Bind("AzureAd", options); })
.EnableTokenAcquisitionToCallDownstreamApi(_ => { })
.AddInMemoryTokenCaches();
builder.Services.AddAuthentication()
.AddMicrosoftIdentityWebApi(configuration, "AzureAdB2C", "B2CScheme")
.EnableTokenAcquisitionToCallDownstreamApi();
然后在 ASP .NET 中:
[Authorize(AuthenticationSchemes = "B2CScheme,Bearer")]
这应该允许两种模式都发挥作用。
使用 GraphQL 稍微复杂一些,但这是我的用例,所以我将其包含在这里。 将 GraphQL 与 HotChocolate 结合使用:
serviceCollection.AddGraphQLServer()
.AddHttpRequestInterceptor<AuthenticationInterceptor>()
.AddAuthorization();
然后您可以在此处编写 AuthenticationInterceptor:
public class AuthenticationInterceptor : DefaultHttpRequestInterceptor
{
public override async ValueTask OnCreateAsync(HttpContext context, IRequestExecutor requestExecutor, IQueryRequestBuilder requestBuilder, CancellationToken cancellationToken)
{
var result = await context.AuthenticateAsync("B2CScheme");
if (!result.Succeeded)
{
result = await context.AuthenticateAsync();
}
//not an else here because we reassign the value of result. Just looks confusing
if (result.Succeeded)
{
context.User = result.Principal;
}
await base.OnCreateAsync(context, requestExecutor, requestBuilder, cancellationToken);
}
}