You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The core issue seems to be that OpenCode is passing a 'resource' parameter that doesn't match your Entra configuration. Issues #7306 and #7135 discuss similar problems with OAuth configuration overrides and path handling.
Feel free to ignore if none of these address your specific case.
2. The @modelcontextprotocol/sdk sends resource to Entra ID
The SDK's startAuthorization() reads the resource field from the PRM document and appends resource=https://my.server.com/mcp/ to the Entra ID /authorize URL. executeTokenRequest() does the same for the /token endpoint.
3. Entra ID v2.0 rejects the resource parameter
Microsoft Entra ID v2.0 endpoints do not support RFC 8707 Resource Indicators. In v2.0, the target resource is encoded within the scope parameter (e.g., api://<app-id>/User.Mcp). When resource is present alongside scope pointing to different identifiers, Entra returns AADSTS9010010.
4. The spec already has a fix, the SDK hasn't caught up
SEP-835 (merged) updated the MCP auth spec with a scope selection strategy: use scope from WWW-Authenticate first, then fall back to scopes_supported from PRM — making the resource parameter unnecessary when scopes are available. However, the TypeScript SDK still unconditionally sends resource.
TL;DR: The SDK sends resource per RFC 8707, Entra ID doesn't support RFC 8707, and the spec-level fix (SEP-835) exists but the SDK hasn't implemented it yet. OpenCode needs to strip resource at its layer until the SDK catches up.
There doesn’t need to be a vendor specific case. Just add another identifier uri to the entra app with the trailing slash and everything starts working.
The resource parameter validation in the V2 end point is requiring an exact match. That means you need to have an identifier with the trailing slash and another identifier without it because some MCP clients will include it and some will not. I am raising that issue internally right now to try and ignore the trailing slash difference.
This is actually a really important security issue to get right because without the resource parameter, someone can steal your tokens by hosting a malicious MCP server that advertises scopes for another service. This is called a confused deputy attack.
where e0630cc0-d0c2-4512-beda-dec29dd569bc application id of mcp server app, c1e199f6-ae8d-40a3-9c0c-b921e1af6474 is the connector app where we allow opencode localhost callback url.
the same pair of clientId and scope works in PowerApp custom connector to connect same mcp server to Copilot Studio Agent.
when i execute auth from opencode
it fails with error
Request Id: f8cb6001-b9ac-4d32-8740-7ed346793d00
Correlation Id: 4eb8dc33-7c4e-4e42-ba5b-ff239de6f8da
Timestamp: 2026-03-19T07:05:35Z
Message: AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes.
redirect url looks like this https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/authorize?response_type=code&client_id=c1e199f6-ae8d-40a3-9c0c-b921e1af6474&code_challenge=fFFoIXv8ix4UmmrIncHCLktPiVhP_x-EFQA7ilvHYvE&code_challenge_method=S256&redirect_uri=http%3A%2F%2F127.0.0.1%3A19876%2Fmcp%2Foauth%2Fcallback&state=3ab490b787e62afb0ee1a22782748f163146ef4282293490b6a772f956c5c543&scope=api%3A%2F%2Fe0630cc0-d0c2-4512-beda-dec29dd569bc%2FUser.Mcp+api%3A%2F%2Fe0630cc0-d0c2-4512-beda-dec29dd569bc%2FUser.McpPlus&resource=https%3A%2F%2Fdomain.com%2Fmcp%2F
p.s. entra id application id uri must not end in a slash
There doesn’t need to be a vendor specific case. Just add another identifier uri to the entra app with the trailing slash and everything starts working.
p.p.s. opencode uses 127.0.0.1 instead of localhost and there is no way to add 127.0.0.1 as return url in entra id, but this the next issues after resource
It seems there is a difference between msft internal app validation and 3p app validation. I'll code up a fix today so trailing slash is normalized internally at login.microsoftonline.com
It seems there is a difference between msft internal app validation and 3p app validation. I'll code up a fix today so trailing slash is normalized internally at login.microsoftonline.com
This would be great! Yes Microsoft doesn't allow the app identifier to end with a trailing slash so currently it's impossible to get them to match. But I think using resource at all is discouraged in the v2 flow so that's why I attempted that. If we can remove the trailing slash then I should be able to get things working.
Also the change to localhost from 127.0.0.1 is required since Microsoft will only allow callback urls that are http if it's localhost (that word exactly) otherwise it requires https
@sergey-tihon there are a number of things that can be done to tighten up the security of that app manifest, but I'm going to focus specifically on the issue we are discussing. The resource parameter validation in the V2 endpoint of Entra sources the allowed values from the identifier URIs and not the redirect URIs. You would need to add the http uri that is being passed by opencode in the resource parameter to you identifier uris array.
BTW, there is already a PR internally to relax the trailing /issue in resource parameter validation.
I’ve fixed my issue. Here is the solution that worked for me:
Change the resource name in.well-known/oauth-protected-resource from https://app.example.com/mcp/ to https://app.example.com/mcp (remove the trailing slash)
Add the new resource name to theidentifierUris list in the MCP server app manifest.
Create a new Entra ID application for public clients (e.g. OpenCode) with the PCKE flow enabled:
Allow public client flows by setting "isFallbackPublicClient": true
Closing as resolved by the configuration documented in this thread. The working setup uses an exact protected-resource URI registered in Entra identifierUris, a public PKCE client, and the OpenCode loopback callback under Mobile and desktop applications. OpenCode intentionally retains RFC 8707 resource indicators; removing them, as proposed in #12321, would weaken audience binding.
Activity
github-actions commentedon Feb 5, 2026
This issue might be a duplicate of existing issues. Please check:
The core issue seems to be that OpenCode is passing a 'resource' parameter that doesn't match your Entra configuration. Issues #7306 and #7135 discuss similar problems with OAuth configuration overrides and path handling.
Feel free to ignore if none of these address your specific case.
sergey-tihon commentedon Mar 3, 2026
Investigation: Root cause traced to MCP TypeScript SDK sending
resourceparameter to Entra IDI've investigated this issue. Here's what happens:
1. The MCP server responds correctly (per RFC 9728)
The server returns a
WWW-Authenticateheader pointing to its Protected Resource Metadata:That metadata document contains:
{ "resource": "https://my.server.com/mcp/", "authorization_servers": ["https://login.microsoftonline.com/<tenant>/v2.0"], "scopes_supported": ["api://<app-id>/User.Mcp"] }2. The
@modelcontextprotocol/sdksendsresourceto Entra IDThe SDK's
startAuthorization()reads theresourcefield from the PRM document and appendsresource=https://my.server.com/mcp/to the Entra ID/authorizeURL.executeTokenRequest()does the same for the/tokenendpoint.3. Entra ID v2.0 rejects the
resourceparameterMicrosoft Entra ID v2.0 endpoints do not support RFC 8707 Resource Indicators. In v2.0, the target resource is encoded within the
scopeparameter (e.g.,api://<app-id>/User.Mcp). Whenresourceis present alongsidescopepointing to different identifiers, Entra returnsAADSTS9010010.4. The spec already has a fix, the SDK hasn't caught up
SEP-835 (merged) updated the MCP auth spec with a scope selection strategy: use
scopefromWWW-Authenticatefirst, then fall back toscopes_supportedfrom PRM — making theresourceparameter unnecessary when scopes are available. However, the TypeScript SDK still unconditionally sendsresource.This is tracked upstream as modelcontextprotocol/typescript-sdk#862. The SDK maintainers prefer to solve this at the spec level (done) and via pluggable auth (typescript-sdk#485) rather than adding vendor-specific carve-outs.
TL;DR: The SDK sends
resourceper RFC 8707, Entra ID doesn't support RFC 8707, and the spec-level fix (SEP-835) exists but the SDK hasn't implemented it yet. OpenCode needs to stripresourceat its layer until the SDK catches up.adameska commentedon Mar 3, 2026
Agreed, it would be nice to not have a vendor specific case for this, but until then this does fix the problem and is scoped to entra: #12321
DavidParks8 commentedon Mar 19, 2026
There doesn’t need to be a vendor specific case. Just add another identifier uri to the entra app with the trailing slash and everything starts working.
DavidParks8 commentedon Mar 19, 2026
The resource parameter validation in the V2 end point is requiring an exact match. That means you need to have an identifier with the trailing slash and another identifier without it because some MCP clients will include it and some will not. I am raising that issue internally right now to try and ignore the trailing slash difference.
This is actually a really important security issue to get right because without the resource parameter, someone can steal your tokens by hosting a malicious MCP server that advertises scopes for another service. This is called a confused deputy attack.
DavidParks8 commentedon Mar 19, 2026
@adameska please abandon the PR. We should not introduce an easily exploitable vulnerability into opencode.
sergey-tihon commentedon Mar 19, 2026
@DavidParks8 thank you for looking at this issue, but it is not only '/' issue.
Here is configuration of my mcp
where
e0630cc0-d0c2-4512-beda-dec29dd569bcapplication id of mcp server app,c1e199f6-ae8d-40a3-9c0c-b921e1af6474is the connector app where we allow opencode localhost callback url.the same pair of
clientIdandscopeworks in PowerApp custom connector to connect same mcp server to Copilot Studio Agent.when i execute auth from opencode
it fails with error
redirect url looks like this
https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/authorize?response_type=code&client_id=c1e199f6-ae8d-40a3-9c0c-b921e1af6474&code_challenge=fFFoIXv8ix4UmmrIncHCLktPiVhP_x-EFQA7ilvHYvE&code_challenge_method=S256&redirect_uri=http%3A%2F%2F127.0.0.1%3A19876%2Fmcp%2Foauth%2Fcallback&state=3ab490b787e62afb0ee1a22782748f163146ef4282293490b6a772f956c5c543&scope=api%3A%2F%2Fe0630cc0-d0c2-4512-beda-dec29dd569bc%2FUser.Mcp+api%3A%2F%2Fe0630cc0-d0c2-4512-beda-dec29dd569bc%2FUser.McpPlus&resource=https%3A%2F%2Fdomain.com%2Fmcp%2Fp.s. entra id application id uri must not end in a slash
p.p.s. opencode uses
127.0.0.1instead oflocalhostand there is no way to add127.0.0.1as return url in entra id, but this the next issues afterresourceDavidParks8 commentedon Mar 19, 2026
It seems there is a difference between msft internal app validation and 3p app validation. I'll code up a fix today so trailing slash is normalized internally at login.microsoftonline.com
DavidParks8 commentedon Mar 19, 2026
@sergey-tihon can you also share your app registration manifest, but with sensitive data stripped?
adameska commentedon Mar 19, 2026
This would be great! Yes Microsoft doesn't allow the app identifier to end with a trailing slash so currently it's impossible to get them to match. But I think using resource at all is discouraged in the v2 flow so that's why I attempted that. If we can remove the trailing slash then I should be able to get things working.
Also the change to localhost from 127.0.0.1 is required since Microsoft will only allow callback urls that are http if it's localhost (that word exactly) otherwise it requires https
adameska commentedon Mar 19, 2026
Here is mine, if resource is required i would need to change my appId to be "https://mcpserver.company.com" instead of the default since MCP requires that resource to also match the URL of the hosted mcp resource...:
{ "id": "objectid", "deletedDateTime": null, "appId": "appid", "applicationTemplateId": null, "disabledByMicrosoftStatus": null, "createdByAppId": "azure", "createdDateTime": "2026-02-03T15:59:18Z", "displayName": "DEV - Agent Instructions", "description": "MCP Server to serve up instruction and prompt resources", "groupMembershipClaims": null, "identifierUris": [ "api://appid" ], "isDeviceOnlyAuthSupported": null, "isFallbackPublicClient": null, "nativeAuthenticationApisEnabled": null, "notes": null, "publisherDomain": "mycompany.onmicrosoft.com", "serviceManagementReference": null, "signInAudience": "AzureADMyOrg", "tags": [ "Owners=AI Devs", "Environment=development", "UniqueName=AI.AgentInstructions" ], "tokenEncryptionKeyId": null, "samlMetadataUrl": null, "defaultRedirectUri": null, "certification": null, "servicePrincipalLockConfiguration": null, "requestSignatureVerification": null, "addIns": [], "api": { "acceptMappedClaims": true, "knownClientApplications": [], "requestedAccessTokenVersion": 2, "oauth2PermissionScopes": [ { "adminConsentDescription": "basic", "adminConsentDisplayName": "basic", "id": "abc", "isEnabled": true, "type": "User", "userConsentDescription": "basic", "userConsentDisplayName": "basic", "value": "basic" } ], "preAuthorizedApplications": [ { "appId": "", "delegatedPermissionIds": [ "abc" ] } ] }, "appRoles": [ { "allowedMemberTypes": [ "User" ], "description": "PromptsRead", "displayName": "PromptsRead", "id": "", "isEnabled": true, "origin": "Application", "value": "PromptsRead" }, { "allowedMemberTypes": [ "User" ], "description": "InstructionsRead", "displayName": "InstructionsRead", "id": "", "isEnabled": true, "origin": "Application", "value": "InstructionsRead" } ], "info": { "logoUrl": null, "marketingUrl": null, "privacyStatementUrl": null, "supportUrl": null, "termsOfServiceUrl": null }, "keyCredentials": [], "optionalClaims": { "accessToken": [ { "additionalProperties": [], "essential": false, "name": "auth_time", "source": null }, { "additionalProperties": [], "essential": false, "name": "email", "source": null }, { "additionalProperties": [], "essential": false, "name": "upn", "source": null } ], "idToken": [], "saml2Token": [] }, "parentalControlSettings": { "countriesBlockedForMinors": [], "legalAgeGroupRule": "Allow" }, "passwordCredentials": [], "publicClient": { "redirectUris": [] }, "requiredResourceAccess": [ { "resourceAppId": "00000003-0000-0000-c000-000000000000", "resourceAccess": [ { "id": "64a6cdd6-aab1-4aaf-94b8-3cc8405e90d0", "type": "Scope" }, { "id": "14dad69e-099b-42c9-810b-d002981feec1", "type": "Scope" } ] } ], "verifiedPublisher": { "displayName": null, "verifiedPublisherId": null, "addedDateTime": null }, "web": { "homePageUrl": null, "logoutUrl": null, "redirectUris": [], "implicitGrantSettings": { "enableAccessTokenIssuance": true, "enableIdTokenIssuance": true }, "redirectUriSettings": [] }, "spa": { "redirectUris": [ "http://localhost:19876/mcp/oauth/callback", "http://localhost:5533/" ] } }sergey-tihon commentedon Mar 19, 2026
@DavidParks8 sure. you can find 2 manifest below
Main App Manifest (Backend/API/MCP) (
main_app_manifest.json){ "id": "99f0b3e2-393b-448b-8cda-94be0a7345fa", "deletedDateTime": null, "appId": "e0630cc0-d0c2-4512-beda-dec29dd569bc", "applicationTemplateId": null, "disabledByMicrosoftStatus": null, "createdByAppId": null, "createdDateTime": "2022-10-20T12:43:58Z", "displayName": "myapp-prod", "description": null, "groupMembershipClaims": "ApplicationGroup", "identifierUris": [ "https://app.example.com/mcp", "api://app.example.com/e0630cc0-d0c2-4512-beda-dec29dd569bc", "api://cloud.app.example.com/e0630cc0-d0c2-4512-beda-dec29dd569bc", "api://e0630cc0-d0c2-4512-beda-dec29dd569bc", "api://ui.app.example.com/e0630cc0-d0c2-4512-beda-dec29dd569bc" ], "isDeviceOnlyAuthSupported": false, "isFallbackPublicClient": false, "nativeAuthenticationApisEnabled": null, "notes": null, "publisherDomain": "CONTOSO.onmicrosoft.com", "serviceManagementReference": null, "signInAudience": "AzureADMyOrg", "tags": [], "tokenEncryptionKeyId": null, "samlMetadataUrl": null, "defaultRedirectUri": null, "certification": null, "servicePrincipalLockConfiguration": null, "requestSignatureVerification": null, "addIns": [], "api": { "acceptMappedClaims": false, "knownClientApplications": [], "requestedAccessTokenVersion": 2, "oauth2PermissionScopes": [ { "adminConsentDescription": "Use MCP Server extra tools", "adminConsentDisplayName": "Use MCP Server extra tools", "id": "4bc15a96-b613-4607-9ac1-51f05d894320", "isEnabled": true, "type": "User", "userConsentDescription": "Use MCP Server extra tools", "userConsentDisplayName": "Use MCP Server extra tools", "value": "User.McpPlus" }, { "adminConsentDescription": "Use MCP Server", "adminConsentDisplayName": "Use MCP Server", "id": "29450ca4-66cf-42c8-84d4-f87e92b2fb55", "isEnabled": true, "type": "User", "userConsentDescription": "Use MCP Server", "userConsentDisplayName": "Use MCP Server", "value": "User.Mcp" }, { "adminConsentDescription": "Access to file metadata (download url, preview url, project code, contacts and etc)", "adminConsentDisplayName": "Access documents metadata", "id": "e56a56ab-01ac-4a9b-b229-762da01ab3a5", "isEnabled": true, "type": "User", "userConsentDescription": "Access to file metadata (download url, preview url, project code, contacts and etc)", "userConsentDisplayName": "Access documents metadata", "value": "Files.Details" }, { "adminConsentDescription": "Search all documents", "adminConsentDisplayName": "Search all documents", "id": "00a5dd9b-9def-487d-880a-9e25d056ef46", "isEnabled": true, "type": "User", "userConsentDescription": "Search all documents", "userConsentDisplayName": "Search all documents", "value": "Files.Search" }, { "adminConsentDescription": "Use experimental tools", "adminConsentDisplayName": "Use experimental tools", "id": "f48f23e1-d087-44e0-b69e-b27bf035e282", "isEnabled": true, "type": "User", "userConsentDescription": "Use experimental tools", "userConsentDisplayName": "Use experimental tools", "value": "Experimental.Tools" }, { "adminConsentDescription": "Allows the app to read the signed-in user's profile.", "adminConsentDisplayName": "Read user profile", "id": "1df87b64-f079-4893-8559-ddca2ffec997", "isEnabled": true, "type": "User", "userConsentDescription": "Allows the app to read your profile.", "userConsentDisplayName": "Read your profile", "value": "User.Read" } ], "preAuthorizedApplications": [ { "appId": "04b07795-8ddb-461a-bbee-02f9e1bf7b46", "delegatedPermissionIds": [ "29450ca4-66cf-42c8-84d4-f87e92b2fb55", "4bc15a96-b613-4607-9ac1-51f05d894320", "e56a56ab-01ac-4a9b-b229-762da01ab3a5" ] }, { "appId": "a8ade976-1dab-4375-9583-e7d010c2b764", "delegatedPermissionIds": [ "00a5dd9b-9def-487d-880a-9e25d056ef46", "1df87b64-f079-4893-8559-ddca2ffec997", "29450ca4-66cf-42c8-84d4-f87e92b2fb55", "e56a56ab-01ac-4a9b-b229-762da01ab3a5" ] }, { "appId": "ba9f306a-e8fa-4d3f-8b3c-952d2fe72378", "delegatedPermissionIds": [ "4bc15a96-b613-4607-9ac1-51f05d894320", "29450ca4-66cf-42c8-84d4-f87e92b2fb55" ] }, { "appId": "a148733f-13d0-490c-be20-c627d7f49910", "delegatedPermissionIds": [ "29450ca4-66cf-42c8-84d4-f87e92b2fb55" ] }, { "appId": "22cf1b3f-2609-494a-8736-98dd32ac392f", "delegatedPermissionIds": [ "29450ca4-66cf-42c8-84d4-f87e92b2fb55", "4bc15a96-b613-4607-9ac1-51f05d894320" ] }, { "appId": "61d9c2a8-2065-47c7-b93c-d3f544cf9d26", "delegatedPermissionIds": [ "29450ca4-66cf-42c8-84d4-f87e92b2fb55", "4bc15a96-b613-4607-9ac1-51f05d894320" ] }, { "appId": "aebc6443-996d-45c2-90f0-388ff96faa56", "delegatedPermissionIds": [ "29450ca4-66cf-42c8-84d4-f87e92b2fb55", "4bc15a96-b613-4607-9ac1-51f05d894320" ] }, { "appId": "c1e199f6-ae8d-40a3-9c0c-b921e1af6474", "delegatedPermissionIds": [ "29450ca4-66cf-42c8-84d4-f87e92b2fb55", "4bc15a96-b613-4607-9ac1-51f05d894320" ] }, { "appId": "d3590ed6-52b3-4102-aeff-aad2292ab01c", "delegatedPermissionIds": [ "1df87b64-f079-4893-8559-ddca2ffec997", "00a5dd9b-9def-487d-880a-9e25d056ef46", "e56a56ab-01ac-4a9b-b229-762da01ab3a5" ] }, { "appId": "ea5a67f6-b6f3-4338-b240-c655ddc3cc8e", "delegatedPermissionIds": [ "1df87b64-f079-4893-8559-ddca2ffec997", "00a5dd9b-9def-487d-880a-9e25d056ef46", "e56a56ab-01ac-4a9b-b229-762da01ab3a5" ] }, { "appId": "1fec8e78-bce4-4aaf-ab1b-5451cc387264", "delegatedPermissionIds": [ "1df87b64-f079-4893-8559-ddca2ffec997", "00a5dd9b-9def-487d-880a-9e25d056ef46", "e56a56ab-01ac-4a9b-b229-762da01ab3a5" ] }, { "appId": "93d53678-613d-4013-afc1-62e9e444a0a5", "delegatedPermissionIds": [ "1df87b64-f079-4893-8559-ddca2ffec997", "00a5dd9b-9def-487d-880a-9e25d056ef46", "e56a56ab-01ac-4a9b-b229-762da01ab3a5" ] }, { "appId": "39207959-1658-49d1-b164-d3a2bc17b6a5", "delegatedPermissionIds": [ "00a5dd9b-9def-487d-880a-9e25d056ef46", "1df87b64-f079-4893-8559-ddca2ffec997" ] }, { "appId": "bb9415ea-4b3c-4e1b-af38-025144276196", "delegatedPermissionIds": [ "00a5dd9b-9def-487d-880a-9e25d056ef46", "1df87b64-f079-4893-8559-ddca2ffec997" ] }, { "appId": "303026a5-4e03-488b-8ab8-d7d013f7e0c7", "delegatedPermissionIds": [ "00a5dd9b-9def-487d-880a-9e25d056ef46", "e56a56ab-01ac-4a9b-b229-762da01ab3a5", "1df87b64-f079-4893-8559-ddca2ffec997" ] }, { "appId": "2bfda454-7707-4bb8-a51a-dcd4504252db", "delegatedPermissionIds": [ "1df87b64-f079-4893-8559-ddca2ffec997", "f48f23e1-d087-44e0-b69e-b27bf035e282", "00a5dd9b-9def-487d-880a-9e25d056ef46", "e56a56ab-01ac-4a9b-b229-762da01ab3a5" ] } ] }, "appRoles": [ { "allowedMemberTypes": [ "User" ], "description": "Has access to administrative features", "displayName": "Administrator", "id": "cf6a71a6-df77-4578-b5de-e6482f3f19f6", "isEnabled": true, "origin": "Application", "value": "Admin" }, { "allowedMemberTypes": [ "User" ], "description": "Can moderate content", "displayName": "Content Moderator", "id": "44a8acfc-1013-4144-8812-b97f558336d6", "isEnabled": true, "origin": "Application", "value": "Moderator" }, { "allowedMemberTypes": [ "User" ], "description": "Has access to Swagger UI", "displayName": "Swagger UI", "id": "67b8a138-8948-44f0-b07e-7c7ef2766c6e", "isEnabled": true, "origin": "Application", "value": "Swagger" }, { "allowedMemberTypes": [ "User" ], "description": "Has access to early access features (private beta)", "displayName": "Early Access", "id": "606f7969-e8c1-4f09-861d-3b94a65f7418", "isEnabled": true, "origin": "Application", "value": "EarlyAccess" } ], "info": { "logoUrl": null, "marketingUrl": null, "privacyStatementUrl": null, "supportUrl": null, "termsOfServiceUrl": null }, "keyCredentials": [ { "customKeyIdentifier": "AABBCCDD00112233445566778899AABBCCDDEEFF", "displayName": "CN=example-app-prod", "endDateTime": "2027-03-17T11:56:28Z", "key": null, "keyId": "3d6c6208-63c3-b767-827e-0101fc361d1e", "startDateTime": "2025-03-17T11:46:28Z", "type": "AsymmetricX509Cert", "usage": "Verify" }, { "customKeyIdentifier": "AABBCCDD00112233445566778899AABBCCDDEEFF", "displayName": "CN=example-app-prod", "endDateTime": "2027-03-17T11:56:28Z", "key": null, "keyId": "52c735f8-9015-46c7-bbe4-c218b98b5e9a", "startDateTime": "2025-03-17T11:46:28Z", "type": "AsymmetricX509Cert", "usage": "Verify" } ], "optionalClaims": { "accessToken": [], "idToken": [], "saml2Token": [] }, "parentalControlSettings": { "countriesBlockedForMinors": [], "legalAgeGroupRule": "Allow" }, "passwordCredentials": [], "publicClient": { "redirectUris": [] }, "requiredResourceAccess": [ { "resourceAppId": "00000003-0000-0000-c000-000000000000", "resourceAccess": [ { "id": "df85f4d6-205c-4ac5-a5ea-6bf408dba283", "type": "Scope" }, { "id": "5c28f0bf-8a70-41f1-8ab2-9032436ddb65", "type": "Scope" }, { "id": "14dad69e-099b-42c9-810b-d002981feec1", "type": "Scope" }, { "id": "205e70e5-aba6-4c52-a976-6d2d46c48043", "type": "Scope" }, { "id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d", "type": "Scope" }, { "id": "37f7f235-527c-4136-accd-4a02d197296e", "type": "Scope" }, { "id": "7ab1d382-f21e-4acd-a863-ba3e13f7da61", "type": "Role" }, { "id": "883ea226-0bf2-4a8f-9f9d-92c9162a727d", "type": "Role" }, { "id": "f12eb8d6-28e3-46e6-b2c0-b7e4dc69fc95", "type": "Role" }, { "id": "df021288-bdef-4463-88db-98f22de89214", "type": "Role" } ] }, { "resourceAppId": "00000003-0000-0ff1-ce00-000000000000", "resourceAccess": [ { "id": "4e0d77b0-96ba-4398-af14-3baa780278f4", "type": "Scope" }, { "id": "c8e3537c-ec53-43b9-bed3-b2bd3617ae97", "type": "Role" }, { "id": "20d37865-089c-4dee-8c41-6967602d4ac8", "type": "Role" } ] } ], "verifiedPublisher": { "displayName": null, "verifiedPublisherId": null, "addedDateTime": null }, "web": { "homePageUrl": null, "logoutUrl": "https://ui.app.example.com/signout-oidc", "redirectUris": [ "https://app.example.com/signin-oidc", "https://ui.app.example.com/signin-oidc", "https://cloud.app.example.com/signin-oidc" ], "implicitGrantSettings": { "enableAccessTokenIssuance": true, "enableIdTokenIssuance": true }, "redirectUriSettings": [ { "uri": "https://app.example.com/signin-oidc", "index": null }, { "uri": "https://ui.app.example.com/signin-oidc", "index": null }, { "uri": "https://cloud.app.example.com/signin-oidc", "index": null } ] }, "spa": { "redirectUris": [] } }Connector App Manifest (User auth, token exchange for mcp) (
connector_app_manifest.json){ "id": "47b1e109-8cc1-4a83-a52c-c9846fb2a95f", "deletedDateTime": null, "appId": "c1e199f6-ae8d-40a3-9c0c-b921e1af6474", "applicationTemplateId": null, "disabledByMicrosoftStatus": null, "createdByAppId": "18ed3507-a475-4ccb-b669-d66bc9f2a36e", "createdDateTime": "2025-08-20T16:49:27Z", "displayName": "myapp-prod-connector", "description": null, "groupMembershipClaims": null, "identifierUris": [ "api://botid-c1e199f6-ae8d-40a3-9c0c-b921e1af6474" ], "isDeviceOnlyAuthSupported": null, "isFallbackPublicClient": false, "nativeAuthenticationApisEnabled": null, "notes": null, "publisherDomain": "CONTOSO.onmicrosoft.com", "serviceManagementReference": null, "signInAudience": "AzureADMyOrg", "tags": [], "tokenEncryptionKeyId": null, "samlMetadataUrl": null, "defaultRedirectUri": null, "certification": null, "optionalClaims": null, "requestSignatureVerification": null, "addIns": [], "api": { "acceptMappedClaims": null, "knownClientApplications": [], "requestedAccessTokenVersion": 2, "oauth2PermissionScopes": [ { "adminConsentDescription": "Allow to sign in", "adminConsentDisplayName": "Allow to sign in", "id": "9ab1995f-79d7-4dd1-8144-02eb497d8ef0", "isEnabled": true, "type": "User", "userConsentDescription": null, "userConsentDisplayName": null, "value": "Placeholder" } ], "preAuthorizedApplications": [ { "appId": "9f2fa2e2-b09e-4ac7-a64f-29ae31229703", "delegatedPermissionIds": [ "9ab1995f-79d7-4dd1-8144-02eb497d8ef0" ] }, { "appId": "195b235f-d425-4665-ab41-4968f0fd63ee", "delegatedPermissionIds": [ "9ab1995f-79d7-4dd1-8144-02eb497d8ef0" ] }, { "appId": "fe053c5f-3692-4f14-aef2-ee34fc081cae", "delegatedPermissionIds": [ "9ab1995f-79d7-4dd1-8144-02eb497d8ef0" ] }, { "appId": "1fec8e78-bce4-4aaf-ab1b-5451cc387264", "delegatedPermissionIds": [ "9ab1995f-79d7-4dd1-8144-02eb497d8ef0" ] }, { "appId": "5e3ce6c0-2b1f-4285-8d4b-75ee78787346", "delegatedPermissionIds": [ "9ab1995f-79d7-4dd1-8144-02eb497d8ef0" ] } ] }, "appRoles": [], "info": { "logoUrl": null, "marketingUrl": null, "privacyStatementUrl": null, "supportUrl": null, "termsOfServiceUrl": null }, "keyCredentials": [], "parentalControlSettings": { "countriesBlockedForMinors": [], "legalAgeGroupRule": "Allow" }, "passwordCredentials": [ ], "publicClient": { "redirectUris": [] }, "requiredResourceAccess": [ { "resourceAppId": "e0630cc0-d0c2-4512-beda-dec29dd569bc", "resourceAccess": [ { "id": "4bc15a96-b613-4607-9ac1-51f05d894320", "type": "Scope" }, { "id": "29450ca4-66cf-42c8-84d4-f87e92b2fb55", "type": "Scope" } ] }, { "resourceAppId": "00000003-0000-0000-c000-000000000000", "resourceAccess": [ { "id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182", "type": "Scope" }, { "id": "37f7f235-527c-4136-accd-4a02d197296e", "type": "Scope" }, { "id": "14dad69e-099b-42c9-810b-d002981feec1", "type": "Scope" } ] } ], "verifiedPublisher": { "displayName": null, "verifiedPublisherId": null, "addedDateTime": null }, "web": { "homePageUrl": null, "logoutUrl": null, "redirectUris": [ "http://localhost:19876/mcp/oauth/callback", "https://dev.partner.example.com/app/mcp-auth-callback", "http://localhost:5173/mcp-auth-callback", "https://stage.partner.example.com/app/mcp-auth-callback", "https://next.partner.example.com/app/mcp-auth-callback", "https://chat.lab.example.com/auth/toolset-signin", "https://global.consent.azure-apim.net/redirect/example-20app-20connector-5f998f9c4d-28edd1166c07dd9d", "https://europe.token.botframework.com/.auth/web/redirect", "https://token.botframework.com/.auth/web/redirect", "https://global.consent.azure-apim.net/redirect/example-2dapp-2dmcp-5ff4e77a5f9f7799e1-5f37921915678c3f1c" ], "implicitGrantSettings": { "enableAccessTokenIssuance": true, "enableIdTokenIssuance": true }, "redirectUriSettings": [ { "uri": "http://localhost:19876/mcp/oauth/callback", "index": null }, { "uri": "https://dev.partner.example.com/app/mcp-auth-callback", "index": null }, { "uri": "http://localhost:5173/mcp-auth-callback", "index": null }, { "uri": "https://stage.partner.example.com/app/mcp-auth-callback", "index": null }, { "uri": "https://next.partner.example.com/app/mcp-auth-callback", "index": null }, { "uri": "https://chat.lab.example.com/auth/toolset-signin", "index": null }, { "uri": "https://global.consent.azure-apim.net/redirect/example-20app-20connector-5f998f9c4d-28edd1166c07dd9d", "index": null }, { "uri": "https://europe.token.botframework.com/.auth/web/redirect", "index": null }, { "uri": "https://token.botframework.com/.auth/web/redirect", "index": null }, { "uri": "https://global.consent.azure-apim.net/redirect/example-2dapp-2dmcp-5ff4e77a5f9f7799e1-5f37921915678c3f1c", "index": null } ] }, "servicePrincipalLockConfiguration": { "isEnabled": true, "allProperties": true, "credentialsWithUsageVerify": true, "credentialsWithUsageSign": true, "identifierUris": false, "tokenEncryptionKeyId": true }, "spa": { "redirectUris": [] } }DavidParks8 commentedon Mar 20, 2026
@sergey-tihon there are a number of things that can be done to tighten up the security of that app manifest, but I'm going to focus specifically on the issue we are discussing. The resource parameter validation in the V2 endpoint of Entra sources the allowed values from the identifier URIs and not the redirect URIs. You would need to add the http uri that is being passed by opencode in the resource parameter to you identifier uris array.
BTW, there is already a PR internally to relax the trailing /issue in resource parameter validation.
sergey-tihon commentedon Mar 27, 2026
@DavidParks8 How long does such a fix usually take before we can test it?
Thank you for taking a look at the manifest. I’d appreciate it if you could share your thoughts by email at sergey.tihon[at]gmail.com.
sergey-tihon commentedon Apr 1, 2026
I’ve fixed my issue. Here is the solution that worked for me:
resourcename in.well-known/oauth-protected-resourcefromhttps://app.example.com/mcp/tohttps://app.example.com/mcp(remove the trailing slash)resourcename to theidentifierUrislist in the MCP server app manifest."isFallbackPublicClient": true"publicClient": { "redirectUris": [ "http://127.0.0.1:19876/mcp/oauth/callback" ] }The final version of the MCP configuration
sergey-tihon commentedon May 4, 2026
hi, @DavidParks8 Has this been merged or released yet?
rekram1-node commentedon Jun 24, 2026
Closing as resolved by the configuration documented in this thread. The working setup uses an exact protected-resource URI registered in Entra
identifierUris, a public PKCE client, and the OpenCode loopback callback under Mobile and desktop applications. OpenCode intentionally retains RFC 8707 resource indicators; removing them, as proposed in #12321, would weaken audience binding.