There is no real solution to this issue, only various workarounds until net7 comes around. In net7 we should once again be able to request scopes from multiple sources in one call according to https://github.com/dotnet/aspnetcore/pull/43954
I have a .net5 blazor webassembly application, set up with msal auth for azure.
services.AddMsalAuthentication(options =>
{
configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("api://xxxxx/API.Access"); // API
options.ProviderOptions.Cache.CacheLocation = "localStorage";
});
This works fine.
Additionally I need to get access to Microsoft graph. I've done this with the graph sdk and provided an authentication handler for the graph sdk
public class GraphAuthenticationProvider : IAuthenticationProvider
{
private readonly NavigationManager _navigationManager;
public GraphAuthenticationProvider(IAccessTokenProvider tokenProvider, NavigationManager navigationManager)
{
TokenProvider = tokenProvider;
_navigationManager = navigationManager;
}
public IAccessTokenProvider TokenProvider { get; }
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
string[] scopes = new[] { "https://graph.microsoft.com/Mail.ReadWrite", "https://graph.microsoft.com/Mail.Send" };
var result = await TokenProvider.RequestAccessToken(
new AccessTokenRequestOptions()
{
Scopes = scopes,
ReturnUrl = _navigationManager.Uri
});
if (result.TryGetToken(out var token))
{
request.Headers.Authorization ??= new AuthenticationHeaderValue(
"Bearer", token.Value);
}
else
{
_navigationManager.NavigateTo(result.RedirectUrl);
}
}
}
This seems to be the way to do it according to the documentation I could find, though it seems to assume that you're trying to get additional scopes on the same resource. https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-5.0#request-additional-access-tokens
There are two issues with this:
- I can't find any way to do this with a popup, which means I am forced to navigate to a redirect which results in losing the program state. This can be worked around, but it seems that the popup version of consent should be possible?
- When the authentication finishes and I get back to my own application, it tries to create a token for all scopes, both my api and the graph, which obviously fails with a more than one resource error, moving me to the login error page. Even though it actually did correctly get both tokens and I can simply navigate away from the error page and access both my api and graph.
I've been unable to find any documentation for blazor webassembly msal with multiple resources. Can someone explain what I'm doing wrong or point me to the correct documentation?
public class GraphAuthorizationMessageHandler : AuthorizationMessageHandler
{
public GraphAuthorizationMessageHandler(IAccessTokenProvider provider, NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(authorizedUrls: new[] { "https://graph.microsoft.com/" }, scopes: new[] { "https://graph.microsoft.com/Mail.ReadWrite", "https://graph.microsoft.com/Mail.Send" });
}
}
services.AddScoped<CustomAuthorizationMessageHandler>();
services.AddScoped<GraphAuthenticationProvider>();
services.AddScoped<GraphHttpProvider>();
services.AddScoped<GraphAuthorizationMessageHandler>();
services.AddHttpClient<GraphHttpProvider>(
client => client.BaseAddress = new Uri("https://graph.microsoft.com"))
.AddHttpMessageHandler<GraphAuthorizationMessageHandler>();
services.AddScoped(sp =>
new GraphServiceClient(
sp.GetRequiredService<GraphAuthenticationProvider>(),
sp.GetRequiredService<GraphHttpProvider>())
);
Edit: Related github issue https://github.com/dotnet/aspnetcore/issues/33241 - Seems that for now this functionality is bugged.