I am following this guidance on how to enable Entra Id auth for my Blazor WASM SPA. This should securely log the users in - using Entra External ID - collect some custom attributes on sign up and call my own downstream Web API (NET) project.
I have configured the two app registrations and can successfully login using a Google account. So the auth works fine.
There are app_roles assigned to the user, and these show correctly in the id_token
. I have also configured the id_token to include custom properties I have collected during sign-up.
Problems start when I want to call my API. Looking at the access_token
:
Whatever I do, its
aud
claim value is always00000003-0000-0000-c000-000000000000
(MS Graph API and not the scope I have requested, which is api://<CLIENT_ID_WEB_API>/access_as_user).The app_roles shown in the id_token do not carry over to the access_token. I want to use this for AuthZ decisions.
Custom attributes also don't carry over, there is one attribute (a guid, which is assigned by me), I need to use in the backend to use with filtering the data that is returned to the user.
General guidance is to for an app to not even look at the access token and just pass it along to the downstream API. In my case, this token does not contain al lhe required information I need.
Therefore I'd started to wonder:
- The id_token is signed
- It contains a unique identifier
- It contains the app_roles
- It contains the custom attributes
- It contains the Blazor SPA's client_id in the
aud
claim, but I could just employ a policy on the backend to enforce exactly this value
Both apps (SPA + API), I control. When there is no other information I need why do I not just use the id_token
instead?
Do you find this acceptable?
Let me start with your question: Can I use ID Token to call API?
The short answer is: No, you should never use ID Tokens to call APIs. Access Tokens should be used for that instead. Why?
Now moving forward, let's fix your problem. I assume that you have two app registrations in your Microsoft Entra External ID tenant:
If you're implementing app role business logic in an app-calling-API scenario, you have two app registrations. One app registration is for the app, and a second app registration is for the API. In this case, define the app roles and assign them to the user or group in the app registration of the API. When the user authenticates with the app and requests an access token to call the API, a roles claim is included in the token. Your next step is to add code to your web API to check for those roles when the API is called. Here is example of two roles I defined for tests:
I also assume that you granted permissions for your Blazor app to call API like this:
Next, you have to assign App Roles to users. To do it, you have to open Enterprise Applications section and from there select your API app:
Next, select Users and groups tab, then click + button to add assign user to the application and assign specific role:
Once you assign role or roles, then you have to update your Blazor application's code. I assume you use MSAL and default configuration there. To get Access Token to call your API, you have to define the right scope (using DefaultAccessTokenScopes):
Once user is successfully authenticated, you will also get Access Token with the roles assigned to your user:
Now you can add this Access Token to the authorization header of the HTTP request and call your API securely following the best practices. I hope this helped.