Skip to content

AADSTS90009 fix: app requests a token for itself

Short answer: AADSTS90009 means an app asked Microsoft Entra ID for a token where the resource is the app itself, but named itself with an App ID URI or other string instead of its GUID application ID. Microsoft supports that only with the GUID. It is a developer error with no attack value. Use the client ID GUID as the resource, or call the right API.

Fix: Developer: if the app really needs a token for itself, use its application (client) ID GUID as the resource or scope, for example `<client-id>/.default`. More often the scope should name a different API: put that API's App ID URI or client ID in the scope instead of your own. Full steps

At a glance
CodeAADSTS90009
Microsoft nameTokenForItselfMissingIdenticalAppIdentifier
CategoryApp configuration
TypeConfiguration error
Attack signalLow: usually configuration or user error
Microsoft lookuplogin.microsoftonline.com/error?code=90009

Microsoft’s description:

TokenForItselfMissingIdenticalAppIdentifier: The application is requesting a token for itself. This scenario is supported only if the resource that’s specified is using the GUID-based application ID.

In a token request the app names two things: itself (the client, by its application ID) and the API it wants a token for (the resource). Here both point at the same app. Entra ID accepts that only if the resource is given as the app’s GUID application ID. If the resource is given another way, such as the App ID URI, the request fails with 90009.

An app can be named by more than its GUID. Microsoft describes the App ID URI as a string a resource sets “as its identifier”, which “Clients who acquire a token for the API can use … during an OAuth request”. The service principal’s servicePrincipalNames hold those URIs. Microsoft recommends api://<appId> as the format. So a request with resource api://<your client ID> or https://yourdomain/... can point back at your own app without containing the bare GUID.

The full message users report reads: “AADSTS90009: Application ‘’() is requesting a token for itself. This scenario is supported only if resource is specified using the GUID based App Identifier.”

No. A request that names itself badly gets nothing. Users report it in three situations:

Pattern Likely meaning
An MSAL.js app gets 90009 from acquireTokenSilent when asking for a token for its own App Service API Reported in Microsoft Q&A. The thread doesn’t show the scope used; the error says the app named itself without its GUID.
A script or service account asks for a scope with the app’s own custom-domain URI The scope was meant for another API but named the calling app. In one Microsoft Q&A thread a moderator found exactly that: “the value in scope should be the client-id or application ID URI of your web API”, not the caller’s.
A password-grant (ROPC) request through an identity broker such as Auth0 The broker sends a resource or audience that resolves to its own Entra app. Reported in the Auth0 community.

None of these is a security signal. In the second row the app didn’t mean to ask for itself at all, so check that first.

Microsoft doesn’t document which sign-in table 90009 lands in, or whether it is logged at all. Our interpretation is that it may be rejected before a sign-in row is written, as a malformed request. Check your workspace:

union withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs
| where TimeGenerated > ago(30d)
| where ResultType == "90009"
| summarize Rows = count(), Apps = dcount(AppId) by SourceTable

If rows exist, which apps request tokens for themselves:

union SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs
| where TimeGenerated > ago(30d)
| where ResultType == "90009"
| summarize Rows = count(),
Users = dcount(UserPrincipalName),
Resources = make_set(ResourceIdentity, 5),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by AppId, AppDisplayName = coalesce(AppDisplayName, ServicePrincipalName)
| sort by Rows desc

If nothing comes back, the error message the developer got is the evidence. It names the application ID. ResultType is a string column, so compare it with "90009".

For developers:

  1. Decide which API the token is really for. If it is a different API, put that API’s identifier in the scope, for example api://<web API client ID>/.default or <web API client ID>/.default. Then add that API under your app’s API permissions and get consent.
  2. If your app really needs a token for itself (one registration for front end and API), give the resource as the GUID application ID, for example a scope of <your client ID>/.default or <your client ID>/<scope>. Microsoft notes that some APIs “expect only the app ID (a GUID) and the scope name” in the scope.
  3. With the v1.0 endpoint or a library that sends resource, set resource to the client ID GUID, not the App ID URI. The Auth0 thread quotes a Stack Overflow answer with this advice; the poster didn’t confirm it worked.
  4. For app-only tokens, Microsoft’s rule is that “The scope to request for a client credential flow is the name of the resource followed by /.default”.

For admins: nothing to change in the tenant. Send the app ID from the error to the developer.

Sources

  1. Microsoft Entra authentication and authorization error codes
  2. Restrictions on identifier URIs of Microsoft Entra applications
  3. servicePrincipal resource type (Microsoft Graph)
  4. Scopes for a web API accepting v1.0 tokens
  5. Acquire tokens to call a web API using a daemon application
  6. SigninLogs table reference (Azure Monitor)
  7. Microsoft Q&A: OAuth 2.0 and Azure AD error AADSTS90009 (user report)
  8. Microsoft Q&A: MSAL Python service account token error (user report)
  9. Auth0 Community: Azure AD integration with password grant returns error (user report)