@@ -71,6 +71,19 @@ export interface OAuthClientProvider {
71
71
* the authorization result.
72
72
*/
73
73
codeVerifier ( ) : string | Promise < string > ;
74
+
75
+ /**
76
+ * The resource to be used for the current session.
77
+ *
78
+ * Implements RFC 8707 Resource Indicators.
79
+ *
80
+ * This is placed in the provider to ensure the strong binding between tokens
81
+ * and their intended resource throughout the authorization session.
82
+ *
83
+ * This method is optional and only needs to be implemented if using
84
+ * Resource Indicators (RFC 8707).
85
+ */
86
+ resource ?( ) : string | undefined ;
74
87
}
75
88
76
89
export type AuthResult = "AUTHORIZED" | "REDIRECT" ;
@@ -142,6 +155,7 @@ export async function auth(
142
155
authorizationCode,
143
156
codeVerifier,
144
157
redirectUri : provider . redirectUrl ,
158
+ resource : provider . resource ?.( ) ,
145
159
} ) ;
146
160
147
161
await provider . saveTokens ( tokens ) ;
@@ -158,6 +172,7 @@ export async function auth(
158
172
metadata,
159
173
clientInformation,
160
174
refreshToken : tokens . refresh_token ,
175
+ resource : provider . resource ?.( ) ,
161
176
} ) ;
162
177
163
178
await provider . saveTokens ( newTokens ) ;
@@ -170,12 +185,20 @@ export async function auth(
170
185
const state = provider . state ? await provider . state ( ) : undefined ;
171
186
172
187
// Start new authorization flow
173
- const { authorizationUrl, codeVerifier } = await startAuthorization ( authorizationServerUrl , {
188
+
189
+ const resource = provider . resource ?.( ) ;
190
+ const { authorizationUrl, codeVerifier } = await startAuthorization ( authorizationServerUrl , {
174
191
metadata,
175
192
clientInformation,
176
193
state,
177
194
redirectUrl : provider . redirectUrl ,
178
195
scope : scope || provider . clientMetadata . scope ,
196
+ /**
197
+ * Although RFC 8707 supports multiple resources, we currently only support
198
+ * a single resource per auth session to maintain a 1:1 token-resource binding
199
+ * based on current auth flow implementation
200
+ */
201
+ resources : resource ? [ resource ] : undefined ,
179
202
} ) ;
180
203
181
204
await provider . saveCodeVerifier ( codeVerifier ) ;
@@ -310,13 +333,20 @@ export async function startAuthorization(
310
333
redirectUrl,
311
334
scope,
312
335
state,
336
+ resources,
313
337
} : {
314
338
metadata ?: OAuthMetadata ;
315
339
clientInformation : OAuthClientInformation ;
316
340
redirectUrl : string | URL ;
317
341
scope ?: string ;
318
342
state ?: string ;
319
- } ,
343
+ /**
344
+ * Array type to align with RFC 8707 which supports multiple resources,
345
+ * making it easier to extend for multiple resource indicators in the future
346
+ * (though current implementation only uses a single resource)
347
+ */
348
+ resources ?: string [ ] ;
349
+ }
320
350
) : Promise < { authorizationUrl : URL ; codeVerifier : string } > {
321
351
const responseType = "code" ;
322
352
const codeChallengeMethod = "S256" ;
@@ -365,6 +395,12 @@ export async function startAuthorization(
365
395
authorizationUrl . searchParams . set ( "scope" , scope ) ;
366
396
}
367
397
398
+ if ( resources ?. length ) {
399
+ for ( const resource of resources ) {
400
+ authorizationUrl . searchParams . append ( "resource" , resource ) ;
401
+ }
402
+ }
403
+
368
404
return { authorizationUrl, codeVerifier } ;
369
405
}
370
406
@@ -379,13 +415,15 @@ export async function exchangeAuthorization(
379
415
authorizationCode,
380
416
codeVerifier,
381
417
redirectUri,
418
+ resource,
382
419
} : {
383
420
metadata ?: OAuthMetadata ;
384
421
clientInformation : OAuthClientInformation ;
385
422
authorizationCode : string ;
386
423
codeVerifier : string ;
387
424
redirectUri : string | URL ;
388
- } ,
425
+ resource ?: string ;
426
+ }
389
427
) : Promise < OAuthTokens > {
390
428
const grantType = "authorization_code" ;
391
429
@@ -412,6 +450,7 @@ export async function exchangeAuthorization(
412
450
code : authorizationCode ,
413
451
code_verifier : codeVerifier ,
414
452
redirect_uri : String ( redirectUri ) ,
453
+ ...( resource ? { resource } : { } ) ,
415
454
} ) ;
416
455
417
456
if ( clientInformation . client_secret ) {
@@ -442,11 +481,13 @@ export async function refreshAuthorization(
442
481
metadata,
443
482
clientInformation,
444
483
refreshToken,
484
+ resource,
445
485
} : {
446
486
metadata ?: OAuthMetadata ;
447
487
clientInformation : OAuthClientInformation ;
448
488
refreshToken : string ;
449
- } ,
489
+ resource ?: string ;
490
+ }
450
491
) : Promise < OAuthTokens > {
451
492
const grantType = "refresh_token" ;
452
493
@@ -471,6 +512,7 @@ export async function refreshAuthorization(
471
512
grant_type : grantType ,
472
513
client_id : clientInformation . client_id ,
473
514
refresh_token : refreshToken ,
515
+ ...( resource ? { resource } : { } ) ,
474
516
} ) ;
475
517
476
518
if ( clientInformation . client_secret ) {
0 commit comments