{
  "schemaVersion": "1.0",
  "code": "9010010",
  "name": null,
  "title": "AADSTS9010010: Resource parameter doesn't match the requested scopes (fix)",
  "url": "https://brambrinkmeier.com/entra/aadsts/9010010/",
  "alternates": {
    "markdown": "https://brambrinkmeier.com/entra/aadsts/9010010.md",
    "json": "https://brambrinkmeier.com/api/aadsts/9010010.json",
    "html": "https://brambrinkmeier.com/entra/aadsts/9010010/"
  },
  "description": "AADSTS9010010: the resource parameter doesn't match the requested scopes. Common with MCP clients and trailing slashes. Cause, fix and KQL.",
  "summary": "AADSTS9010010 means a token request carried a resource parameter that doesn't match the resource named in its scopes. Microsoft documents it for MCP clients: the resource value must exactly match the Application ID URI, and the scopes must belong to that same app. A trailing slash is a common cause. It is a client configuration error with low attack value.",
  "inMicrosoftReference": false,
  "unlistedChecked": "2026-10-30",
  "meaningText": "Microsoft's [error code reference](https://learn.microsoft.com/entra/identity-platform/reference-error-codes) doesn't list AADSTS9010010 (checked 30 October 2026).\n\nMy lab tenant returned this message for a client-credentials token request that carried both `scope=https://graph.microsoft.com/.default` and `resource=https://management.azure.com/`:\n\n> AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes.\n\nMicrosoft does explain the code, on its page about securing an MCP server with Entra ID:\n\n> `AADSTS9010010` means the **Application ID URI configured on your app registration doesn't exactly match the value of the `resource` parameter** in the client's OAuth request.\n\nThe same page gives a second cause: *\"This error also occurs when the client sends a scope that's defined on a different app registration than the one referenced by the `resource` parameter. Microsoft Entra ID resolves the `resource` and the scope to the same resource app; if they point at different app registrations, the request fails.\"* That second cause is what the lab request did: the scope names Microsoft Graph, the resource names Azure Resource Manager.\n\nBackground. On the v2.0 endpoint the resource is normally named inside the scope. Microsoft's scopes page: *\"Using `scope={resource-identifier}/.default` is functionally the same as `resource={resource-identifier}` on the v1.0 endpoint.\"* The `resource` parameter comes back through RFC 8707 (Resource Indicators), which the MCP authorization spec requires. Microsoft's MCP page: *\"The MCP specification requires the client to identify the protected resource it wants a token for by sending the `resource` parameter (from RFC 8707) in its token request.\"* So a request now carries the resource twice, and the two must agree.\n\nThe match is exact. Microsoft: *\"The `resource` value the client sends must match the Application ID URI character for character, including the scheme (`https://`), casing of the host, and path. A trailing slash is a common culprit: because an Application ID URI can't end with a slash, a client that sends `resource=https://mcp.contoso.com/` never matches the registered `https://mcp.contoso.com`.\"*",
  "sections": [
    {
      "heading": "What does AADSTS9010010 mean?",
      "text": "Microsoft's [error code reference](https://learn.microsoft.com/entra/identity-platform/reference-error-codes) doesn't list AADSTS9010010 (checked 30 October 2026).\n\nMy lab tenant returned this message for a client-credentials token request that carried both `scope=https://graph.microsoft.com/.default` and `resource=https://management.azure.com/`:\n\n> AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes.\n\nMicrosoft does explain the code, on its page about securing an MCP server with Entra ID:\n\n> `AADSTS9010010` means the **Application ID URI configured on your app registration doesn't exactly match the value of the `resource` parameter** in the client's OAuth request.\n\nThe same page gives a second cause: *\"This error also occurs when the client sends a scope that's defined on a different app registration than the one referenced by the `resource` parameter. Microsoft Entra ID resolves the `resource` and the scope to the same resource app; if they point at different app registrations, the request fails.\"* That second cause is what the lab request did: the scope names Microsoft Graph, the resource names Azure Resource Manager.\n\nBackground. On the v2.0 endpoint the resource is normally named inside the scope. Microsoft's scopes page: *\"Using `scope={resource-identifier}/.default` is functionally the same as `resource={resource-identifier}` on the v1.0 endpoint.\"* The `resource` parameter comes back through RFC 8707 (Resource Indicators), which the MCP authorization spec requires. Microsoft's MCP page: *\"The MCP specification requires the client to identify the protected resource it wants a token for by sending the `resource` parameter (from RFC 8707) in its token request.\"* So a request now carries the resource twice, and the two must agree.\n\nThe match is exact. Microsoft: *\"The `resource` value the client sends must match the Application ID URI character for character, including the scheme (`https://`), casing of the host, and path. A trailing slash is a common culprit: because an Application ID URI can't end with a slash, a client that sends `resource=https://mcp.contoso.com/` never matches the registered `https://mcp.contoso.com`.\"*"
    },
    {
      "heading": "Reproduced in the lab",
      "text": "On 30 October 2026 an automated lab run sent a client-credentials request to the token endpoint with `scope=https://graph.microsoft.com/.default` and `resource=https://management.azure.com/`. Entra returned `AADSTS9010010` with this first line:\n\n> AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes.\n\nThat confirms the code and its wording at the token endpoint for an app (not a user) request. It lands in **`AADServicePrincipalSignInLogs`** for an app-only request: one row, Failure, client credential type \"federatedIdentityCredential\", resource Microsoft Graph. The KQL below was run on these rows ([how](https://brambrinkmeier.com/methodology/)). Both queries found the row (the second with its app ID placeholder replaced)."
    },
    {
      "heading": "Is it an attack?",
      "text": "No. It is a mismatch in how the client builds its token request. No token is issued.\n\n| Pattern | Likely meaning |\n|---|---|\n| An MCP client (Claude Code, an IDE, an agent) fails to connect to an Entra-protected MCP server | The client sends `resource` with a trailing slash, or a URL that differs from the Application ID URI. Users report exactly this in [claude-code issue 52871](https://github.com/anthropics/claude-code/issues/52871), where `resource=https://mcp.businesscentral.dynamics.com/` was sent while the server's metadata declared it without the slash. |\n| A script or daemon sends `resource` and `scope` for two different APIs | The scope belongs to another app registration, as in the lab request. |\n| A broker or identity provider in front of Entra forwards `resource` with only OIDC scopes (`openid profile`) | Users report this with Auth0 federated to Entra in the same GitHub issue. Our reading: no resource-prefixed scope exists to match. |\n| Your own MCP server app is still on v1 access tokens | Microsoft: if `requestedAccessTokenVersion` isn't `2`, *\"you can't register an HTTPS Application ID URI that matches your MCP server URL, so the `resource` value can never match.\"* |\n\nOur reading: the request fails before a token is issued, and only the app's own owner can make it match. There is nothing an attacker gains from triggering it."
    },
    {
      "heading": "How do I find it in my logs (KQL)?",
      "text": "Microsoft doesn't document which sign-in table 9010010 is logged in. Our reading: user-delegated requests (MCP clients signing in a user) would land in `SigninLogs` or `AADNonInteractiveUserSignInLogs`, and app-only requests like the lab one in `AADServicePrincipalSignInLogs`. That is not verified. Check all three:\n\n```kusto\nunion withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where ResultType == \"9010010\"\n| summarize Rows = count(), LastSeen = max(TimeGenerated) by SourceTable, AppId, ResourceDisplayName\n| sort by Rows desc\n```\n\nTo fix the request you need the right resource. For an app that fails with 9010010, see which resources it does get tokens for:\n\n```kusto\nunion withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where AppId == \"<app id of the failing client>\"\n| summarize Successes = countif(ResultType == \"0\"), Failures9010010 = countif(ResultType == \"9010010\") by SourceTable, ResourceDisplayName, ResourceIdentity\n| sort by Failures9010010 desc\n```\n\nA resource with failures and no successes is the one whose `resource` value or scope is wrong. Both queries are starting points; adjust the time window to your retention."
    },
    {
      "heading": "How do I fix AADSTS9010010?",
      "text": "**For the user:** you can't fix this yourself. Report the app or MCP server name to whoever set it up, and include the full error.\n\n**For developers (client side):**\n1. If you talk to the v2.0 endpoint and don't need RFC 8707, drop `resource` and name the API only in `scope`, for example `scope=https://graph.microsoft.com/.default`. Microsoft's scopes page says the two are functionally the same.\n2. If you must send `resource` (MCP clients do), send it verbatim from the server's protected resource metadata. Don't normalise it; the reporter of [claude-code issue 52871](https://github.com/anthropics/claude-code/issues/52871) suspects that round-tripping it through a URL parser added the trailing slash. Nobody in the thread confirmed that.\n3. Make every scope in the request belong to the same API as `resource`. Remove scopes from other APIs and request them in a separate token request.\n\n**For developers (MCP server side), from Microsoft's MCP page:**\n1. Set the app registration to v2 access tokens (`requestedAccessTokenVersion` = `2`).\n2. Set the Application ID URI to the exact canonical URL of your MCP server, with no trailing slash.\n3. Publish that same value as `resource` in your protected resource metadata, so clients send what Entra expects.\n\n**For admins:** compare the failing app's `resource` value with the Application ID URI of the API's app registration. If they differ by a slash, a case or a path, that is the cause."
    }
  ],
  "microsoftWording": {
    "text": "AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes.",
    "source": "Quoted on the page from the lab or from user reports; the code is not in Microsoft's error code reference",
    "sourceUrl": null
  },
  "securityReading": {
    "category": {
      "id": "app-config",
      "label": "App configuration"
    },
    "kind": {
      "id": "config",
      "label": "Configuration error"
    },
    "attackSignal": {
      "level": "low",
      "label": "Low: usually configuration or user error"
    },
    "mitre": [],
    "tables": [],
    "isItAnAttack": "No. It is a mismatch in how the client builds its token request. No token is issued.\n\n| Pattern | Likely meaning |\n|---|---|\n| An MCP client (Claude Code, an IDE, an agent) fails to connect to an Entra-protected MCP server | The client sends `resource` with a trailing slash, or a URL that differs from the Application ID URI. Users report exactly this in [claude-code issue 52871](https://github.com/anthropics/claude-code/issues/52871), where `resource=https://mcp.businesscentral.dynamics.com/` was sent while the server's metadata declared it without the slash. |\n| A script or daemon sends `resource` and `scope` for two different APIs | The scope belongs to another app registration, as in the lab request. |\n| A broker or identity provider in front of Entra forwards `resource` with only OIDC scopes (`openid profile`) | Users report this with Auth0 federated to Entra in the same GitHub issue. Our reading: no resource-prefixed scope exists to match. |\n| Your own MCP server app is still on v1 access tokens | Microsoft: if `requestedAccessTokenVersion` isn't `2`, *\"you can't register an HTTPS Application ID URI that matches your MCP server URL, so the `resource` value can never match.\"* |\n\nOur reading: the request fails before a token is issued, and only the app's own owner can make it match. There is nothing an attacker gains from triggering it.",
    "conditionalAccess": null
  },
  "kql": [
    {
      "title": null,
      "leadIn": "Microsoft doesn't document which sign-in table 9010010 is logged in. Our reading: user-delegated requests (MCP clients signing in a user) would land in `SigninLogs` or `AADNonInteractiveUserSignInLogs`, and app-only requests like the lab one in `AADServicePrincipalSignInLogs`. That is not verified. Check all three:",
      "query": "union withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where ResultType == \"9010010\"\n| summarize Rows = count(), LastSeen = max(TimeGenerated) by SourceTable, AppId, ResourceDisplayName\n| sort by Rows desc"
    },
    {
      "title": null,
      "leadIn": "To fix the request you need the right resource. For an app that fails with 9010010, see which resources it does get tokens for:",
      "query": "union withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where AppId == \"<app id of the failing client>\"\n| summarize Successes = countif(ResultType == \"0\"), Failures9010010 = countif(ResultType == \"9010010\") by SourceTable, ResourceDisplayName, ResourceIdentity\n| sort by Failures9010010 desc"
    }
  ],
  "kqlSectionText": "Microsoft doesn't document which sign-in table 9010010 is logged in. Our reading: user-delegated requests (MCP clients signing in a user) would land in `SigninLogs` or `AADNonInteractiveUserSignInLogs`, and app-only requests like the lab one in `AADServicePrincipalSignInLogs`. That is not verified. Check all three:\n\n```kusto\nunion withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where ResultType == \"9010010\"\n| summarize Rows = count(), LastSeen = max(TimeGenerated) by SourceTable, AppId, ResourceDisplayName\n| sort by Rows desc\n```\n\nTo fix the request you need the right resource. For an app that fails with 9010010, see which resources it does get tokens for:\n\n```kusto\nunion withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where AppId == \"<app id of the failing client>\"\n| summarize Successes = countif(ResultType == \"0\"), Failures9010010 = countif(ResultType == \"9010010\") by SourceTable, ResourceDisplayName, ResourceIdentity\n| sort by Failures9010010 desc\n```\n\nA resource with failures and no successes is the one whose `resource` value or scope is wrong. Both queries are starting points; adjust the time window to your retention.",
  "fix": {
    "short": "Developer: send `resource` exactly as the Application ID URI (no trailing slash, same case and path), and keep every scope on that API. If you don't need RFC 8707, drop `resource` and name the API only in `scope`. MCP server owner: use v2 access tokens and publish the same value in your metadata.",
    "steps": "**For the user:** you can't fix this yourself. Report the app or MCP server name to whoever set it up, and include the full error.\n\n**For developers (client side):**\n1. If you talk to the v2.0 endpoint and don't need RFC 8707, drop `resource` and name the API only in `scope`, for example `scope=https://graph.microsoft.com/.default`. Microsoft's scopes page says the two are functionally the same.\n2. If you must send `resource` (MCP clients do), send it verbatim from the server's protected resource metadata. Don't normalise it; the reporter of [claude-code issue 52871](https://github.com/anthropics/claude-code/issues/52871) suspects that round-tripping it through a URL parser added the trailing slash. Nobody in the thread confirmed that.\n3. Make every scope in the request belong to the same API as `resource`. Remove scopes from other APIs and request them in a separate token request.\n\n**For developers (MCP server side), from Microsoft's MCP page:**\n1. Set the app registration to v2 access tokens (`requestedAccessTokenVersion` = `2`).\n2. Set the Application ID URI to the exact canonical URL of your MCP server, with no trailing slash.\n3. Publish that same value as `resource` in your protected resource metadata, so clients send what Entra expects.\n\n**For admins:** compare the failing app's `resource` value with the Application ID URI of the API's app registration. If they differ by a slash, a case or a path, that is the cause."
  },
  "labReproduced": {
    "date": "2026-10-30",
    "method": "Client-credentials token request against a lab tenant",
    "scenario": "Both scope and resource sent",
    "message": "AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes.",
    "logCaptured": "2026-10-30",
    "kqlTested": "2026-10-30"
  },
  "labSectionText": "On 30 October 2026 an automated lab run sent a client-credentials request to the token endpoint with `scope=https://graph.microsoft.com/.default` and `resource=https://management.azure.com/`. Entra returned `AADSTS9010010` with this first line:\n\n> AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes.\n\nThat confirms the code and its wording at the token endpoint for an app (not a user) request. It lands in **`AADServicePrincipalSignInLogs`** for an app-only request: one row, Failure, client credential type \"federatedIdentityCredential\", resource Microsoft Graph. The KQL below was run on these rows ([how](https://brambrinkmeier.com/methodology/)). Both queries found the row (the second with its app ID placeholder replaced).",
  "labRuns": [
    {
      "date": "2026-10-30",
      "run": "round 4",
      "method": "token-endpoint requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/67",
      "scenario": "Both scope and resource sent",
      "expected": "9010010",
      "got": "9010010",
      "matchedExpectation": true,
      "message": "AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes."
    },
    {
      "date": "2026-10-30",
      "run": "round 5",
      "method": "token-endpoint requests against a lab tenant, using lab-created test apps",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/69",
      "scenario": "Both scope and resource sent",
      "expected": "9010010",
      "got": "9010010",
      "matchedExpectation": true,
      "message": "AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes."
    },
    {
      "date": "2026-10-30",
      "run": "round 5 (first attempt)",
      "method": "token-endpoint requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/68",
      "scenario": "Both scope and resource sent",
      "expected": "9010010",
      "got": "9010010",
      "matchedExpectation": true,
      "message": "AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes."
    },
    {
      "date": "2026-10-30",
      "run": "round 6",
      "method": "token-endpoint requests against a lab tenant, using lab-created test apps",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/70",
      "scenario": "Both scope and resource sent",
      "expected": "9010010",
      "got": "success",
      "matchedExpectation": false,
      "message": ""
    },
    {
      "date": "2026-10-30",
      "run": "round 7",
      "method": "token-endpoint requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/71",
      "scenario": "Both scope and resource sent",
      "expected": "9010010",
      "got": "success",
      "matchedExpectation": false,
      "message": ""
    },
    {
      "date": "2026-10-30",
      "run": null,
      "method": "ROPC and other token-endpoint requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/66",
      "scenario": "Both scope and resource sent",
      "expected": "9010010",
      "got": "9010010",
      "matchedExpectation": true,
      "message": "AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes."
    },
    {
      "date": "2026-10-31",
      "run": "round 8",
      "method": "token-endpoint and device-code requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/72",
      "scenario": "Both scope and resource sent",
      "expected": "9010010",
      "got": "9010010",
      "matchedExpectation": true,
      "message": "AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes."
    },
    {
      "date": "2026-10-31",
      "run": "round 9",
      "method": "token-endpoint and device-code requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/73",
      "scenario": "Both scope and resource sent",
      "expected": "9010010",
      "got": "success",
      "matchedExpectation": false,
      "message": ""
    }
  ],
  "verification": "lab-reproduced",
  "related": [
    "500011",
    "9002313",
    "700016"
  ],
  "coveredBy": [],
  "sources": [
    {
      "title": "Microsoft Entra authentication and authorization error codes",
      "url": "https://learn.microsoft.com/entra/identity-platform/reference-error-codes"
    },
    {
      "title": "Secure a Model Context Protocol (MCP) server with Microsoft Entra ID",
      "url": "https://learn.microsoft.com/en-us/entra/agent-id/secure-mcp-server-with-entra-id"
    },
    {
      "title": "Scopes and permissions in the Microsoft identity platform",
      "url": "https://learn.microsoft.com/entra/identity-platform/scopes-oidc"
    },
    {
      "title": "anthropics/claude-code issue 52871 (AADSTS9010010 with MCP OAuth)",
      "url": "https://github.com/anthropics/claude-code/issues/52871"
    },
    {
      "title": "SigninLogs table reference (Azure Monitor)",
      "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/reference/tables/signinlogs"
    },
    {
      "title": "AADServicePrincipalSignInLogs table reference (Azure Monitor)",
      "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/reference/tables/aadserviceprincipalsigninlogs"
    }
  ],
  "published": "2026-10-30",
  "lastUpdated": "2026-10-30",
  "lastVerified": null,
  "license": "CC BY 4.0",
  "licenseUrl": "https://creativecommons.org/licenses/by/4.0/",
  "licenseNote": "CC BY 4.0 covers the author's own text and classification fields. Quoted Microsoft error wording belongs to Microsoft, is quoted with its source and can change.",
  "attribution": "Bram Brinkmeier, \"AADSTS9010010: Resource parameter doesn't match the requested scopes (fix)\", https://brambrinkmeier.com/entra/aadsts/9010010/ (CC BY 4.0)",
  "microsoftErrorLookup": "https://login.microsoftonline.com/error?code=9010010"
}
