@@ -116,6 +116,37 @@ public async Task<OAuthToken> HandleAuthenticationAsync(
116116 return tokenResponse ;
117117 }
118118
119+ /// <summary>
120+ /// Handles the exchange of an authorization code for an OAuth token.
121+ /// </summary>
122+ /// <param name="tokenEndpoint">The token endpoint URI.</param>
123+ /// <param name="clientId">The client ID.</param>
124+ /// <param name="clientSecret">The client secret, if any.</param>
125+ /// <param name="redirectUri">The redirect URI used in the authorization request.</param>
126+ /// <param name="authorizationCode">The authorization code received from the authorization server.</param>
127+ /// <param name="codeVerifier">The PKCE code verifier.</param>
128+ /// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
129+ /// <returns>The OAuth token response.</returns>
130+ public async Task < OAuthToken > HandleAuthorizationCodeAsync (
131+ Uri tokenEndpoint ,
132+ string clientId ,
133+ string ? clientSecret ,
134+ Uri redirectUri ,
135+ string authorizationCode ,
136+ string codeVerifier ,
137+ CancellationToken cancellationToken = default )
138+ {
139+ // Simply call our private implementation
140+ return await ExchangeAuthorizationCodeForTokenAsync (
141+ tokenEndpoint ,
142+ clientId ,
143+ clientSecret ,
144+ redirectUri ,
145+ authorizationCode ,
146+ codeVerifier ,
147+ cancellationToken ) ;
148+ }
149+
119150 private Uri ? ExtractResourceMetadataUri ( string wwwAuthenticateHeader )
120151 {
121152 if ( string . IsNullOrEmpty ( wwwAuthenticateHeader ) )
@@ -356,14 +387,25 @@ private string GenerateRandomString(int length)
356387 . Substring ( 0 , length ) ;
357388 }
358389
359- // This method would be used in a real implementation after receiving the authorization code
390+ /// <summary>
391+ /// Exchanges an authorization code for an OAuth token.
392+ /// </summary>
393+ /// <param name="tokenEndpoint">The token endpoint URI.</param>
394+ /// <param name="clientId">The client ID.</param>
395+ /// <param name="clientSecret">The client secret, if any.</param>
396+ /// <param name="redirectUri">The redirect URI used in the authorization request.</param>
397+ /// <param name="authorizationCode">The authorization code received from the authorization server.</param>
398+ /// <param name="codeVerifier">The PKCE code verifier.</param>
399+ /// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
400+ /// <returns>The OAuth token response.</returns>
360401 private async Task < OAuthToken > ExchangeAuthorizationCodeForTokenAsync (
361402 Uri tokenEndpoint ,
362403 string clientId ,
363404 string ? clientSecret ,
364405 Uri redirectUri ,
365406 string authorizationCode ,
366- string codeVerifier )
407+ string codeVerifier ,
408+ CancellationToken cancellationToken = default )
367409 {
368410 var tokenRequest = new Dictionary < string , string >
369411 {
@@ -381,18 +423,21 @@ private async Task<OAuthToken> ExchangeAuthorizationCodeForTokenAsync(
381423 {
382424 // Add client authentication if secret is available
383425 var authValue = Convert . ToBase64String ( Encoding . UTF8 . GetBytes ( $ "{ clientId } :{ clientSecret } ") ) ;
384- _httpClient . DefaultRequestHeaders . Authorization = new AuthenticationHeaderValue ( "Basic" , authValue ) ;
385- response = await _httpClient . PostAsync ( tokenEndpoint , requestContent ) ;
386- _httpClient . DefaultRequestHeaders . Authorization = null ;
426+ using var request = new HttpRequestMessage ( HttpMethod . Post , tokenEndpoint )
427+ {
428+ Content = requestContent
429+ } ;
430+ request . Headers . Authorization = new AuthenticationHeaderValue ( "Basic" , authValue ) ;
431+ response = await _httpClient . SendAsync ( request , cancellationToken ) ;
387432 }
388433 else
389434 {
390- response = await _httpClient . PostAsync ( tokenEndpoint , requestContent ) ;
435+ response = await _httpClient . PostAsync ( tokenEndpoint , requestContent , cancellationToken ) ;
391436 }
392437
393438 response . EnsureSuccessStatusCode ( ) ;
394439
395- var json = await response . Content . ReadAsStringAsync ( ) ;
440+ var json = await response . Content . ReadAsStringAsync ( cancellationToken ) ;
396441 var tokenResponse = JsonSerializer . Deserialize ( json , McpJsonUtilities . DefaultOptions . GetTypeInfo < OAuthToken > ( ) ) ;
397442 if ( tokenResponse == null )
398443 {
0 commit comments