Hi
@CU19031015-0,
The behavior comes from how scopes and resources work in MSAL and the Microsoft identity platform.
acquireTokenSilent can only return a token for one resource (audience) at a time. When multiple scopes for different resources are passed in a single call, the token is issued only for the first resource in the scopes list. That token will work for Microsoft Graph if the first scope is a Graph scope (for example User.Read), but it will be invalid for the Azure Function, which expects a token whose aud matches the Function’s app ID URI.
To call both Microsoft Graph and the Azure Function from the SPA, request separate tokens, one per resource.
- Request a token for Microsoft Graph only:
TypeScript
const graphTokenResponse = await msalInstance.acquireTokenSilent({
scopes: ["User.Read", "Mail.Read"] // Graph-only scopes});
const graphAccessToken = graphTokenResponse.accessToken;
// Use graphAccessToken in Authorization: Bearer when calling Graph
- Request a token for the Azure Function only:
Use the app ID URI and scope name configured on the Azure Function’s app registration, for example:
TypeScript
const functionTokenResponse = await msalInstance.acquireTokenSilent({
scopes: ["api://<your-function-app-client-id>/your.scope"]
});
const functionAccessToken = functionTokenResponse.accessToken;
// Use functionAccessToken in Authorization: Bearer when calling the Function
- Do not mix resources in a single
scopes array:
TypeScript
// ❌ Wrong: mixed resourcesawait msalInstance.acquireTokenSilent({
scopes: [ "User.Read", // Microsoft Graph"api://<your-function-app-client-id>/your.scope" // Azure Function
]
});
// This returns a token only for the first resource (Graph),// so the Function sees an invalid JWT (wrong audience).
- Verify the scope format for the Azure Function:
The scope must match the Function’s API configuration, typically:
api://<your-function-app-client-id>/<scopeName>
If the Function is fronted by another service or expects a different audience format, use the exact app ID URI and scope format that worked in Postman.
- Use the token correctly when calling the Function:
TypeScript
const headers = new Headers();
headers.append("Authorization", `Bearer ${functionAccessToken}`);
const response = await fetch("https://<your-function-app>.azurewebsites.net/api/endpoint", {
method: "GET",
headers
});
If the Function still reports an invalid JWT, inspect the token’s aud claim and compare it to what the Function expects. If the aud does not match, adjust the scope value (for example, with or without scheme/host) to align with the Function’s configuration.
Hope this helps.
If this helps resolve your issue, please consider marking the response as Verified so it can help others facing a similar scenario.
If you found this helpful, you can also click “Yes” on “Was this reply helpful?” or give it a Like.