2

The question has been raised a couple times on SO but none of these answers seems to work in my situation.

How do I get the access token from a blazor (server-side) web app?

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

Server side Blazor get bearer token

I have a Blazor application (Server-Side) running on .net6. It is connected to my Azure Active Directory. I can successfully authenticate myself to AAD and get the ClaimsPrincipal instance.

Here is a excerpt from the Program.cs file where it sets up the AAD:

[...]
builder.Services
    .AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    ;

builder.Services
    .Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
    {       
        options.SaveTokens = true;
    });
[...]

The configuration related to the AzureAd section contains the following information:

[...]
"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "XXX.com",
  "TenantId": "5dXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "ClientId": "26XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "CallbackPath": "/signin-oidc"
}
[...]

Based on the previous answers, once I get successfully authenticated, the following call in the file _Host.cshtml should have given me a valid access token but it is not the case in my situation:

Access token is null

Question

Why is the returned access token null in my Blazor application?

Update 1

I have seen answers using these extra 2 calls in the startup file:

    [...]
    builder.Services
.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)      
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(new string[] { "openid" })
.AddInMemoryTokenCaches()
;

I see no changes.

Kzryzstof
  • 7,688
  • 10
  • 61
  • 108
  • 1
    I am no expert, but the gist is that Blazor Server apps do not have an `HttpContext`. You can capture the `access_token` following the `InitialApplicationState` approaches in these links: https://learn.microsoft.com/en-us/power-apps/developer/data-platform/webapi/quick-start-blazor-server-app#prepare-the-app-to-use-azure-ad-tokens and https://learn.microsoft.com/en-us/aspnet/core/blazor/security/server/additional-scenarios?view=aspnetcore-6.0 – Steve Wong Jul 01 '22 at 13:30
  • @SteveWong Thanks for the info :) However, as you can see at step .5, it all relies on the ability to get the `access_token` via the `HttpContext`... – Kzryzstof Jul 01 '22 at 13:34
  • Yes, it does use the `HttpContext` because the `HttpContext` is available there. I don't know the full details, but I have followed this approach on my blazor server app. This will cache the `access_token` in your Blazor app once authenticated and allows you to retrieve the token w/o calling the `httpContext` again. – Steve Wong Jul 01 '22 at 13:39
  • 2
    There is a training sight course that I found helpful when doing this. If you search for "Securing Blazor Server-Side Applications", you should find it. I'm not sure if I'm allowed to post it here, so DM me if you can't find it. – Steve Wong Jul 01 '22 at 13:44

1 Answers1

4

If you want to request a token for your downstream API, TokenAcquisition is the way to go:

using Microsoft.Identity.Web;
//...
string token = await TokenAcquisition.GetAccessTokenForUserAsync(new string[] {apiScope});

Combined with IDownstreamWebApi you don't even have to save the token as a string, it will get assigned when the api is called:

protected readonly IDownstreamWebApi _webApi; // Injected
protected readonly ITokenAcquisition TokenAcquisition; // Injected
// ...
public async Task CallApi(){
     await TokenAcquisition.GetAccessTokenForUserAsync(new string[] {apiScope});
     var response = await _webApi.CallWebApiForUserAsync("apiName", options => ... );
}

And appsettings structure:

"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "<AD domain>",
"TenantId": "<TenantId>",
"ClientId": "<ClientId of this registered app>",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-callback-oidc",

"ClientSecret": "<Secret of registered API>"
},
"API": {
"APIScope": "api://<APIAppID>/access_as_user",
"Scopes": "api://<APIAppID/access_as_user",
"BaseUrl": "https://localhost:7003/"
}
Robin GM
  • 185
  • 1
  • 8
  • This section "Web app that calls an API" is mainly what I used to make this work: https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-call-api-app-configuration?tabs=aspnetcore – Robin GM Jul 01 '22 at 14:22
  • Also useful, if you are getting consent issues with B2C tenants: https://github.com/AzureAD/microsoft-identity-web/wiki/Managing-incremental-consent-and-conditional-access#in-blazor-server – Robin GM Jul 04 '22 at 07:45
  • This answer has been very helpful. Thank you! I will mark it as accepted answer and I will most likely add another (not-accepted) answer with the full detail of what has been working for me for future reference once it is (fully) working on my side :) – Kzryzstof Jul 06 '22 at 12:32