【问题标题】:Azure B2C : Asp.Net Web API calling Asp.Net Web.APIAzure B2C:调用 Asp.Net Web.API 的 Asp.Net Web API
【发布时间】:2020-12-02 19:11:14
【问题描述】:

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

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

注意:当我从目标 WebAPI 中删除 Scope Policy 和 Authorized 属性时,一切正常,但这当然不安全。

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

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

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

我不确定它是否需要,但我还在源 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();

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

app.UseAuthentication();
app.UseAuthorization();

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

当我尝试在目标 API 上调用方法时,我返回一个 gRPC 异常,指出源未经身份验证。

然后我想也许我需要传递一个应用程序访问令牌但是当我尝试获取 JWT 访问令牌时(在 this example 调用 tokenAcquisition.GetAccessTokenForAppAsync 之后)我得到另一个异常“AADB2C90086:提供的grant_type [client_credentials]不支持。”

注意:我有一个 Blazor 应用程序调用相同的安全 API 没有问题。但是在那里我调用 GetAccessTokenForUserAsync... 它返回正确的令牌,其中包含调用相同下游源或目标 Web api 服务所需的应用范围。

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

感谢您的帮助! 干杯

【问题讨论】:

  • 能否请您告诉我您如何调用目标 API

标签: c# asp.net-web-api azure-ad-b2c msal


【解决方案1】:

我无法直接从 Azure B2C 使用 Microsoft Identity Web Api 让客户端凭据流正常工作,因此我遵循了 @Hossam Barakat 的这一出色指导,每 this link

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

的 Web API
  • 列表项

Microsoft Identity Web 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"
        ); 

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

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

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

 [Authorize(AuthenticationSchemes = "APIToAPI, APPToAPI")]

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

这是我用来获取 Web APP 中当前经过身份验证的用户的访问令牌,调用 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;

    }

当它是一个调用 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;
    }

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

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

【讨论】:

  • 嗨。您是否找到了在访问令牌中取回声明的方法?
  • 你有这个回购吗?
猜你喜欢
  • 1970-01-01
  • 2016-04-18
  • 1970-01-01
  • 2017-01-27
  • 1970-01-01
  • 2017-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多