Skip to content

AccessDenied (Microsoft Graph): required scopes are missing in the token

Short answer: AccessDenied is the third of Microsoft Graph's three permission-denied codes. It comes from services behind Graph that have their own authorisation layer, such as the Conditional Access policy endpoints, and its message says only that scopes are missing without naming them. The endpoint you called is what tells you which permission to add.

Fix: Look up the permission for the endpoint in Microsoft's permissions reference, add it to the app registration as an Application or Delegated permission to match your token type, grant admin consent, then acquire a new token. For Conditional Access endpoints the read permission is Policy.Read.All. Full steps

At a glance
Error codeAccessDenied
HTTP status403
In Microsoft's Graph error documentationNo (checked ). The wording on this page is what Graph returned in the lab.
CategoryGraph permissions
Attack signalMedium: worth a look in context

Your token was valid and the service behind Graph refused the operation because the token does not carry a scope it requires. HTTP 403, and the message is:

You cannot perform the requested operation, required scopes are missing in the token.

Nothing in it names the scope.

This is one of three codes Graph uses for the same underlying problem, and which one you get depends on which service handled the request rather than on anything you did:

Code Seen on What the message gives you
Authentication_MSGraphPermissionMissing /auditLogs/* The permission name, in the message
AccessDenied /identity/conditionalAccess/policies “required scopes are missing in the token”
Authorization_RequestDenied directory reads and writes “Insufficient privileges to complete the operation.”

Our reading of why there are three: Graph is a front door onto several services, and each brought its own authorisation layer and its own error vocabulary with it. The directory produces Authorization_RequestDenied, the Graph permission layer produces Authentication_MSGraphPermissionMissing, and services with their own scope checks produce AccessDenied. If you are writing an error handler, you need all three, because you cannot predict from the shape of a request which one an endpoint will use.

On the exact casing. A search of learn.microsoft.com for "AccessDenied" as a Graph error code returned no page defining it. A lower-case accessDenied is defined on the OneDrive REST API error page as “The caller doesn’t have permission to perform the action” — a different service’s catalogue, on a non-Graph page, and a different string from the one Graph returned here. Microsoft’s current Graph error reference lists HTTP status codes only and has no named-code table at all. The wording on this page is what Graph v1.0 returned in a lab tenant on 31 October 2026.

On 31 October 2026 an automated run called GET /identity/conditionalAccess/policies with an app-only token whose roles claim, recorded by the run, held Application.ReadWrite.OwnedBy and User.ReadWrite.All:

{
"error": {
"code": "AccessDenied",
"message": "You cannot perform the requested operation, required scopes are missing in the token."
}
}

In the same run, and this is the comparison that makes the point, GET /auditLogs/signIns with the same token returned a different code — Authentication_MSGraphPermissionMissing — whose message named AuditLog.Read.All outright. Two endpoints, the same missing-permission situation, the same token, two different codes and two very different amounts of help.

The permission this endpoint wanted is Policy.Read.All. Nothing in the response says so.

Medium, in aggregate, and the endpoint matters more than the code.

A single occurrence is an integration without consent, which is ordinary and noisy. What deserves attention is which endpoints an identity is collecting 403s on. /identity/conditionalAccess/policies is a reconnaissance target: an attacker who can read Conditional Access policies learns where the gaps are — which apps are excluded, which locations are trusted, which accounts are exempt — before trying anything. A service principal repeatedly failing against the Conditional Access endpoints is more interesting than one failing against a mailbox, even though both produce a 403.

So the pattern to watch is a non-interactive identity producing permission denials across several security-relevant endpoints in a short window: Conditional Access, directory roles, policies, audit logs. Each failure is cheap for the caller and tells them whether to keep going.

As with every Graph error, these do not appear in Entra sign-in logs. The token acquisition appears in AADServicePrincipalSignInLogs and it succeeded, so the identity looks healthy from the sign-in side. The calls land in MicrosoftGraphActivityLogs: KQL for finding this pattern. Because that table carries the HTTP status and not the error code, a query for 403s against /identity/conditionalAccess/* is how you find this one specifically.

  1. Work out the permission from the endpoint, because the message will not tell you. Look the operation up in Microsoft’s permissions reference, which lists permissions per endpoint with the least-privileged first. For reading Conditional Access policies it is Policy.Read.All; for changing them, Policy.ReadWrite.ConditionalAccess.
  2. Add it as the right type. An app-only token (client credentials) needs an Application permission; a token for a signed-in user needs a Delegated permission. In the Microsoft Entra admin center: Identity > Applications > App registrations > your app > API permissions.
  3. Grant admin consent. Grant admin consent for <tenant> on the same blade. Until then the permission is requested, not held.
  4. Acquire a new token. Permissions are claims inside the token. A token minted before the consent does not carry the new scope and will keep failing until it expires.
  5. Think hard before granting the write permission. Policy.ReadWrite.ConditionalAccess lets an application rewrite the rules that protect every sign-in in the tenant. If the app only needs to read policy, grant only Policy.Read.All. A permission granted once to make a script work is standing privilege for the life of the app registration, and Conditional Access is the wrong place to leave spare privilege lying around.

Sources

  1. Microsoft Graph error responses and resource types
  2. Microsoft Graph permissions reference
  3. Common error responses and codes (OneDrive REST API)