Azure B2C:Asp.Net Web API 调用 Asp.Net Web.API

Joh*_*ars 0 c# asp.net-web-api azure-ad-b2c azure-ad-msal

我有两个 Web API 服务(源和目标)。每个都使用客户端密钥在 Azure B2C 中注册为 AD 应用程序。

我正在尝试调用从源服务到目标服务 API 的 gRPC API 调用,但是当我尝试进行调用时,出现 gRPC 异常,表明源未经身份验证。

注意:当我从目标 WebAPI 中删除范围策略和授权属性时,一切正常,但当然这是不安全的。

在源服务启动中我有以下内容......

注意:源和目标的配置设置都是正确的。我已经三次检查所有配置设置是否正确。

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddMicrosoftIdentityWebApi(options =>
    {
        Configuration.Bind("AzureAdB2C", options);
        options.TokenValidationParameters.NameClaimType = "name";
    },
    options =>
    {
        Configuration.Bind("AzureAdB2C", options);
    });
Run Code Online (Sandbox Code Playgroud)

我不确定是否需要,但我还在源 API 的启动中包含了以下逻辑。

注意:我现在已经对所需的范围进行了硬编码。

services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAdB2C")
    .EnableTokenAcquisitionToCallDownstreamApi(new string[] { "https://nextwaredev.onmicrosoft.com/nextware.productportal.sharedservices.api/sharedservices_readaccess", "https://nextwaredev.onmicrosoft.com/nextware.productportal.sharedservices.api/sharedservices_readwriteaccess" })
    .AddInMemoryTokenCaches();
Run Code Online (Sandbox Code Playgroud)

目标服务具有正确的策略,并且两个服务都配置为使用授权和身份验证。

app.UseAuthentication();
app.UseAuthorization();
Run Code Online (Sandbox Code Playgroud)

我已经根据上面的相同范围添加了源所需的 API 权限。

在此输入图像描述

当我尝试调用目标 API 上的方法时,我收到一个 gRPC 异常,表明源未经身份验证。

因此,我想也许我需要传递应用程序访问令牌,但是当我尝试获取 JWT 访问令牌时(以下示例 调用 tokenAcquisition.GetAccessTokenForAppAsync),我收到另一个异常“AADB2C90086:不支持提供的 grant_type [client_credentials]。 ”

注意:我有一个 Blazor 应用程序,调用相同的安全 API 时没有任何问题。不过,我调用 GetAccessTokenForUserAsync... ,它返回正确的令牌,并具有调用相同下游源或目标 Web api 服务所需的应用范围。

我是否需要在启动时包含 AddMicrosoftIdentityWebAppAuthentication?我还做错了什么?

谢谢您的帮助!干杯

Joh*_*ars 6

我无法直接从 Azure B2C 使用Microsoft Identity Web Api获取客户端凭据流,因此我按照此链接遵循了 @Hossam Barakat 的出色指导

我以两种身份验证类型结束,一种允许使用Jwt Bearer进行 Web API => Web API 通信,另一种由 Web APP 使用 => 利用 Jwt Bearer 的 Web API

  • 项目清单

微软身份网络 API ...

        var authority = @"https://login.microsoftonline.com/" + Configuration["AzureAdB2C:TenantId"];
        var validIssuer = @"https://login.microsoftonline.com/" + Configuration["AzureAdB2C:TenantId"] + @"/v2.0";
        var audience = Configuration["AzureAdB2C:ClientId"];
        var validAudience = Configuration["AzureAdB2C:ClientId"];

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)

        .AddJwtBearer("APIToAPI", options =>
           {
               options.Audience = audience;
               options.Authority = authority;
               options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
               {
                   ValidAudience = validAudience,
                   ValidIssuer = validIssuer
               };
           }
        )

        .AddMicrosoftIdentityWebApi(options =>
               {
                   Configuration.Bind("AzureAdB2C", options);
                   options.TokenValidationParameters.NameClaimType = "name";
               }
               , options =>
                {
                    Configuration.Bind("AzureAdB2C", options);
                },
                jwtBearerScheme: "AppToAPI"
        ); 
Run Code Online (Sandbox Code Playgroud)

然后为了启用这两个方案,我必须添加以下内容......

        services.AddAuthorization(options =>
        {
            options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().AddAuthenticationSchemes("APIToAPI", "APPToAPI").Build();
        }
Run Code Online (Sandbox Code Playgroud)

我还在控制器操作中添加了以下授权属性...

 [Authorize(AuthenticationSchemes = "APIToAPI, APPToAPI")]
Run Code Online (Sandbox Code Playgroud)

每种类型的令牌创建都是不同的。

这就是我用来获取 Web 应用程序中当前经过身份验证的用户的访问令牌、调用 API 的方法...

 private static string GetAccessTokenForUser(List<string> scopes)
    {
        if (scopes != null)
        {
            var container = ContainerLocator.Current.Container;

            var configuration = container.Resolve<IConfiguration>();

            var httpContextAccessor = container.Resolve<IHttpContextAccessor>();
            if (httpContextAccessor?.HttpContext?.User?.Identity?.IsAuthenticated ?? false)
            {
                var tokenService = httpContextAccessor.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>();
                try
                {
                    return Task.Run(() => tokenService.GetAccessTokenForUserAsync(scopes)).Result;
                }
                catch (System.Exception ex)
                {
                    Log.Error(ex, "error while loading token for current user, try to login again");
                }
            }
        }
        return null;

    }
Run Code Online (Sandbox Code Playgroud)

当 Web API 调用 Web API 时,我用它来获取访问令牌...

public static async Task<TokenResponse> GetAccessTokenAsync(string scope)
    {
        var client = new HttpClient();
        var container = ContainerLocator.Current.Container;
        var configuration = container.Resolve<IConfiguration>();

        var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
        {
            Address = @"https://login.microsoftonline.com/" + configuration["AzureAdB2C:Domain"] +@"/oauth2/v2.0/token",
            ClientId = configuration["AzureAdB2C:ClientId"],
            ClientSecret = configuration["AzureAdB2C:ClientSecret"],
            Scope = scope
        });

        if (tokenResponse.IsError)
        {
            return null;
        }

        return tokenResponse;
    }
Run Code Online (Sandbox Code Playgroud)

如果获取了访问令牌,则只需在调用 gRPC 和/或 REST API 方法之前将其添加到请求标头中即可。

我希望这对其他人有帮助。