@@ -4289,6 +4289,54 @@ describe('SEP-2352: authorization server binding', () => {
42894289 expect ( redirectUrl . searchParams . get ( 'client_id' ) ) . toBe ( 'new-client-id' ) ;
42904290 } ) ;
42914291
4292+ it ( 'refreshes cached discovery from an explicit resource metadata challenge before comparing authorization servers' , async ( ) => {
4293+ const { provider, invalidateCredentials, saveClientInformation, redirectToAuthorization } = createBoundProvider ( {
4294+ client_id : 'old-client-id' ,
4295+ client_secret : 'old-client-secret'
4296+ } ) ;
4297+ const resourceMetadataUrl = new URL ( 'https://resource.example.com/.well-known/oauth-protected-resource' ) ;
4298+
4299+ provider . discoveryState = vi . fn ( ) . mockResolvedValue ( {
4300+ authorizationServerUrl : oldAuthServerUrl ,
4301+ resourceMetadata : sameResourceMetadata ,
4302+ authorizationServerMetadata : sameAuthMetadata
4303+ } ) ;
4304+ provider . saveDiscoveryState = vi . fn ( ) ;
4305+
4306+ mockDiscoveryAndRegistration ( {
4307+ resourceMetadata : newResourceMetadata ,
4308+ authMetadata : newAuthMetadata ,
4309+ registeredClient : { client_id : 'new-client-id' , client_secret : 'new-client-secret' }
4310+ } ) ;
4311+
4312+ const result = await auth ( provider , {
4313+ serverUrl : 'https://resource.example.com' ,
4314+ resourceMetadataUrl
4315+ } ) ;
4316+
4317+ expect ( result ) . toBe ( 'REDIRECT' ) ;
4318+
4319+ const prmCalls = mockFetch . mock . calls . filter ( call => call [ 0 ] . toString ( ) . includes ( 'oauth-protected-resource' ) ) ;
4320+ expect ( prmCalls ) . toHaveLength ( 1 ) ;
4321+ expect ( prmCalls [ 0 ] ! [ 0 ] . toString ( ) ) . toBe ( resourceMetadataUrl . toString ( ) ) ;
4322+
4323+ expect ( provider . saveDiscoveryState ) . toHaveBeenCalledWith (
4324+ expect . objectContaining ( {
4325+ authorizationServerUrl : 'https://new-auth.example.com' ,
4326+ resourceMetadataUrl : resourceMetadataUrl . toString ( ) ,
4327+ resourceMetadata : newResourceMetadata ,
4328+ authorizationServerMetadata : newAuthMetadata
4329+ } )
4330+ ) ;
4331+ expect ( invalidateCredentials ) . toHaveBeenCalledWith ( 'client' ) ;
4332+ expect ( invalidateCredentials ) . toHaveBeenCalledWith ( 'tokens' ) ;
4333+ expect ( saveClientInformation ) . toHaveBeenCalledWith ( expect . objectContaining ( { client_id : 'new-client-id' } ) ) ;
4334+
4335+ const redirectUrl : URL = redirectToAuthorization . mock . calls [ 0 ] ! [ 0 ] ;
4336+ expect ( redirectUrl . origin ) . toBe ( 'https://new-auth.example.com' ) ;
4337+ expect ( redirectUrl . searchParams . get ( 'client_id' ) ) . toBe ( 'new-client-id' ) ;
4338+ } ) ;
4339+
42924340 it ( 'does not invalidate credentials when the authorization server is unchanged' , async ( ) => {
42934341 const { provider, invalidateCredentials, redirectToAuthorization } = createBoundProvider ( {
42944342 client_id : 'old-client-id' ,
0 commit comments