Quantcast
Channel: Active questions tagged blazor - Stack Overflow
Viewing all articles
Browse latest Browse all 4269

Blazor Web App (Server) EntraID authentication and downstream Api

$
0
0

I'm building a Blazor Web App (.NET 8) using Server interactivity and authenticating users via Microsoft Entra ID. Authentication works — I see the user is logged in, and their claims (oid, tid, etc.) are present.

My problems comes when I try to call the API from the blazor app:

var token = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes, user);

The error from the above call is

MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call

Verbose MSAL logs say:

Found 0 accounts in MSAL cacheNo refresh tokens or accounts foundToken cache could not satisfy silent request

This happens even though the user is authenticated.

My setup:

// Program.csbuilder.Services.AddBlazorServerAuthentication(builder.Configuration);builder.Services.AddBlazorServerAuthorization();// Add services to the container._ = builder.Services.AddRazorComponents()    .AddInteractiveServerComponents()    .AddMicrosoftIdentityConsentHandler();_ = builder.Services.AddServerSideBlazor()    .AddMicrosoftIdentityConsentHandler();_ = builder.Services.AddHttpContextAccessor();_ = builder.Services.AddCascadingAuthenticationState(); = builder.Services.AddTransient<AccessTokenHandler>();_ = builder.Services    .AddHttpClient("WebApiClient", client => client.BaseAddress = builder.Configuration.GetValue<Uri>("DownstreamApi:BaseUrl"))    .AddHttpMessageHandler<AccessTokenHandler>();
public static void AddBlazorServerAuthentication(this IServiceCollection services, IConfiguration configuration){    JwtSecurityTokenHandler.DefaultMapInboundClaims = false;    string[] scopes = configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(';');    _ = services.AddAuthentication(options =>        {            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;        })        .AddBearerToken()        .AddMicrosoftIdentityWebApp(options =>        {            configuration.Bind("AzureAd", options);            options.MaxAge = TimeSpan.FromHours(8);            options.AutomaticRefreshInterval = TimeSpan.FromMinutes(15);            options.Events.OnRemoteFailure = async context =>            {                try                {                    AuthenticationProperties authenticationProperties = new()                    {                        RedirectUri = "/"                    };                    authenticationProperties.Parameters["logoutHint"] = context?.HttpContext?.User?.Identity?.Name;                    await context.HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme, authenticationProperties);                    await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);                    Console.WriteLine("Authentication failed. Logging out user silently");                    context.HandleResponse();                    await Task.CompletedTask;                }                catch (Exception)                {                    throw;                }            };        })        .EnableTokenAcquisitionToCallDownstreamApi(scopes)        .AddInMemoryTokenCaches();}public static void AddBlazorServerAuthorization(this IServiceCollection services){    _ = services.AddAuthorization(options =>    {        options.FallbackPolicy = new AuthorizationPolicyBuilder()            .RequireAuthenticatedUser()            .Build();    });}
//AccessTokenHandlerpublic class AccessTokenHandler : DelegatingHandler{    private readonly ITokenAcquisition _tokenAcquisition;    private readonly IConfiguration _config;    private readonly IHttpContextAccessor _httpContextAccessor;    public AccessTokenHandler(ITokenAcquisition tokenAcquisition, IConfiguration config, IHttpContextAccessor httpContextAccessor)    {        _tokenAcquisition = tokenAcquisition;        _config = config;        _httpContextAccessor = httpContextAccessor;    }    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)    {        string[] scopes = _config["DownstreamApi:Scopes"]?.Split('', StringSplitOptions.RemoveEmptyEntries);        if (scopes?.Length > 0)        {            string token = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes,                user: _httpContextAccessor.HttpContext?.User,                authenticationScheme: OpenIdConnectDefaults.AuthenticationScheme);            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);        }        return await base.SendAsync(request, cancellationToken);    }}
"AzureAd": {"Instance": "https://login.microsoftonline.com/","ClientId": "xxxxxxx","ClientSecret": "xyxyxyxyxy","TenantId": "zzzzzzzzz"},"DownstreamApi": {"BaseUrl": "https://localhost:44385","Scopes": "api://yyyyyyyyyy/access_as_user"}

Viewing all articles
Browse latest Browse all 4269

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>