dotnet核心WebApp和使用AzureB2C,MSAL的多个Web api的访问令牌

问题描述 投票:0回答:1

我已经为WebApp和Api设置了身份验证/授权,并且可以正常工作。问题是当我必须引入其他的Api时(将从WebAPP中调用)。

局限性是,您不能在一次调用中询问具有混合Web api范围的令牌。这是服务(AAD)的限制,而不是库的限制。您必须向https:// {tenant} .onmicrosoft.com / api1 / read索取令牌然后您可以静默获取https:// {tenant} .onmicrosoft.com / api2 / read的令牌,因为这是两个不同的APIS。我从SO herehere]了解了更多信息

由于除了几行代码外没有完整的示例,所以我试图找到实现此解决方案的最佳方法。

当前我在启动中已设置身份验证

 services.AddAuthentication(sharedOptions =>
 {
    sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
 })

 services.AddAzureAdB2C(options => Configuration.Bind("AzureAdB2C", options)).AddCookie();

[AddAzureAdB2C是来自Samples的自定义扩展方法。

public static AuthenticationBuilder AddAzureAdB2C(this AuthenticationBuilder builder, Action<AzureAdB2COptions> configureOptions)
{
    builder.Services.Configure(configureOptions);
        builder.Services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, OpenIdConnectOptionsSetup>();
    builder.AddOpenIdConnect();
        return builder;
}

public class OpenIdConnectOptionsSetup : IConfigureNamedOptions<OpenIdConnectOptions>
{
    public void Configure(OpenIdConnectOptions options)
    {
        options.ClientId = AzureAdB2COptions.ClientId;
        options.Authority = AzureAdB2COptions.Authority;
        options.UseTokenLifetime = true;
        options.TokenValidationParameters = new TokenValidationParameters() { NameClaimType = "name" };

        options.Events = new OpenIdConnectEvents()
        {
            OnRedirectToIdentityProvider = OnRedirectToIdentityProvider,
            OnRemoteFailure = OnRemoteFailure,
            OnAuthorizationCodeReceived = OnAuthorizationCodeReceived
        };
    }

    public Task OnRedirectToIdentityProvider(RedirectContext context)
    {
        var defaultPolicy = AzureAdB2COptions.DefaultPolicy;
        if (context.Properties.Items.TryGetValue(AzureAdB2COptions.PolicyAuthenticationProperty, out var policy) &&
                !policy.Equals(defaultPolicy))
        {
            context.ProtocolMessage.Scope = OpenIdConnectScope.OpenIdProfile;
            context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
            context.ProtocolMessage.IssuerAddress = context.ProtocolMessage.IssuerAddress.ToLower().Replace(defaultPolicy.ToLower(), policy.ToLower());
                context.Properties.Items.Remove(AzureAdB2COptions.PolicyAuthenticationProperty);
        }
        else if (!string.IsNullOrEmpty(AzureAdB2COptions.ApiUrl))
        {
            context.ProtocolMessage.Scope += $" offline_access {AzureAdB2COptions.ApiScopes}";
            context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.CodeIdToken;
        }
        return Task.FromResult(0);
    }
}

我想必须在每行API的此行上设置范围,但这是管道的一部分。(否则,如果上面的OnRedirectToIdentityProvide方法的一部分)

 context.ProtocolMessage.Scope += $" offline_access {AzureAdB2COptions.ApiScopes}";

以下是api客户端配置

services.AddHttpClient<IApiClient1, ApiClient1>()
   .AddHttpMessageHandler<API1AccessTokenHandler>();

services.AddHttpClient<IApiClient2, ApiClient2>()
    .AddHttpMessageHandler<API2AccessTokenHandler>();

以下是用于静默获取API1令牌的代码。

 public class API1AccessTokenHandler : DelegatingHandler
 {
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        IConfidentialClientApplication publicClientApplication = null;
        try
        {
            // Retrieve the token with the specified scopes
            scopes = AzureAdB2COptions.ApiScopes.Split(' ');
            string signedInUserID = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;

            publicClientApplication =  ConfidentialClientApplicationBuilder.Create(AzureAdB2COptions.ClientId)
                                                                            .WithRedirectUri(AzureAdB2COptions.RedirectUri)
                                                                            .WithClientSecret(AzureAdB2COptions.ClientSecret)
                                                                            .WithB2CAuthority(AzureAdB2COptions.Authority)
                                                                            .Build();

            new MSALStaticCache(signedInUserID, _httpContextAccessor.HttpContext).EnablePersistence(publicClientApplication.UserTokenCache);

            var accounts = await publicClientApplication.GetAccountsAsync();
            result = await publicClientApplication.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
                .ExecuteAsync();
        }
        catch (MsalUiRequiredException ex)
        {
        }

        if (result.AccessToken== null)
        {
            throw new Exception();
        }

        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);

        return await base.SendAsync(request, cancellationToken);
    }
}

下面是用于静默获取API2,API2AccessTokenHandler的令牌的代码。

 public class API2AccessTokenHandler : DelegatingHandler
 {
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        IConfidentialClientApplication publicClientApplication = null;
        try
        {
            // Retrieve the token with the specified scopes
            scopes = Constants.Api2Scopes.Split(' ');
            string signedInUserID = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;

            publicClientApplication =  ConfidentialClientApplicationBuilder.Create(AzureAdB2COptions.ClientId)
                                                                            .WithRedirectUri(AzureAdB2COptions.RedirectUri)
                                                                            .WithClientSecret(AzureAdB2COptions.ClientSecret)
                                                                            .WithB2CAuthority(AzureAdB2COptions.Authority)
                                                                            .Build();

            new MSALStaticCache(signedInUserID, _httpContextAccessor.HttpContext).EnablePersistence(publicClientApplication.UserTokenCache);

            var accounts = await publicClientApplication.GetAccountsAsync();
            result = await publicClientApplication.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
                .ExecuteAsync();
        }
        catch (MsalUiRequiredException ex)
        {
        }

        if (result.AccessToken== null)
        {
            throw new Exception();
        }

        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);

        return await base.SendAsync(request, cancellationToken);
    }
}
  1. [在获取令牌时传递范围没有帮助。代币始终为null。
  • 该帐户始终具有Api1的范围,但没有Api2。
  • APi1的范围是从AzureB2COptions.ApiScope中添加的作为Startup.cs中ServiceCollection管道代码的一部分]
  • 我想在Api2的情况下单独调用Acquire令牌没有帮助,因为在Startup.cs中为Api1设置了范围。

    请提供您的宝贵建议以及代码示例。

    UPDATE:

    我正在寻找类似于为IPublicClientApplication.AcquireTokenInteractive设计的WithExtraScopeToConsent。我需要类似的扩展,以用于AcquireTokenByAuthorizationCode的ConfidentialClientApplicationBuilder
    cca.AcquireTokenByAuthorizationCode(AzureAdB2COptions.ApiScopes.Split(' '), code)
       .WithExtraScopeToConsent(additionalScopeForAPi2)
       .ExecuteAsync();
    

    我已经为WebApp和Api设置了身份验证/授权,并且可以正常工作。问题是当我必须引入其他的Api时(将从WebAPP中调用)。限制是...

    azure-active-directory asp.net-core-webapi openid azure-ad-b2c msal
    1个回答
    0
    投票

    是的,对于同一个api,我们可以有多个作用域,而不是来自不同Apis的多个作用域。

    © www.soinside.com 2019 - 2024. All rights reserved.