@@ -236,7 +236,7 @@ export interface OAuthClientProvider {
236236 *
237237 * Implementations must verify the returned resource matches the MCP server.
238238 */
239- validateResourceURL ?( serverUrl : string | URL , resource ?: string ) : Promise < URL | undefined > ;
239+ validateResourceURL ?( serverUrl : string | URL , resource ?: string ) : Promise < OAuthResourceIndicator | undefined > ;
240240
241241 /**
242242 * If implemented, provides a way for the client to invalidate (e.g. delete) the specified
@@ -384,6 +384,11 @@ function isClientAuthMethod(method: string): method is ClientAuthMethod {
384384
385385const AUTHORIZATION_CODE_RESPONSE_TYPE = 'code' ;
386386const AUTHORIZATION_CODE_CHALLENGE_METHOD = 'S256' ;
387+ type OAuthResourceIndicator = string | URL ;
388+
389+ function serializeResourceIndicator ( resource : OAuthResourceIndicator ) : string {
390+ return typeof resource === 'string' ? resource : resource . href ;
391+ }
387392
388393/**
389394 * Determines the best client authentication method to use based on server support and client configuration.
@@ -684,11 +689,11 @@ async function authInternal(
684689 // Save authorization server URL for providers that need it (e.g., CrossAppAccessProvider)
685690 await provider . saveAuthorizationServerUrl ?.( String ( authorizationServerUrl ) ) ;
686691
687- const resource : URL | undefined = await selectResourceURL ( serverUrl , provider , resourceMetadata ) ;
692+ const resource : OAuthResourceIndicator | undefined = await selectResourceURL ( serverUrl , provider , resourceMetadata ) ;
688693
689694 // Save resource URL for providers that need it (e.g., CrossAppAccessProvider)
690695 if ( resource ) {
691- await provider . saveResourceUrl ?.( String ( resource ) ) ;
696+ await provider . saveResourceUrl ?.( serializeResourceIndicator ( resource ) ) ;
692697 }
693698
694699 // Scope selection used consistently for DCR and the authorization request.
@@ -844,7 +849,7 @@ export async function selectResourceURL(
844849 serverUrl : string | URL ,
845850 provider : OAuthClientProvider ,
846851 resourceMetadata ?: OAuthProtectedResourceMetadata
847- ) : Promise < URL | undefined > {
852+ ) : Promise < OAuthResourceIndicator | undefined > {
848853 const defaultResource = resourceUrlFromServerUrl ( serverUrl ) ;
849854
850855 // If provider has custom validation, delegate to it
@@ -861,8 +866,9 @@ export async function selectResourceURL(
861866 if ( ! checkResourceAllowed ( { requestedResource : defaultResource , configuredResource : resourceMetadata . resource } ) ) {
862867 throw new Error ( `Protected resource ${ resourceMetadata . resource } does not match expected ${ defaultResource } (or origin)` ) ;
863868 }
864- // Prefer the resource from metadata since it's what the server is telling us to request
865- return new URL ( resourceMetadata . resource ) ;
869+ // Prefer the exact resource indicator from metadata since it's what the server is telling us to request.
870+ // Constructing a URL would normalize pathless origins by appending "/", which can change the audience.
871+ return resourceMetadata . resource ;
866872}
867873
868874/**
@@ -1376,7 +1382,7 @@ export async function startAuthorization(
13761382 redirectUrl : string | URL ;
13771383 scope ?: string ;
13781384 state ?: string ;
1379- resource ?: URL ;
1385+ resource ?: OAuthResourceIndicator ;
13801386 }
13811387) : Promise < { authorizationUrl : URL ; codeVerifier : string } > {
13821388 let authorizationUrl : URL ;
@@ -1424,7 +1430,7 @@ export async function startAuthorization(
14241430 }
14251431
14261432 if ( resource ) {
1427- authorizationUrl . searchParams . set ( 'resource' , resource . href ) ;
1433+ authorizationUrl . searchParams . set ( 'resource' , serializeResourceIndicator ( resource ) ) ;
14281434 }
14291435
14301436 return { authorizationUrl, codeVerifier } ;
@@ -1472,7 +1478,7 @@ export async function executeTokenRequest(
14721478 tokenRequestParams : URLSearchParams ;
14731479 clientInformation ?: OAuthClientInformationMixed ;
14741480 addClientAuthentication ?: OAuthClientProvider [ 'addClientAuthentication' ] ;
1475- resource ?: URL ;
1481+ resource ?: OAuthResourceIndicator ;
14761482 fetchFn ?: FetchLike ;
14771483 }
14781484) : Promise < OAuthTokens > {
@@ -1484,7 +1490,7 @@ export async function executeTokenRequest(
14841490 } ) ;
14851491
14861492 if ( resource ) {
1487- tokenRequestParams . set ( 'resource' , resource . href ) ;
1493+ tokenRequestParams . set ( 'resource' , serializeResourceIndicator ( resource ) ) ;
14881494 }
14891495
14901496 if ( addClientAuthentication ) {
@@ -1548,7 +1554,7 @@ export async function exchangeAuthorization(
15481554 authorizationCode : string ;
15491555 codeVerifier : string ;
15501556 redirectUri : string | URL ;
1551- resource ?: URL ;
1557+ resource ?: OAuthResourceIndicator ;
15521558 addClientAuthentication ?: OAuthClientProvider [ 'addClientAuthentication' ] ;
15531559 fetchFn ?: FetchLike ;
15541560 }
@@ -1590,7 +1596,7 @@ export async function refreshAuthorization(
15901596 metadata ?: AuthorizationServerMetadata ;
15911597 clientInformation : OAuthClientInformationMixed ;
15921598 refreshToken : string ;
1593- resource ?: URL ;
1599+ resource ?: OAuthResourceIndicator ;
15941600 addClientAuthentication ?: OAuthClientProvider [ 'addClientAuthentication' ] ;
15951601 fetchFn ?: FetchLike ;
15961602 }
@@ -1651,7 +1657,7 @@ export async function fetchToken(
16511657 fetchFn
16521658 } : {
16531659 metadata ?: AuthorizationServerMetadata ;
1654- resource ?: URL ;
1660+ resource ?: OAuthResourceIndicator ;
16551661 /** Authorization code for the default `authorization_code` grant flow */
16561662 authorizationCode ?: string ;
16571663 /** Optional scope parameter from auth() options */
0 commit comments