@@ -69,3 +69,92 @@ export class AuthBasicMetadataVar1Scenario implements Scenario {
6969 return this . checks ;
7070 }
7171}
72+
73+ export class AuthBasicMetadataVar2Scenario implements Scenario {
74+ // TODO: name should match what we put in the scenario map
75+ name = 'auth/basic-metadata-var2' ;
76+ description =
77+ 'Tests Basic OAuth flow with DCR, PRM at root location, OAuth metadata at path-based OAuth discovery path' ;
78+ private authServer = new ServerLifecycle ( ) ;
79+ private server = new ServerLifecycle ( ) ;
80+ private checks : ConformanceCheck [ ] = [ ] ;
81+
82+ async start ( ) : Promise < ScenarioUrls > {
83+ this . checks = [ ] ;
84+
85+ const authApp = createAuthServer ( this . checks , this . authServer . getUrl , {
86+ metadataPath : '/tenant1/.well-known/openid-configuration' ,
87+ isOpenIdConfiguration : true ,
88+ routePrefix : '/tenant1'
89+ } ) ;
90+
91+ authApp . get ( '/.well-known/oauth-authorization-server' , ( req , res ) => {
92+ this . checks . push ( {
93+ id : 'authorization-server-metadata-wrong-path' ,
94+ name : 'AuthorizationServerMetadataWrongPath' ,
95+ description :
96+ 'Client requested authorization server at the root path when the AS URL has a path-based location' ,
97+ status : 'FAILURE' ,
98+ timestamp : new Date ( ) . toISOString ( ) ,
99+ specReferences : [
100+ {
101+ id : 'RFC-8414' ,
102+ url : 'https://tools.ietf.org/html/rfc8414'
103+ }
104+ ] ,
105+ details : {
106+ url : req . url
107+ }
108+ } ) ;
109+ res . status ( 404 ) . send ( 'Not Found' ) ;
110+ } ) ;
111+
112+ await this . authServer . start ( authApp ) ;
113+
114+ const app = createServer (
115+ this . checks ,
116+ this . server . getUrl ,
117+ ( ) => `${ this . authServer . getUrl ( ) } /tenant1` ,
118+ {
119+ // TODO: this will put this path in the WWW-Authenticate header
120+ // but RFC 9728 states that in that case, the resource in the PRM
121+ // must match the URL used to make the request to the resource server.
122+ // We'll need to establish an opinion on whether that means the
123+ // URL for the metadata fetch, or the URL for the MCP endpoint,
124+ // or more generally what are the valid scenarios / combos.
125+ prmPath : '/.well-known/oauth-protected-resource'
126+ }
127+ ) ;
128+ await this . server . start ( app ) ;
129+
130+ return { serverUrl : `${ this . server . getUrl ( ) } /mcp` } ;
131+ }
132+
133+ async stop ( ) {
134+ await this . authServer . stop ( ) ;
135+ await this . server . stop ( ) ;
136+ }
137+
138+ getChecks ( ) : ConformanceCheck [ ] {
139+ const expectedSlugs = [
140+ 'authorization-server-metadata' ,
141+ 'client-registration' ,
142+ 'authorization-request' ,
143+ 'token-request'
144+ ] ;
145+
146+ for ( const slug of expectedSlugs ) {
147+ if ( ! this . checks . find ( ( c ) => c . id === slug ) ) {
148+ this . checks . push ( {
149+ id : slug ,
150+ name : `Expected Check Missing: ${ slug } ` ,
151+ description : `Expected Check Missing: ${ slug } ` ,
152+ status : 'FAILURE' ,
153+ timestamp : new Date ( ) . toISOString ( )
154+ } ) ;
155+ }
156+ }
157+
158+ return this . checks ;
159+ }
160+ }
0 commit comments