@@ -17,7 +17,7 @@ import {
1717} from '../../src/client/auth.js' ;
1818import { createPrivateKeyJwtAuth } from '../../src/client/auth-extensions.js' ;
1919import { InvalidClientMetadataError , ServerError } from '../../src/server/auth/errors.js' ;
20- import { AuthorizationServerMetadata , OAuthTokens } from '../../src/shared/auth.js' ;
20+ import { AuthorizationServerMetadata , OAuthClientMetadata , OAuthTokens } from '../../src/shared/auth.js' ;
2121import { expect , vi , type Mock } from 'vitest' ;
2222
2323// Mock pkce-challenge
@@ -1873,6 +1873,43 @@ describe('OAuth Authorization', () => {
18731873 ) ;
18741874 } ) ;
18751875
1876+ it ( 'includes scope in registration body when provided, overriding clientMetadata.scope' , async ( ) => {
1877+ const clientMetadataWithScope : OAuthClientMetadata = {
1878+ ...validClientMetadata ,
1879+ scope : 'should-be-overridden'
1880+ } ;
1881+
1882+ const expectedClientInfo = {
1883+ ...validClientInfo ,
1884+ scope : 'openid profile'
1885+ } ;
1886+
1887+ mockFetch . mockResolvedValueOnce ( {
1888+ ok : true ,
1889+ status : 200 ,
1890+ json : async ( ) => expectedClientInfo
1891+ } ) ;
1892+
1893+ const clientInfo = await registerClient ( 'https://auth.example.com' , {
1894+ clientMetadata : clientMetadataWithScope ,
1895+ scope : 'openid profile'
1896+ } ) ;
1897+
1898+ expect ( clientInfo ) . toEqual ( expectedClientInfo ) ;
1899+ expect ( mockFetch ) . toHaveBeenCalledWith (
1900+ expect . objectContaining ( {
1901+ href : 'https://auth.example.com/register'
1902+ } ) ,
1903+ expect . objectContaining ( {
1904+ method : 'POST' ,
1905+ headers : {
1906+ 'Content-Type' : 'application/json'
1907+ } ,
1908+ body : JSON . stringify ( { ...validClientMetadata , scope : 'openid profile' } )
1909+ } )
1910+ ) ;
1911+ } ) ;
1912+
18761913 it ( 'validates client information response schema' , async ( ) => {
18771914 mockFetch . mockResolvedValueOnce ( {
18781915 ok : true ,
@@ -2746,6 +2783,12 @@ describe('OAuth Authorization', () => {
27462783 const redirectCall = ( mockProvider . redirectToAuthorization as Mock ) . mock . calls [ 0 ] ;
27472784 const authUrl : URL = redirectCall [ 0 ] ;
27482785 expect ( authUrl . searchParams . get ( 'scope' ) ) . toBe ( 'mcp:read mcp:write mcp:admin' ) ;
2786+
2787+ // Verify the same scope was also used in the DCR request body
2788+ const registerCall = mockFetch . mock . calls . find ( call => call [ 0 ] . toString ( ) . includes ( '/register' ) ) ;
2789+ expect ( registerCall ) . toBeDefined ( ) ;
2790+ const registerBody = JSON . parse ( registerCall ! [ 1 ] . body ) ;
2791+ expect ( registerBody . scope ) . toBe ( 'mcp:read mcp:write mcp:admin' ) ;
27492792 } ) ;
27502793
27512794 it ( 'prefers explicit scope parameter over scopes_supported from PRM' , async ( ) => {
0 commit comments