@@ -368,61 +368,67 @@ export async function startAuthorization(
368
368
return { authorizationUrl, codeVerifier } ;
369
369
}
370
370
371
- /**
372
- * Exchanges an authorization code for an access token with the given server.
373
- */
374
371
export async function exchangeAuthorization (
375
- authorizationServerUrl : string | URL ,
376
- {
377
- metadata,
378
- clientInformation,
379
- authorizationCode,
380
- codeVerifier,
381
- redirectUri,
382
- } : {
383
- metadata ?: OAuthMetadata ;
384
- clientInformation : OAuthClientInformation ;
385
- authorizationCode : string ;
386
- codeVerifier : string ;
387
- redirectUri : string | URL ;
388
- } ,
372
+ authorizationServerUrl : string | URL ,
373
+ {
374
+ metadata,
375
+ clientInformation,
376
+ authorizationCode,
377
+ codeVerifier,
378
+ redirectUri,
379
+ } : {
380
+ metadata ?: OAuthMetadata ;
381
+ clientInformation : OAuthClientInformation ;
382
+ authorizationCode : string ;
383
+ codeVerifier : string ;
384
+ redirectUri : string | URL ;
385
+ } ,
389
386
) : Promise < OAuthTokens > {
390
387
const grantType = "authorization_code" ;
391
388
392
- let tokenUrl : URL ;
393
- if ( metadata ) {
394
- tokenUrl = new URL ( metadata . token_endpoint ) ;
389
+ const tokenUrl = metadata ?. token_endpoint
390
+ ? new URL ( metadata . token_endpoint )
391
+ : new URL ( "/token" , authorizationServerUrl ) ;
395
392
396
- if (
397
- metadata . grant_types_supported &&
393
+ if (
394
+ metadata ? .grant_types_supported &&
398
395
! metadata . grant_types_supported . includes ( grantType )
399
- ) {
400
- throw new Error (
396
+ ) {
397
+ throw new Error (
401
398
`Incompatible auth server: does not support grant type ${ grantType } ` ,
402
- ) ;
403
- }
404
- } else {
405
- tokenUrl = new URL ( "/token" , authorizationServerUrl ) ;
399
+ ) ;
406
400
}
407
401
408
- // Exchange code for tokens
402
+ const headers : HeadersInit = {
403
+ "Content-Type" : "application/x-www-form-urlencoded" ,
404
+ } ;
405
+
409
406
const params = new URLSearchParams ( {
410
407
grant_type : grantType ,
411
- client_id : clientInformation . client_id ,
412
408
code : authorizationCode ,
413
409
code_verifier : codeVerifier ,
414
410
redirect_uri : String ( redirectUri ) ,
415
411
} ) ;
416
412
417
- if ( clientInformation . client_secret ) {
418
- params . set ( "client_secret" , clientInformation . client_secret ) ;
413
+ const { client_id, client_secret } = clientInformation ;
414
+ const supportedMethods =
415
+ metadata ?. token_endpoint_auth_methods_supported ?? [ ] ;
416
+
417
+ const useBasicAuth = ! ! client_secret && supportedMethods . includes ( "client_secret_basic" ) ;
418
+
419
+ if ( client_secret && useBasicAuth ) {
420
+ headers [ "Authorization" ] = `Basic ${ Buffer . from ( `${ client_id } :${ client_secret } ` ) . toString ( "base64" ) } ` ;
421
+ // Do not put credentials in body
422
+ } else {
423
+ params . set ( "client_id" , client_id ) ;
424
+ if ( client_secret ) {
425
+ params . set ( "client_secret" , client_secret ) ;
426
+ }
419
427
}
420
428
421
429
const response = await fetch ( tokenUrl , {
422
430
method : "POST" ,
423
- headers : {
424
- "Content-Type" : "application/x-www-form-urlencoded" ,
425
- } ,
431
+ headers,
426
432
body : params ,
427
433
} ) ;
428
434
@@ -433,6 +439,7 @@ export async function exchangeAuthorization(
433
439
return OAuthTokensSchema . parse ( await response . json ( ) ) ;
434
440
}
435
441
442
+
436
443
/**
437
444
* Exchange a refresh token for an updated access token.
438
445
*/
0 commit comments