0

I've got a Blazor WASM app with .NET 7 and I'm using Azure AD with a public (multi-tenant) app registration. I'm trying to request a scope so that I can authenticate against an API. But it doesn't request include the requested scopes during authentication.

Here is my appsettings.json:

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/common",
    "ClientId": "[My Balzor app client ID]",
    "ValidateAuthority": true,
    "Scopes": "openid email profile offline_access api://[My API app client ID]/user_read"
  }
}

In Program.cs I'm configuring this here:

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
});

I've also tried adding it explicitly:

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
    options.ProviderOptions.AdditionalScopesToConsent.Add("api://[My API app clinet ID]/user_read");
});

However, these scopes are never requested from AAD. The URI only ever includes openid, profile, and offline_access.

Screenshot of MSAL generated URI that only includes openid, profile, and offline_access scopes, not the additional scopes I am requesting.

Why is my additional scope not requested? How do I fix this?

Matt G
  • 444
  • 5
  • 23
  • in your wasm apps registration on the portal do you have all the "Other permissions granted for ... " added and consented? – Brian Parker Apr 24 '23 at 04:21
  • @BrianParker yes I do. Although I would expect that to give an error _if_ the request contained these scopes and _if_ I hadn't done so. But it doesn't get that far. But yes I have anyway. – Matt G Apr 24 '23 at 05:30

3 Answers3

1

Your Scopes property is being ignored, because you are binding your "AzureAd" appsettings section to options.ProviderOptions.Authentication.

And options.ProviderOptions.Authentication is of type Microsoft.Authentication.WebAssembly.Msal.MsalAuthenticationOptions which does not include the property DefaultAccessTokenScopes or Scopes.

The DefaultAccessTokenScopes property (of type IList string) is a sibling to the Authentication property on the Microsoft.Authentication.WebAssembly.Msal.Models.MsalProviderOptions class.

Thus if you restructure your AzureAd section slightly to take into account the MsalProviderOptions structure, it should all bind correctly without having to call options.ProviderOptions.DefaultAccessTokenScopes.Add() manually in the Program.cs.

{
  "AzureAd": {
    "Authentication": {
      "Authority": "https://login.microsoftonline.com/common",
      "ClientId": "[My Balzor app client ID]",
      "ValidateAuthority": "true"
    },
    "DefaultAccessTokenScopes": ["api://[My API app client ID]/user_read", "..."]
  }
}

And then your Program.cs can be updated as follows:

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions);
});
LinFelix
  • 1,026
  • 1
  • 13
  • 23
Shane
  • 26
  • 2
  • Awesome thanks! I'll switch my accepted answer to this. – Matt G Jun 02 '23 at 21:37
  • @MattG have you tried this? As far as I'm aware you can't add scopes from multiple different APIs into DefaultAccessTokenScopes and you will get an exception. – Dan Harris Jun 22 '23 at 10:31
  • @DanHarris I haven't tried this yet, I'm still using my original posted answer, and it works fine. When I need to access the Graph API, I simply request a new token with the specified Graph scope using the IAccessTokenProvider. And this works fine. – Matt G Jun 22 '23 at 22:22
0

I created an Azure AD Application and granted Admin consent for the API permissions like below:

enter image description here

Make sure to grant Admin consent to all the API permissions.

I generated access token via Postman using the below parameters:

https://login.microsoftonline.com/TenantID/oauth2/v2.0/authorize?
&client_id=ClientID
&response_type=code
&redirect_uri=https://jwt.ms
&response_mode=query
&scope=openid email profile offline_access api://ab0d71e6-1263-48cf-967b-ef0ce7422639/user_read
&state=12345

enter image description here

When I decoded the token, I got the scopes same as you:

enter image description here

Now, I generated access token using the scope as below:

scope=api://ClientID/user_read openid email profile offline_access

enter image description here

When I decoded the token, I got the API scope successfully like below:

enter image description here

To resolve the error, modify appsettings.json and try:

{
  "AzureAd": {
    "Authority": "https://login.microsoftonline.com/common",
    "ClientId": "[My Balzor app client ID]",
    "ValidateAuthority": true,
    "Scopes": "api://[My API app client ID]/user_read openid email profile offline_access"
  }
}

The user will get the consent screen:

enter image description here

If still the issue persists, try creating class for each Api and generate access token. Refer the below link:

How to handle tokens for multiple resources in blazor webassembly MSAL - Stack Overflow by Greg Grater.

Rukmini
  • 6,015
  • 2
  • 4
  • 14
  • Thanks, this doesn't answer the question. The question is about MSAL in a Blazor app not adding the scopes I am specifying to the authentication request. Also the linked question doesn't address the issue, everything suggested in the linked question is already covered in my post. Not to mention I am only trying to get a token for one resource. – Matt G Apr 24 '23 at 07:54
  • Assuming that you want the scope as `API://ClientID` when decoding the token? – Rukmini Apr 24 '23 at 07:58
  • That's not important. I want the audience on the token to be the API client ID. The app registration is fine, I can get the expected token if I use postman or modify the URL. This is a Blazor MSAL question. – Matt G Apr 24 '23 at 13:05
  • Btw I also tried listing my custom scope first. No effect. – Matt G Apr 24 '23 at 22:16
0

EDIT: This works, but it's a workaround. The actual answer to my question, and solution, is provided by @Shane below.

I'm not sure why the scope listed in appsettings.json is ignored. But I found that I can include it by adding options.ProviderOptions.DefaultAccessTokenScopes.Add to my AddMsalAuthentication options:

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
    options.ProviderOptions.DefaultAccessTokenScopes.Add("api://[My API client ID/user_read");
});
Matt G
  • 444
  • 5
  • 23
  • I think there's still an issue in MSAL with Blazor because AdditionalScopesToConsent is ignored by MSAL when re-directing to AAD, thus the user doesn't get prompted to consent for the additional scopes. It feels like this would still be an issue if you need scopes from more than one API? An example would be if your Blazor apps wants to call Graph API directly, but also your own API – Dan Harris Jun 22 '23 at 10:34
  • @DanHarris Yep I think you're right. I just add my API scopes here, and when I call the Graph API I use the IAccessTokenProvider for a token with the Graph scopes I need. This works fine. – Matt G Jun 22 '23 at 22:24
  • Is your AddMsalAuthentication call exactly the same as in your answer above or are you specifying both your own API scope as well as a Graph API scope? – Dan Harris Jun 26 '23 at 13:21
  • 1
    It's exactly the same as this. I add the Graph scope when I call the Graph API. I'll open a chat and show you the code. EDIT: turns out I don't know how to do that. I'll contact you another way. – Matt G Jun 26 '23 at 22:39