Skip to content

AdditionalScopesToConsent seemingly being ignored #61610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 task done
JSparshottO2 opened this issue Apr 22, 2025 · 0 comments
Open
1 task done

AdditionalScopesToConsent seemingly being ignored #61610

JSparshottO2 opened this issue Apr 22, 2025 · 0 comments

Comments

@JSparshottO2
Copy link

JSparshottO2 commented Apr 22, 2025

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

I believe this is actually a bug within the MSAL library. I have raised an issue on that related repo but it's pretty silent there. After picking this issue back up today I've been looking through the codebase and AddMsalAuthentication, as a call, actually lives in this repo so On the off-chance the issue is here (maybe it's not passing through the config to the MSAL library), I'm going to raise it here as well. At the very least I hope to drum up some attention on the MSAL issue.

The issue on the MSAL board accurately reproduces it, copied from that issue for ease:

I am in a Blazor Wasm application. The End-State that I'm trying to get to is on application login, scopes are provided such that 1st and 3rd party resources are accessible using the single login without any future redirecting. In this example I am trying to access a 1st party API and a 3rd party Azure resource (a storage account).
I am using a custom TokenCredential implementation that uses the IAccessTokenProvider to request a token with the Azure service scope, and creates an Azure.Core.AccessToken that can be used with the Azure.Storage.Blobs library.

I've tried various combinations of DefaultAccessTokenScopes and AdditionalScopesToConsent. AdditionalScopesToConsent doesn't seem to do anything in terms of "prepping" the scopes for future use. I'm not even sure it's purpose really, it seems to do nothing.

Depending on which scope is set as the DefaultAccessTokenScopes will dictate which service works.

I'm happy to provide any additional context as needed. I've spent the last 2 days both trying to work out the combinations based upon my knowledge, and using the internet to try and work out how I can use the SSO into the blazor app to work with multiple external resources.

N.B. All external resources use the same Microsoft Entra source. Users are configured in the IAM of the related resources.

Relevant code snippets

Program.cs

//unrelated code...
builder.Services.AddScoped<TokenCredential, WasmTokenCredential>();
builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
    options.ProviderOptions.DefaultAccessTokenScopes.Add("api://<API_resource_id>/access_as_user");
    options.ProviderOptions.AdditionalScopesToConsent.Add("https://storage.azure.com/.default");
    options.UserOptions.RoleClaim = "roles";
});
//more unrelated code...

WasmTokenCredential.cs

using Azure.Core;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

using AzureAccessToken = Azure.Core.AccessToken;
using WasmAccessToken = Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccessToken;

namespace TokenExample;

public class WasmTokenCredential : TokenCredential
{
    private readonly IAccessTokenProvider _accessTokenProvider;

    public WasmTokenCredential(IAccessTokenProvider accessTokenProvider)
    {
        _accessTokenProvider = accessTokenProvider;
    }

    public override AzureAccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
        => throw new NotSupportedException("Cannot use synchronous token acquisition in a WebAssembly environment.");

    public override async ValueTask<AzureAccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
        => FromWasmResult(await _accessTokenProvider.RequestAccessToken(new()
        {
            Scopes = [.. requestContext.Scopes]
        }));

    private AzureAccessToken FromWasmResult(AccessTokenResult tokenResult)
    {
        if (tokenResult.TryGetToken(out WasmAccessToken? accessToken) == false)
        {
            throw new InvalidOperationException("Failed to obtain an access token.");
        }
        return new AzureAccessToken(accessToken.Value, accessToken.Expires);
    }
}

BlobService.cs

using Azure.Core;
using Azure.Storage.Blobs;
using Microsoft.Extensions.Options;

namespace TokenExample;

public class BlobService
{
    private readonly BlobServiceClient _blobServiceClient;

    public BlobService(IOptions<BlobOptions> configOption, TokenCredential credentials)
    {
        _blobServiceClient = new BlobServiceClient(new Uri(configOption.Value.Location), credentials);
    }

    public async Task<bool> HasContainer(string container)
        => (await _blobServiceClient.GetBlobContainerClient(container).ExistsAsync()).Value;
}

appsettings.json

{
  "AzureAd": {
    "ClientId": "<CLIENT_ID>",
    "Authority": "https://login.microsoftonline.com/<TENANT_ID>",
    "ValidateAuthority": true
  },
  "Blob": {
    "Location": "https://<BLOB_NAME>.blob.core.windows.net"
  }
}

Expected Behavior

On initial login in blazor WebAssembly, no further login prompts are required for accessing other authorized Azure Resources. (in this example, A blob storage)

.NET Version

9.0.203

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label Apr 22, 2025
@javiercn javiercn added area-security and removed area-blazor Includes: Blazor, Razor Components labels Apr 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants