I have Blazor Server App which is getting data from Microsoft's Data Api Builder (DAB). According to the docs, if I want to secure my API, I have to use Azure AD. I have set up two apps in Azure AD, one for my client and one for my API based on these docs:
https://github.com/Azure/data-api-builder/blob/main/docs/authentication-azure-ad.md https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps
The first doc, also explains how to set up Azure CLI and use it login and get a token. If I use the token it generates, my app can access the API just fine.
However, when trying to find a solution to generate and manage JWT tokens without using the CLI, I came across IdentityModel, which attaches itself to the HttpClient and requests the JWT token from Azure for you, Nice!
The problem is, when using the token requested by IdentityServer, I get 403 forbidden.
The token that worked using the AZ CLI, included the scope and roles I expected to see:
Cmd is:
az account get-access-token --scope api://bba-8a1e-e6d16fa4f99f/Endpoint.Access
This returns a token:
{
"typ": "JWT",
"alg": "RS256",
"kid": "-KI3Q9nNR7ZGew"
}.{
"aud": "bba4057e-67d4-4f99f",
"iss": "https://login.microsoftonline.com/51d526da-a780bffe07/v2.0",
"iat": 1682877238,
"nbf": 1682877238,
"exp": 1682882388,
"aio": "AYQAe/8TfUYSCvGWRrrOdUN8=",
"azp": "04b07795-8ddb-461a-bbee-02f9e1bf7b46",
"azpacr": "0",
"idp": "live.com",
"name": "Admin User",
"oid": "c83386fc-b7bc-448a5-801525",
"preferred_username": "soandso@outlook.com",
"rh": "0.AWIA2ibVUT2nl0ic0aHngL_-B34FpLvUZ09Eih7m0W-k-Z9jAFE.",
"roles": [
"Test.Role"
],
"scp": "Endpoint.Access",
"sub": "LvXOKlWQikSGa53IniRQE",
"tid": "51d526da-a73d9cd1-a1e7",
"uti": "C8oLb6qhRUKnAA",
"ver": "2.0"
}.[Signature]
When using the following approach as outlined here: https://identitymodel.readthedocs.io/en/latest/aspnetcore/worker.html
using var client = new HttpClient();
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = "https://login.microsoftonline.com/51da-a73d-4897-9cd1-07/oauth2/v2.0/token",
ClientId = "5285be6e-0134-d824",
ClientSecret = "H768Q~J.cQmhNVAmIBaZU", //SPA secret
Scope = "api://bba4057e-67df99f/.default",
GrantType = "client_credentials"
});
I get token:
{
"typ": "JWT",
"alg": "RS256",
"kid": "-KI3Q9nNR7bRofxmeZoXqbHZGew"
}.{
"aud": "bba4057e-67d4-444f-8a1e-e6d16fa4f99f",
"iss": "https://login.microsoftonline.com/51d52cd1-a1e780bffe07/v2.0",
"iat": 1682877623,
"nbf": 1682877623,
"exp": 1682881523,
"aio": "E2ZgYAhMqo9fcejA5cz26hm8d//cBQA=",
"azp": "bba4057e-67d4-444f-8a1e-e6d16fa4f99f",
"azpacr": "1",
"oid": "9d235ad2-4432-4983-b1e9-8726d68ddb84",
"rh": "0.AWIA2ibVUT2nl0ic0aHngL_-B34FpLvUZ09Eih7m0W-k-Z9jAAA.",
"sub": "9d235ad2-4432-4983-b1e9-8726d68ddb84",
"tid": "51d526da-a73d-4897-9cd1-a1e780bffe07",
"uti": "cb7ttHUn5kCvHllSI7m6AA",
"ver": "2.0"
}.[Signature]
Notice how the token is missing the scope and roles. I'm not 100% sure if this is why I am getting rejected, but I assume so.
According to this statement found here: https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps
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 ID token to call the API, a roles claim is included in the ID token. Your next step is to add code to your web API to check for those roles when the API is called.
It seems that that the roles are not returned unless I request an ID token instead of an Access token, however I cannot see a clear way to do so using the IdentityModel. Some documentation says I need to use OpenID for this but since I the consumer of my API is my client app and not a specific user, this doesn't seem like the proper flow. In fact the docs here, https://identitymodel.readthedocs.io/en/latest/aspnetcore/web.html say the following,
In web applications you might either want to call APIs using the client identity or the user identity. The client identity scenario is exactly the same as the previous section that covered service workers.
And the "worker" approach led me to the sample code used to request the token above.
The following shows my registered apps in Azure AD (second and fourth):
The following image shows my API app's permissions:
The following shows the API is exposed and my client app is authorized to use it.
The following shows the Role assigned to the API app:
I do appreciate any help I can get. I have spent a couple of days on this so far and have run out of ideas. Thanks in advance.