为什么在调用受 AAD 保护的 AspNet Core API 时出现 Http 401。我们的 WebApp、Web API 和 AD 组织

Why Http 401 when calling AspNet Core API secured with AAD. Our WebApp, Web API and AD Org

我一直在关注此 Azure 示例以保护 AspNet WebApp to a WebApi we own and secure with our own Azure Active Directory organisation

我们有一个现有的 AspNet 站点,该站点已经使用 Azure Active Directory 进行保护,所以我实际上只是想插入与示例的 TodoListService 等效的内容。

示例使用 MSAL,因此我们已将网站移至使用它。 WebSite Startup中的ConfigureServices方法是

public void ConfigureServices(IServiceCollection services)
{
    services.AddOptions();

    services.AddMicrosoftIdentityWebAppAuthentication(this.Configuration)
            .EnableTokenAcquisitionToCallDownstreamApi(new string[] { "https://ourdomain/app.our-service/user_impersonation" })
            .AddDownstreamWebApi("OurService", this.Configuration.GetSection("OurServiceApi"))
            .AddInMemoryTokenCaches();

    // Add Apis
    services.AddOurService(this.Configuration);

    services.AddAuthorization(options =>
    {
        options.DefaultPolicy = new AuthorizationPolicyBuilder()
                        .RequireAuthenticatedUser()
                        .RequireClaim(
                                System.Security.Claims.ClaimsIdentity.DefaultRoleClaimType,
                                "Team_Administrators")
                        .Build();
    });

    services.AddRazorPages()
            .AddMicrosoftIdentityUI();

    services.AddServerSideBlazor()
            .AddMicrosoftIdentityConsentHandler();
}

示例表示范围需要采用 api:///scope_name 格式,但如您所见,我们的范围名称是 AD 租户领域加上范围。尝试使用客户端 ID 导致此错误

OpenIdConnectProtocolException: Message contains error: 'invalid_resource', error_description: 'AADSTS500011: The resource principal named api://4f3ca2ab-d7dc-401a-a514-37744ab3555f was not found in the tenant named 1300f116-f07e-427f-b2ef-c66643994577. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.

我们可以使用域名格式来对网站进行身份验证。

我们可以按照示例成功调用PrepareAuthenticatedClient方法获取accessToken

private async Task PrepareAuthenticatedClient()
{
    var accessToken = await this.tokenAcquisition.GetAccessTokenForUserAsync(new[] { this.clinicsSettings.Scopes });
    System.Diagnostics.Debug.WriteLine($"access token-{accessToken}");
    this.httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    this.httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}

调用 tokenAcquisition.GetAccessTokenForUserAsync 导致在浏览器中向 AAD 发出重定向请求,然后用户被重定向回来。随后调用 get GetAccessTokenForUserAsync 全部成功,没有重定向。

我们有 AccessToken,我们尝试调用我们的 Web 服务。该调用被 Web 服务拒绝为 401 未授权。具体回复为

{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers: {
Transfer-Encoding: chunked Server: Microsoft-IIS/10.0
WWW-Authenticate: Bearer error="invalid_token", error_description="The audience 'https://OURDOMAIN.co.uk/app.our-service' is invalid" X-Powered-By: ASP.NET Date: Wed, 21 Oct 2020 17:16:01 GMT }}

Web 服务的启动 class 看起来像这样

public void ConfigureServices(IServiceCollection services)
{
    services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
    services.AddControllers();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        // Since IdentityModel version 5.2.1 (or since Microsoft.AspNetCore.Authentication.JwtBearer version 2.2.0),
        // PII hiding in log files is enabled by default for GDPR concerns.
        // For debugging/development purposes, one can enable additional detail in exceptions by setting IdentityModelEventSource.ShowPII to true.
        Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseAuthentication();
    app.UseRouting();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

任何人都可以帮助解释为什么 accessToken 不足以成功调用网络服务吗?

对于 v2.0 访问令牌,范围的格式为“api://{ClientIdOfWebApi}/scope”,除非您的租户有经过验证的域,在这种情况下,范围格式为“yourdomain/scope"

在应用注册入口查看AppId URI即可知道

根据令牌的版本,Microsoft.Identity.Web检查上面形状的受众,但是当你有你的域时,你需要添加:

“观众”:“您的域”

在您的网站 appsettings.json 中 API。

参见:https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-app-configuration#case-where-you-used-a-custom-app-id-uri-for-your-web-api