Blazor 服务器 - 从当前登录用户获取 AAD 访问令牌

Fal*_*noo 3 c# authentication blazor asp.net-core-3.1

我正在尝试从 Blazor 服务器网站检索 Azure AD 令牌,因此我可以将其添加为下游 API 服务中的授权标头。我能够在网站中设置 AAD 身份验证(效果非常好),但无法检索访问令牌,需要将其添加为下游 API 调用中的授权标头。

我在 .NET core 3.1 中使用 Blazor 服务器(所以不是 WebAssembly)

这是我当前的设置,但访问令牌始终为空,并且似乎无法修复它。任何帮助是极大的赞赏!

启动.cs

services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
            .AddAzureAD(options => Configuration.Bind("AzureAd", options))
            .AddCookie();

services.Configure<OpenIdConnectOptions>(AzureADDefaults.AuthenticationScheme, options =>
        {
            options.SaveTokens = true;
        });
Run Code Online (Sandbox Code Playgroud)

_Host.cshtml

我添加了一个代码块并尝试从 HttpContext 检索 accessToken。然后我可以使用 app.razor 中的 CascadingValue 对象将该值传播到我的控制器。但是,access_token 始终为空。“User.Identity.IsAuthenticated”为 true,因此它正在进入我的 if 语句。

@{
    string accessToken = null;
    if (User.Identity.IsAuthenticated)
    {
        accessToken = await HttpContext.GetTokenAsync("access_token");
        // accessToken is always empty :(
    }
}
Run Code Online (Sandbox Code Playgroud)

我在这里缺少什么?我能找到的有关此问题的大多数文章都是针对 Blazor WebAssembly

旁注:不确定这是否与此相关,但在我的控制器中,我能够获取 ClaimsPrincipal 对象。但我认为我无法从该对象获取不记名令牌(但认为这里值得一提)。

var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
Run Code Online (Sandbox Code Playgroud)

Fal*_*noo 5

我找到了这个问题的解决方案。我需要利用“Microsoft.Identity.Web”和“Microsoft.Identity.Web.UI”nuget 包才能检索令牌来调用 API。

我遵循以下示例中解释的解决方案: https: //github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/b07a9e06206f7274fdcadc34a50b8bebf9666fcf/4-WebApp-your-API/4-1-MyOrg #step-2-向您的 azure-active-directory-tenant 注册示例

我确实遇到了用户第一次登录时所需的同意机制的一些问题。这是通过添加“Microsoft 身份同意和条件访问处理程序服务”来解决的,其解释如下:https: //github.com/AzureAD /microsoft-identity-web/wiki/管理增量同意和条件访问

所以案件结案了:)

希望这对将来的人也有帮助。

为了完整起见,我添加了我在代码中所做的更改:

启动.cs

services.AddControllersWithViews(options =>
        {
            var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
            options.Filters.Add(new AuthorizeFilter(policy));
        }).AddMicrosoftIdentityUI();

        
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
             .AddMicrosoftIdentityWebApp(Configuration)
             .EnableTokenAcquisitionToCallDownstreamApi(new string[] { Configuration["DashboardAPI:ApiScope"] })
             .AddInMemoryTokenCaches();

services.AddServerSideBlazor()
            .AddMicrosoftIdentityConsentHandler();
Run Code Online (Sandbox Code Playgroud)

API控制器类

在我的构造函数中注入“ITokenAcquisition”类并将其分配给“_tokenAcquisition”var 每次调用 API 时,我都会先执行“PrepareAuthenticatedClient”方法

private async Task PrepareAuthenticatedClient()
    {
        var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { _dashboardAPIScope });
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    }
Run Code Online (Sandbox Code Playgroud)

在我调用 API 控制器的视图中,我添加了一个用于同意处理的 try-catch 块并注入同意处理程序

[Inject]
MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler { get; set; }

try
{
   // Call to my API controller
}
catch(Exception ex)
{
    ConsentHandler.HandleException(ex);
}
Run Code Online (Sandbox Code Playgroud)