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

  1. Trigger an interactive token acquisition (acquireTokenPopup/Redirect) so MSAL can prompt for consent on the missing scopes.
  2. Verify the requested scopes exactly match scopes exposed/delegated in the app registration (e.g., 'api://<clientId>/access_as_user').
  3. Confirm admin consent is granted for the scopes if the app is configured to require it.
  4. 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

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


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/0bfcc1f46c027c9f. Report an issue: GitHub.