33 startAuthorization ,
44 exchangeAuthorization ,
55 refreshAuthorization ,
6- registerClient ,
76 discoverOAuthProtectedResourceMetadata ,
87 extractResourceMetadataUrl ,
98 auth ,
@@ -17,6 +16,7 @@ global.fetch = mockFetch;
1716describe ( "OAuth Authorization" , ( ) => {
1817 beforeEach ( ( ) => {
1918 mockFetch . mockReset ( ) ;
19+ jest . clearAllMocks ( ) ;
2020 } ) ;
2121
2222 describe ( "extractResourceMetadataUrl" , ( ) => {
@@ -626,89 +626,93 @@ describe("OAuth Authorization", () => {
626626 } ) ;
627627
628628 describe ( "registerClient" , ( ) => {
629- const validClientMetadata = {
630- redirect_uris : [ "http://localhost:3000/callback" ] ,
631- client_name : "Test Client" ,
629+ // ...existing tests...
630+ } ) ;
631+
632+ describe ( "auth" , ( ) => {
633+ // Patch pkce-challenge to return deterministic values
634+ jest . mock ( "pkce-challenge" , ( ) => ( ) => ( {
635+ code_verifier : "test_verifier" ,
636+ code_challenge : "test_challenge" ,
637+ } ) ) ;
638+
639+ const validMetadata = {
640+ issuer : "https://auth.example.com" ,
641+ authorization_endpoint : "https://auth.example.com/authorize" ,
642+ token_endpoint : "https://auth.example.com/token" ,
643+ registration_endpoint : "https://auth.example.com/register" ,
644+ response_types_supported : [ "code" ] ,
645+ code_challenge_methods_supported : [ "S256" ] ,
632646 } ;
633647
634648 const validClientInfo = {
635649 client_id : "client123" ,
636650 client_secret : "secret123" ,
637- client_id_issued_at : 1612137600 ,
638- client_secret_expires_at : 1612224000 ,
639- ...validClientMetadata ,
651+ redirect_uris : [ "http://localhost:3000/callback" ] ,
652+ client_name : "Test Client" ,
640653 } ;
641654
642- it ( "registers client and returns client information" , async ( ) => {
643- mockFetch . mockResolvedValueOnce ( {
644- ok : true ,
645- status : 200 ,
646- json : async ( ) => validClientInfo ,
647- } ) ;
648-
649- const clientInfo = await registerClient ( "https://auth.example.com" , {
650- clientMetadata : validClientMetadata ,
651- } ) ;
652-
653- expect ( clientInfo ) . toEqual ( validClientInfo ) ;
654- expect ( mockFetch ) . toHaveBeenCalledWith (
655- expect . objectContaining ( {
656- href : "https://auth.example.com/register" ,
657- } ) ,
658- expect . objectContaining ( {
659- method : "POST" ,
660- headers : {
661- "Content-Type" : "application/json" ,
662- } ,
663- body : JSON . stringify ( validClientMetadata ) ,
664- } )
665- ) ;
666- } ) ;
667-
668- it ( "validates client information response schema" , async ( ) => {
669- mockFetch . mockResolvedValueOnce ( {
670- ok : true ,
671- status : 200 ,
672- json : async ( ) => ( {
673- // Missing required fields
674- client_secret : "secret123" ,
675- } ) ,
676- } ) ;
677-
678- await expect (
679- registerClient ( "https://auth.example.com" , {
680- clientMetadata : validClientMetadata ,
681- } )
682- ) . rejects . toThrow ( ) ;
683- } ) ;
684-
685- it ( "throws when registration endpoint not available in metadata" , async ( ) => {
686- const metadata = {
687- issuer : "https://auth.example.com" ,
688- authorization_endpoint : "https://auth.example.com/authorize" ,
689- token_endpoint : "https://auth.example.com/token" ,
690- response_types_supported : [ "code" ] ,
655+ it ( "uses scope from registered client information if not present in clientMetadata" , async ( ) => {
656+ // Mock fetch for metadata discovery and registration
657+ mockFetch
658+ . mockResolvedValueOnce ( {
659+ ok : true ,
660+ status : 200 ,
661+ json : async ( ) => ( { } ) ,
662+ } ) // protected resource metadata
663+ . mockResolvedValueOnce ( {
664+ ok : true ,
665+ status : 200 ,
666+ json : async ( ) => validMetadata ,
667+ } ) // discovery
668+ . mockResolvedValueOnce ( {
669+ ok : true ,
670+ status : 200 ,
671+ json : async ( ) => ( {
672+ ...validClientInfo ,
673+ scope : "dynamic scope from registration" ,
674+ } ) ,
675+ } ) ; // registration
676+
677+ // Provider: clientInformation returns undefined first, then fullInformation after registration
678+ const fullInformation = {
679+ ...validClientInfo ,
680+ scope : "dynamic scope from registration" ,
681+ } ;
682+ const clientInformationMock = jest
683+ . fn ( )
684+ . mockResolvedValueOnce ( undefined )
685+ . mockResolvedValueOnce ( fullInformation ) ;
686+
687+ const provider = {
688+ get redirectUrl ( ) {
689+ return "http://localhost:3000/callback" ;
690+ } ,
691+ get clientMetadata ( ) {
692+ // No scope here!
693+ return validClientInfo ;
694+ } ,
695+ clientInformation : clientInformationMock ,
696+ tokens : jest . fn ( ) . mockResolvedValue ( undefined ) ,
697+ saveTokens : jest . fn ( ) ,
698+ saveCodeVerifier : jest . fn ( ) ,
699+ codeVerifier : jest . fn ( ) ,
700+ redirectToAuthorization : jest . fn ( ) ,
701+ saveClientInformation : jest . fn ( ) ,
691702 } ;
692703
693- await expect (
694- registerClient ( "https://auth.example.com" , {
695- metadata,
696- clientMetadata : validClientMetadata ,
697- } )
698- ) . rejects . toThrow ( / d o e s n o t s u p p o r t d y n a m i c c l i e n t r e g i s t r a t i o n / ) ;
699- } ) ;
700-
701- it ( "throws on error response" , async ( ) => {
702- mockFetch . mockResolvedValueOnce ( {
703- ok : false ,
704- status : 400 ,
704+ await auth ( provider , {
705+ serverUrl : "https://auth.example.com" ,
705706 } ) ;
706707
707- await expect (
708- registerClient ( "https://auth.example.com" , {
709- clientMetadata : validClientMetadata ,
710- } )
711- ) . rejects . toThrow ( "Dynamic client registration failed" ) ;
708+ // Check saveClientInformation was called with fullInformation
709+ expect ( provider . saveClientInformation ) . toHaveBeenCalledWith ( fullInformation ) ;
710+
711+ // Check that redirectToAuthorization was called with a URL containing the correct scope from registration
712+ expect ( provider . redirectToAuthorization ) . toHaveBeenCalledTimes ( 1 ) ;
713+ const urlArg = provider . redirectToAuthorization . mock . calls [ 0 ] [ 0 ] ;
714+ expect ( urlArg ) . toBeInstanceOf ( URL ) ;
715+ expect ( urlArg . searchParams . get ( "scope" ) ) . toBe ( "dynamic scope from registration" ) ;
712716 } ) ;
713717 } ) ;
714718
0 commit comments