简体   繁体   中英

Blazor server - get AAD access token from current logged in user

I'm trying to retrieve an Azure AD token from my Blazor server website, so I can add this as an Authorization header in a downstream API service. I was able to setup AAD authentication in the website (which works perfectly fine), but I'm unable to retrieve an access token, which needs to be added as an authorization header in my downstream API calls.

I'm using Blazor server (so not WebAssembly) in .NET core 3.1

This is my current setup, but the access token is always null and can't seem to fix it. Any help is greatly appreciated!

Startup.cs

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

services.Configure<OpenIdConnectOptions>(AzureADDefaults.AuthenticationScheme, options =>
        {
            options.SaveTokens = true;
        });

_Host.cshtml

I added a code block and try to retrieve the accessToken from the HttpContext. Then I can propagate the value to my controllers using a CascadingValue object in my app.razor However, the access_token is always empty. The "User.Identity.IsAuthenticated" is true, so it is entering my if statement.

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

What am I missing here? Most of the articles I could find about this was for Blazor WebAssembly

Side note: Not sure if this is relevant for this, but in my controllers, I'm able to get the ClaimsPrincipal object. But I don't think I'm able to get a bearer token from that object (but thought it was worth mentioning here).

var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;

I found the solution to this issue. I needed to make use of the "Microsoft.Identity.Web" & "Microsoft.Identity.Web.UI" nuget packages to be able to retrieve the tokens to call the API.

I followed the solution explained in following sample: https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/b07a9e06206f7274fdcadc34a50b8bebf9666fcf/4-WebApp-your-API/4-1-MyOrg#step-2-register-the-sample-with-your-azure-active-directory-tenant

I did encounter some issues with the consent mechanism that is needed the first time a user logs in. This is solved by adding the "Microsoft Identity consent and conditional access handler service" which is explained here: https://github.com/AzureAD/microsoft-identity-web/wiki/Managing-incremental-consent-and-conditional-access

So case closed:)

Hope this helps someone in the future as well.

For completeness, I added the changes I did in my code:

Startup.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();

API controller class

Injected "ITokenAcquisition" class in my constructor and assigned it to the "_tokenAcquisition" var Each time I call the API, I execute the "PrepareAuthenticatedClient" method first

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

In the view where I call the API controller, I added a try-catch block for the consent handling and inject the consenthandler

[Inject]
MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler { get; set; }

try
{
   // Call to my API controller
}
catch(Exception ex)
{
    ConsentHandler.HandleException(ex);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM