dotnet/aspnetcore · error
Scopes not granted.
Error message
Scopes not granted.
What it means
Thrown by getTokenCore() in the MSAL interop (AuthenticationService.ts:206) when acquireTokenSilent resolves but the response carries no scopes (response.scopes.length === 0) or an empty access token (response.accessToken === ''). MSAL returning an empty scope set or empty token indicates consent was not granted for any of the requested scopes, so the token is unusable; the service treats this as a hard failure rather than returning a useless token.
Source
Thrown at src/Components/WebAssembly/Authentication.Msal/src/Interop/AuthenticationService.ts:206
async getTokenCore(scopes?: string[]): Promise<AccessToken | undefined> {
const account = this.getAccount();
if (!account) {
throw new Error('Failed to retrieve token, no account found.');
}
const silentRequest = {
redirectUri: this._settings.auth?.redirectUri,
account: account,
scopes: scopes || this._settings.defaultAccessTokenScopes
};
this.debug(`Provisioning a token silently for scopes '${silentRequest.scopes}'`)
this.trace('_msalApplication.acquireTokenSilent', silentRequest);
const response = await this._msalApplication.acquireTokenSilent(silentRequest);
this.trace('_msalApplication.acquireTokenSilent-response', response);
if (response.scopes.length === 0 || response.accessToken === '') {
throw new Error('Scopes not granted.');
}
const result = {
value: response.accessToken,
grantedScopes: response.scopes,
expires: response.expiresOn
};
this.trace('getAccessToken-result', result);
return result;
}
async signIn(context: AuthenticationContext) {
this.trace('signIn', context);
try {
// Before we start any sign-in flow, clear out any previous state so that it doesn't pile up.
this.purgeState();View on GitHub (pinned to 294cab2f9b)
Solutions
- Trigger an interactive token acquisition (acquireTokenPopup/Redirect) so MSAL can prompt for consent on the missing scopes.
- Verify the requested scopes exactly match scopes exposed/delegated in the app registration (e.g., 'api://<clientId>/access_as_user').
- Confirm admin consent is granted for the scopes if the app is configured to require it.
- Check defaultAccessTokenScopes in MSAL configuration is non-empty and contains valid scopes.
Example fix
// before — silent only, fails when consent missing
const r = await msal.acquireTokenSilent({ scopes: ['api://x/y'], account });
// after — fall back to interactive when silent returns nothing
let r = await msal.acquireTokenSilent({ scopes, account }).catch(async () => {
r = await msal.acquireTokenPopup({ scopes });
}); Defensive patterns
Strategy: fallback
Validate before calling
function hasGrantedScopes(response: any): boolean {
return response && Array.isArray(response.scopes) && response.scopes.length > 0 && typeof response.accessToken === 'string' && response.accessToken.length > 0;
} Try / catch
try {
r = await msal.acquireTokenSilent({ scopes, account });
} catch (e) {
if (/scopes not granted/i.test(e.message)) {
r = await msal.acquireTokenPopup({ scopes }); // interactive consent
} else { throw e; }
} Prevention
- Expose and consent to all requested scopes in the app registration.
- Provide an interactive fallback when silent acquisition yields no scopes.
- Verify scope URI format (api://<appId>/<scope>).
- Confirm admin consent when required by policy.
When it happens
Trigger: acquireTokenSilent succeeds but the user/admin has not consented to the requested scopes, or the scopes parameter is empty/invalid, or incremental consent is required. The response object has scopes=[] or accessToken='' after the silent call.
Common situations: Requesting API scopes that were never consented to (the app registration does not expose them, or admin consent is required tenant-wide). Mismatch between defaultAccessTokenScopes and what MSAL can actually grant. Conditional Access policies denying the resource. Using the wrong scope URI format (e.g., missing the API App ID URI).
Related errors
- Failed to retrieve token, no account found.
- Could not load settings from '${settings.configurationEndpoi
- The server responded with status ${response.status}.
- Authorization requires a cascading parameter of type Task<Au
- There is no file with ID ${fileId}. The file list may have c
AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06).
Data as JSON: /api/errors/0bfcc1f46c027c9f.
Report an issue: GitHub.