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

Handling downstream API in a Blazor web app that calls an ASP.NET Web API

$
0
0

Preface my ASP.NET Web API DOES work with token authentication tested in postman, and my web app is able to authenticate users, the issue comes when I try to downstream.

I have a Blazor web app that needs to handle passing user auth to my ASP.NET Web API - hosted on an IIS server. Currently the log in call to Microsoft is functioning - user is able to login and authenticate.

Configured as such:

builder    .Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)    .AddMicrosoftIdentityWebApp(builder.Configuration, "AzureAd")    .EnableTokenAcquisitionToCallDownstreamApi(new string[] { "{Scopes defined in my web api}" })    .AddDownstreamApi("FriendlyName", builder.Configuration.GetSection("ElysiumApi"))    .AddInMemoryTokenCaches();        builder.Services.AddInMemoryTokenCaches();        builder            .Services.AddRazorPages()            .AddMvcOptions(options =>            {                AuthorizationPolicy policy = new AuthorizationPolicyBuilder()                    .RequireAuthenticatedUser()                    .Build();                options.Filters.Add(new AuthorizeFilter(policy));            })            .AddMicrosoftIdentityUI();        builder.Services.AddRazorComponents().AddInteractiveServerComponents();        builder.Services.AddServerSideBlazor().AddMicrosoftIdentityConsentHandler();...        builder.Services.AddHttpClient("ElysiumClient",            client =>                client.BaseAddress = new Uri(                    builder.Configuration.GetValue<string>("ElysiumApi:BaseUrl")!                )        );        builder            .Services.AddHttpClient("MyWebApp")            .AddHeaderPropagation(o => o.Headers.Add("Cookie"));        builder.Services.AddScoped<ElysiumApiService>();

The arguments for

AddMicrosoftIdentityWebApp(builder.Configuration, "AzureAd")

is the app configuration and registration from Entra/Azure AD. In my appsettings.json I have:

"AzureAd": {"ClientId": "My web app client Id","Instance": "https://login.microsoft.com","TenantID": "my tenant Id","ClientSecret": "my secret","CallbackPath": "/signin-oidc","SignedOutCallbackPath": "/signout-oidc"}

In my app registration for the Web API I have configured the following scopes

Scopes defined.

I also configured my web app as an authorized client application

My web app configured as an authorized client application

Then as I've defined it in my downstream Web API argument is as such in my appsettings.json

"ElysiumApi": {"BaseUrl": "my local web server","ScopesForDownStream": [ "Elysium.Read.All" ]}

Finally the service I configured to call the Web API:

public partial class ElysiumApiService{    private readonly HttpClient HttpClient;    private readonly NavigationManager NavigationManager;    readonly ITokenAcquisition tokenAcquisition;    private string[] scopes = ["Elysium.Read.All"];    public ElysiumApiService(        NavigationManager navigationManager,        IHttpClientFactory httpClientFactory,        ITokenAcquisition acquisition    )    {        this.HttpClient = httpClientFactory.CreateClient("ElysiumClient");        this.NavigationManager = navigationManager;        this.tokenAcquisition = acquisition;    }    public async Task<IQueryable<PantheonModel>>? GetPantheons()    {        List<PantheonModel> pList = null;        try        {            string accessToken = await this.tokenAcquisition.GetAccessTokenForUserAsync(this.scopes);            this.HttpClient.DefaultRequestHeaders.Add("Authorization", accessToken);            pList = await this.HttpClient.GetFromJsonAsync<List<PantheonModel>>($"/api/endpoints/pantheon") ?? throw new Exception();        }        catch (MicrosoftIdentityWebChallengeUserException ex)        {            await this.tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeaderAsync(scopes, ex.MsalUiRequiredException);        }        catch (MsalUiRequiredException ex)        {            await tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeaderAsync(scopes, ex);        }        catch        {            throw;        }        return pList.AsQueryable() ?? throw new Exception();    }}

I configured the catches as suggested from this articlehttps://github.com/AzureAD/microsoft-identity-web/wiki/Managing-incremental-consent-and-conditional-access

I get the following error with the catches configured

ArgumentNullException: Value cannot be null. (Parameter 'source')System.ArgumentNullException.Throw(string paramName)System.ArgumentNullException.ThrowIfNull(object argument, string paramName)System.Linq.Queryable.AsQueryable<TElement>(IEnumerable<TElement> source)ElysiumApiService.GetPantheons() in ElysiumApiService.cs-    }        catch (MicrosoftIdentityWebChallengeUserException ex)        {            await this.tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeaderAsync(                scopes,                ex.MsalUiRequiredException            );    }        catch (MsalUiRequiredException ex)        {            await tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeaderAsync(scopes, ex);        }        catch        {            throw;        }        return pList.AsQueryable() ?? throw new Exception();    }

Without the catches in my function I get the below - the article is how I got to the one I referenced above:

MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent.

Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable<string> scopes, string authenticationScheme, string tenantId, string userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions)Stack Query Cookies Headers RoutingMsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call.Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync(CancellationToken cancellationToken)Microsoft.Identity.Client.Internal.Requests.RequestBase+<>c__DisplayClass11_1+<<RunAsync>b__1>d.MoveNext()Microsoft.Identity.Client.Utils.StopwatchService.MeasureCodeBlockAsync(Func<Task> codeBlock)Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)Microsoft.Identity.Client.ApiConfig.Executors.ClientApplicationBaseExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenSilentParameters silentParameters, CancellationToken cancellationToken)Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForWebAppWithAccountFromCacheAsync(IConfidentialClientApplication application, ClaimsPrincipal claimsPrincipal, IEnumerable<string> scopes, string tenantId, MergedOptions mergedOptions, string userFlow, TokenAcquisitionOptions tokenAcquisitionOptions)System.Threading.Tasks.ValueTask<TResult>.get_Result()Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable<string> scopes, string authenticationScheme, string tenantId, string userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions)Raw exception MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent.Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable<string> scopes, string authenticationScheme, string tenantId, string userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions)Microsoft.Identity.Web.TokenAcquisition.GetAccessTokenForUserAsync(IEnumerable<string> scopes, string authenticationScheme, string tenantId, string userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions)AgoraClient.Services.ElysiumApiService.GetPantheons() in ElysiumApiService.cs-    public async Task<IQueryable<PantheonModel>>? GetPantheons()    {        //List<PantheonModel> pList = null;        //try        //{        string accessToken = await this.tokenAcquisition.GetAccessTokenForUserAsync(this.scopes);        this.HttpClient.DefaultRequestHeaders.Add("Authorization", accessToken);        List<PantheonModel> pList =            await this.HttpClient.GetFromJsonAsync<List<PantheonModel>>($"{route}")            ?? throw new Exception();        return pList.AsQueryable();        //}AgoraClient.Components.Pages.Pantheon.OnInitializedAsync() in Pantheon.razor-    ElysiumApiService ElysiumApiService { get; set; } = null!;    IQueryable<PantheonModel>? Pantheons = null;    protected override async Task OnInitializedAsync()    {        Pantheons = await this.ElysiumApiService.GetPantheons();    }}

Viewing all articles
Browse latest Browse all 4839

Trending Articles



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