@@ -368,61 +368,67 @@ export async function startAuthorization(
368368 return { authorizationUrl, codeVerifier } ;
369369}
370370
371- /**
372- * Exchanges an authorization code for an access token with the given server.
373- */
374371export 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+ } ,
389386) : Promise < OAuthTokens > {
390387 const grantType = "authorization_code" ;
391388
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 ) ;
395392
396- if (
397- metadata . grant_types_supported &&
393+ if (
394+ metadata ? .grant_types_supported &&
398395 ! metadata . grant_types_supported . includes ( grantType )
399- ) {
400- throw new Error (
396+ ) {
397+ throw new Error (
401398 `Incompatible auth server: does not support grant type ${ grantType } ` ,
402- ) ;
403- }
404- } else {
405- tokenUrl = new URL ( "/token" , authorizationServerUrl ) ;
399+ ) ;
406400 }
407401
408- // Exchange code for tokens
402+ const headers : HeadersInit = {
403+ "Content-Type" : "application/x-www-form-urlencoded" ,
404+ } ;
405+
409406 const params = new URLSearchParams ( {
410407 grant_type : grantType ,
411- client_id : clientInformation . client_id ,
412408 code : authorizationCode ,
413409 code_verifier : codeVerifier ,
414410 redirect_uri : String ( redirectUri ) ,
415411 } ) ;
416412
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+ }
419427 }
420428
421429 const response = await fetch ( tokenUrl , {
422430 method : "POST" ,
423- headers : {
424- "Content-Type" : "application/x-www-form-urlencoded" ,
425- } ,
431+ headers,
426432 body : params ,
427433 } ) ;
428434
@@ -433,6 +439,7 @@ export async function exchangeAuthorization(
433439 return OAuthTokensSchema . parse ( await response . json ( ) ) ;
434440}
435441
442+
436443/**
437444 * Exchange a refresh token for an updated access token.
438445 */
0 commit comments