6
6
7
7
using System . Net . Http . Headers ;
8
8
using System . Security . Claims ;
9
+ using System . Text ;
9
10
using System . Text . Encodings . Web ;
10
11
using System . Text . Json ;
11
12
using Microsoft . Extensions . Logging ;
@@ -50,6 +51,41 @@ protected override async Task<AuthenticationTicket> CreateTicketAsync(
50
51
return new AuthenticationTicket ( context . Principal ! , context . Properties , Scheme . Name ) ;
51
52
}
52
53
54
+ protected override async Task < OAuthTokenResponse > ExchangeCodeAsync ( [ NotNull ] OAuthCodeExchangeContext context )
55
+ {
56
+ var tokenRequestParameters = new Dictionary < string , string >
57
+ {
58
+ [ "redirect_uri" ] = context . RedirectUri ,
59
+ [ "code" ] = context . Code ,
60
+ [ "grant_type" ] = "authorization_code"
61
+ } ;
62
+
63
+ // PKCE https://tools.ietf.org/html/rfc7636#section-4.5, see BuildChallengeUrl
64
+ if ( context . Properties . Items . TryGetValue ( OAuthConstants . CodeVerifierKey , out var codeVerifier ) )
65
+ {
66
+ tokenRequestParameters [ OAuthConstants . CodeVerifierKey ] = codeVerifier ! ;
67
+ context . Properties . Items . Remove ( OAuthConstants . CodeVerifierKey ) ;
68
+ }
69
+
70
+ using var requestContent = new FormUrlEncodedContent ( tokenRequestParameters ! ) ;
71
+ using var requestMessage = new HttpRequestMessage ( HttpMethod . Post , Options . TokenEndpoint ) ;
72
+ requestMessage . Headers . Accept . Add ( new MediaTypeWithQualityHeaderValue ( "application/json" ) ) ;
73
+ var credentials = Convert . ToBase64String ( Encoding . ASCII . GetBytes (
74
+ $ "{ Options . ClientId } :{ Options . ClientSecret } ") ) ;
75
+ requestMessage . Headers . Authorization = new AuthenticationHeaderValue ( "Basic" , credentials ) ;
76
+ requestMessage . Content = requestContent ;
77
+ requestMessage . Version = Backchannel . DefaultRequestVersion ;
78
+ using var response = await Backchannel . SendAsync ( requestMessage , Context . RequestAborted ) ;
79
+ if ( ! response . IsSuccessStatusCode )
80
+ {
81
+ await Log . ExchangeCodeAsync ( Logger , response , Context . RequestAborted ) ;
82
+ return OAuthTokenResponse . Failed ( new Exception ( "An error occurred while retrieving an access token." ) ) ;
83
+ }
84
+
85
+ var body = await response . Content . ReadAsStringAsync ( Context . RequestAborted ) ;
86
+ return OAuthTokenResponse . Success ( JsonDocument . Parse ( body ) ) ;
87
+ }
88
+
53
89
private static partial class Log
54
90
{
55
91
internal static async Task UserProfileErrorAsync ( ILogger logger , HttpResponseMessage response , CancellationToken cancellationToken )
@@ -67,5 +103,21 @@ private static partial void UserProfileError(
67
103
System . Net . HttpStatusCode status ,
68
104
string headers ,
69
105
string body ) ;
106
+
107
+ internal static async Task ExchangeCodeAsync ( ILogger logger , HttpResponseMessage response , CancellationToken cancellationToken )
108
+ {
109
+ ExchangeCodeAsync (
110
+ logger ,
111
+ response . StatusCode ,
112
+ response . Headers . ToString ( ) ,
113
+ await response . Content . ReadAsStringAsync ( cancellationToken ) ) ;
114
+ }
115
+
116
+ [ LoggerMessage ( 2 , LogLevel . Error , "An error occurred while retrieving an access token: the remote server returned a {Status} response with the following payload: {Headers} {Body}." ) ]
117
+ static partial void ExchangeCodeAsync (
118
+ ILogger logger ,
119
+ System . Net . HttpStatusCode status ,
120
+ string headers ,
121
+ string body ) ;
70
122
}
71
123
}
0 commit comments