@@ -14,6 +14,10 @@ import {
1414 type OAuth2ErrorResponse
1515} from './schemas' ;
1616
17+ // Default scopes when client doesn't provide scope parameter
18+ // This matches scopes_supported in discovery endpoints
19+ const DEFAULT_OAUTH_SCOPES = 'mcp:read mcp:tools:execute offline_access' ;
20+
1721// Reusable Schema Constants
1822const AUTHORIZATION_QUERY_SCHEMA = {
1923 type : 'object' ,
@@ -32,17 +36,21 @@ const AUTHORIZATION_QUERY_SCHEMA = {
3236 } ,
3337 scope : {
3438 ...SCOPE_SCHEMA ,
35- description : 'Space-separated list of requested scopes'
39+ description : 'Space-separated list of requested scopes (optional, defaults to all supported scopes) '
3640 } ,
3741 state : STATE_SCHEMA ,
3842 code_challenge : CODE_CHALLENGE_SCHEMA ,
3943 code_challenge_method : CODE_CHALLENGE_METHOD_SCHEMA ,
4044 team : {
4145 type : 'string' ,
4246 description : 'Team ID for team-scoped OAuth flow (optional, defaults to user team)'
47+ } ,
48+ resource : {
49+ type : 'string' ,
50+ description : 'RFC 8707 Resource Indicator - target MCP server URI (optional, used for audience binding)'
4351 }
4452 } ,
45- required : [ 'response_type' , 'client_id' , 'redirect_uri' , 'scope' , ' state', 'code_challenge' , 'code_challenge_method' ] ,
53+ required : [ 'response_type' , 'client_id' , 'redirect_uri' , 'state' , 'code_challenge' , 'code_challenge_method' ] ,
4654 additionalProperties : false
4755} as const ;
4856
@@ -51,11 +59,12 @@ interface AuthorizationQuery {
5159 response_type : 'code' ;
5260 client_id : string ;
5361 redirect_uri : string ;
54- scope : string ;
62+ scope ? : string ;
5563 state : string ;
5664 code_challenge : string ;
5765 code_challenge_method : 'S256' ;
5866 team ?: string ;
67+ resource ?: string ;
5968}
6069
6170export default async function authorizationRoute ( server : FastifyInstance ) {
@@ -70,15 +79,16 @@ export default async function authorizationRoute(server: FastifyInstance) {
7079 properties : {
7180 client_id : { type : 'string' } ,
7281 redirect_uri : { type : 'string' } ,
73- scope : { type : 'string' } ,
82+ scope : { type : 'string' , description : 'Optional, defaults to all supported scopes' } ,
7483 state : { type : 'string' } ,
7584 code_challenge : { type : 'string' } ,
7685 code_challenge_method : { type : 'string' } ,
7786 response_type : { type : 'string' } ,
7887 team_id : { type : 'string' } ,
79- consent : { type : 'string' , enum : [ 'true' , 'false' ] }
88+ consent : { type : 'string' , enum : [ 'true' , 'false' ] } ,
89+ resource : { type : 'string' , description : 'RFC 8707 Resource Indicator (optional)' }
8090 } ,
81- required : [ 'client_id' , 'redirect_uri' , 'scope' , ' state', 'code_challenge' , 'code_challenge_method' , 'response_type' , 'team_id' , 'consent' ] ,
91+ required : [ 'client_id' , 'redirect_uri' , 'state' , 'code_challenge' , 'code_challenge_method' , 'response_type' , 'team_id' , 'consent' ] ,
8292 additionalProperties : false
8393 } ,
8494 response : {
@@ -94,17 +104,22 @@ export default async function authorizationRoute(server: FastifyInstance) {
94104 }
95105 } , async ( request , reply ) => {
96106 try {
97- const { client_id , redirect_uri , scope , state , code_challenge , code_challenge_method , response_type , team_id , consent } = request . body as {
107+ const body = request . body as {
98108 client_id : string ;
99109 redirect_uri : string ;
100- scope : string ;
110+ scope ? : string ;
101111 state : string ;
102112 code_challenge : string ;
103113 code_challenge_method : string ;
104114 response_type : string ;
105115 team_id : string ;
106116 consent : string ;
117+ resource ?: string ;
107118 } ;
119+ const { client_id, redirect_uri, state, code_challenge, code_challenge_method, response_type, team_id, consent } = body ;
120+
121+ // Use default scopes if not provided
122+ const scope = body . scope || DEFAULT_OAUTH_SCOPES ;
108123
109124 // Check user authentication
110125 if ( ! request . user ) {
@@ -242,16 +257,29 @@ export default async function authorizationRoute(server: FastifyInstance) {
242257 }
243258 } , async ( request , reply ) => {
244259 try {
260+ const query = request . query as AuthorizationQuery ;
245261 const {
246262 response_type,
247263 client_id,
248264 redirect_uri,
249- scope,
250265 state,
251266 code_challenge,
252267 code_challenge_method,
253- team
254- } = request . query as AuthorizationQuery ;
268+ team,
269+ resource
270+ } = query ;
271+
272+ // Use default scopes if not provided (MCP clients may not send scope)
273+ const scope = query . scope || DEFAULT_OAUTH_SCOPES ;
274+
275+ if ( ! query . scope ) {
276+ request . log . debug ( {
277+ operation : 'oauth2_authorization' ,
278+ clientId : client_id ,
279+ defaultScope : scope ,
280+ resource,
281+ } , 'No scope provided, using default MCP scopes' ) ;
282+ }
255283
256284 // Check if user is authenticated first
257285 if ( ! request . user ) {
0 commit comments