0

I have a Blazor Server app where I managed to get the authentication with Azure AD to work, but I am unable to retrieve the Azure AD token. I have the following:

_Host.cshtml:

@{
Layout = null;


var tokens = new InitialApplicationState
{
    AccessToken = await HttpContext.GetTokenAsync("access_token"),
    RefreshToken = await HttpContext.GetTokenAsync("refresh_token")
};
}

<component type="typeof(App)" param-InitialState="tokens" render-mode="ServerPrerendered" />

Startup.cs:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.SaveTokens = true;

    options.Scope.Add("User.Read");
});
services.AddControllersWithViews()
    .AddMicrosoftIdentityUI();
services.AddAuthorization(options =>
{
    options.FallbackPolicy = options.DefaultPolicy;
});
services.AddScoped<TokenProvider>();

TokenProvider.cs:

public class TokenProvider
{
    public string AccessToken { get; set; }
    public string RefreshToken { get; set; }
}

InitialApplicationState.cs:

public class InitialApplicationState
{
    public string AccessToken { get; set; }
    public string RefreshToken { get; set; }
}

GradingApiService.cs:

public class GradingApiService : IGradingApiService
    {
        private readonly HttpClient _httpClient;
        private readonly IConfiguration _configuration;
        private readonly TokenProvider _tokenProvider;

    public GradingApiService(HttpClient httpClient, IConfiguration configuration, TokenProvider tokenProvider)
    {
        _httpClient = httpClient;
        _configuration = configuration;
        _tokenProvider = tokenProvider;
    }

    public async Task<Gradings> GetRiskAppetiteGradingByQuoteID()
    {
        try
        {
            var token = _tokenProvider.AccessToken;
            var request = new HttpRequestMessage(HttpMethod.Get,
                "https://grading-api-func-uks-tst.azurewebsites.net/api/Gradings/a0999a23-b275-4993-a959-6185cd769c0a");
            request.Headers.Add("Authorization", $"Bearer {token}");
            var response = await _httpClient.SendAsync(request);
            response.EnsureSuccessStatusCode();

            return await response.Content.ReadAsAsync<Gradings>();
        }
        catch
        {
            return new Gradings();
        }
    }
}

So whenever I retrieve the token I obtain a null value. Could you help me to understand what's wrong with this?

Many thanks in advance.

1 Answers1

0

Please check if references can be worked around and check if you have missed any configuration:

Make sure you check mark access token and id token

enter image description here

Try to Add client secret if you call api (only if needed)

enter image description here

If you can initialize component in app.razor page

In app.razor component

@using BlazorserverDemoApp.Data
@inject TokenProvider TokensProvider 


<CascadingAuthenticationState>
…
</CascadingAuthenticationState>

@code{
    [Parameter]
 public InitialApplicationState InitialState { get; set; }

    protected override Task OnInitializedAsync()
    {
        TokensProvider.AccessToken = InitialState.AccessToken;
        TokensProvider.RefreshToken = InitialState.RefreshToken;

        return base.OnInitializedAsync();
    }
}

please include services.AddRazorPages(); if it is made use before services.AddScoped();

Add the scope of your api, usually User.Read is the graph endpoint , so provide your scope given in portal for your api here. And add endpoints.MapControllers();

 public void ConfigureServices(IServiceCollection services)
    {
    
            services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
                .AddAzureAD(options => Configuration.Bind("AzureAd", options));

            services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
            {
                options.ResponseType = "code";
                options.SaveTokens = true;

                options.Scope.Add("offline_access");
                options.Scope.Add("<<scope>>");      //add the scope of your api 
                options.Resource = "<<resource>>";
            });

            services.AddHttpClient();                   //add this
            services.AddScoped<TokenProvider>();

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

            services.AddRazorPages();
            services.AddServerSideBlazor();             //this is important
                    .AddMicrosoftIdentityConsentHandler();   
            services.AddTransient<WeatherForecastService>();
    
  
    }

In configure method in startup.cs

  app.UseEndpoints(endpoints =>
        {
           // endpoints.MapRazorPages();
                    endpoints.MapControllers(); //add this
                    endpoints.MapBlazorHub();
                    endpoints.MapFallbackToPage("/_Host");
                });

Please See if you are missing tag

     <app>
        <component type="typeof(App)" param-InitialState="tokens" render-mode="ServerPrerendered" />
     </app>

On the api service side api or In fetch.razor, hope you had made use of IhttpClientFactory

       private readonly IConfiguration _configuration;
        private readonly TokenProvider _tokenProvider;

    public GradingApiService(IhttpClientFactory httpClient, IConfiguration configuration, TokenProvider tokenProvider)
    {
        _httpClient = httpClient.CreateClient();
        _configuration = configuration;
        _tokenProvider = tokenProvider;
    }

public HttpClient _httpClient { get; }

public async Task<Gradings> GetRiskAppetiteGradingByQuoteID()
    {

        Try
          { 
           var token = _tokenProvider.AccessToken;
            
           var request = new HttpRequestMessage(HttpMethod.Get, "your/api"); 

               request.Headers. Authorization=new 
             System.Net.Http.Headers.AuthenticationHeaderValue(“Bearer”, token);
            var response = await _httpClient.SendAsync(request);
            response.EnsureSuccessStatusCode();

             return await response.Content.ReadAsAsync<Gradings>();

               }
             Catch
                  {
                   //code..
                 }
         }
        

References:

  1. Quickstart| Microsoft Docs
  2. blazor-server-aad-sample(github.com)
  3. how-do-i-get-the-access-token | stack overflow
kavyaS
  • 8,026
  • 1
  • 7
  • 19