2
2
// Licensed under the MIT license.
3
3
using System ;
4
4
using System . Collections . Generic ;
5
- using System . Net ;
6
5
using System . Net . Http ;
7
6
using System . Net . Http . Headers ;
8
- using System . Text ;
9
7
using System . Threading ;
10
8
using System . Threading . Tasks ;
11
9
using Microsoft . Git . CredentialManager . Authentication . OAuth . Json ;
@@ -26,7 +24,7 @@ public interface IOAuth2Client
26
24
/// <param name="browser">User agent to use to start the authorization code grant flow.</param>
27
25
/// <param name="ct">Token to cancel the operation.</param>
28
26
/// <returns>Authorization code.</returns>
29
- Task < string > GetAuthorizationCodeAsync ( IEnumerable < string > scopes , IOAuth2WebBrowser browser , CancellationToken ct ) ;
27
+ Task < OAuth2AuthorizationCodeResult > GetAuthorizationCodeAsync ( IEnumerable < string > scopes , IOAuth2WebBrowser browser , CancellationToken ct ) ;
30
28
31
29
/// <summary>
32
30
/// Retrieve a device code grant.
@@ -40,10 +38,10 @@ public interface IOAuth2Client
40
38
/// <summary>
41
39
/// Exchange an authorization code acquired from <see cref="GetAuthorizationCodeAsync"/> for an access token.
42
40
/// </summary>
43
- /// <param name="authorizationCode ">Authorization code.</param>
41
+ /// <param name="authorizationCodeResult ">Authorization code grant result .</param>
44
42
/// <param name="ct">Token to cancel the operation.</param>
45
43
/// <returns>Token result.</returns>
46
- Task < OAuth2TokenResult > GetTokenByAuthorizationCodeAsync ( string authorizationCode , CancellationToken ct ) ;
44
+ Task < OAuth2TokenResult > GetTokenByAuthorizationCodeAsync ( OAuth2AuthorizationCodeResult authorizationCodeResult , CancellationToken ct ) ;
47
45
48
46
/// <summary>
49
47
/// Use a refresh token to get a new access token.
@@ -70,7 +68,7 @@ public class OAuth2Client : IOAuth2Client
70
68
private readonly string _clientId ;
71
69
private readonly string _clientSecret ;
72
70
73
- private IOAuth2NonceGenerator _nonceGenerator ;
71
+ private IOAuth2CodeGenerator _codeGenerator ;
74
72
75
73
public OAuth2Client ( HttpClient httpClient , OAuth2ServerEndpoints endpoints , string clientId , Uri redirectUri = null , string clientSecret = null )
76
74
{
@@ -81,31 +79,37 @@ public OAuth2Client(HttpClient httpClient, OAuth2ServerEndpoints endpoints, stri
81
79
_clientSecret = clientSecret ;
82
80
}
83
81
84
- public IOAuth2NonceGenerator NonceGenerator
82
+ public IOAuth2CodeGenerator CodeGenerator
85
83
{
86
- get => _nonceGenerator ?? ( _nonceGenerator = new OAuth2NonceGenerator ( ) ) ;
87
- set => _nonceGenerator = value ;
84
+ get => _codeGenerator ?? ( _codeGenerator = new OAuth2CryptographicCodeGenerator ( ) ) ;
85
+ set => _codeGenerator = value ;
88
86
}
89
87
90
88
#region IOAuth2Client
91
89
92
- public async Task < string > GetAuthorizationCodeAsync ( IEnumerable < string > scopes , IOAuth2WebBrowser browser , CancellationToken ct )
90
+ public async Task < OAuth2AuthorizationCodeResult > GetAuthorizationCodeAsync ( IEnumerable < string > scopes , IOAuth2WebBrowser browser , CancellationToken ct )
93
91
{
94
- string state = NonceGenerator . CreateNonce ( ) ;
95
- string scopesStr = string . Join ( " " , scopes ) ;
92
+ string state = CodeGenerator . CreateNonce ( ) ;
93
+ string codeVerifier = CodeGenerator . CreatePkceCodeVerifier ( ) ;
94
+ string codeChallenge = CodeGenerator . CreatePkceCodeChallenge ( OAuth2PkceChallengeMethod . Sha256 , codeVerifier ) ;
96
95
97
96
var queryParams = new Dictionary < string , string >
98
97
{
99
- [ OAuth2Constants . AuthorizationEndpoint . ResponseTypeParameter ] = OAuth2Constants . AuthorizationEndpoint . AuthorizationCodeResponseType ,
98
+ [ OAuth2Constants . AuthorizationEndpoint . ResponseTypeParameter ] =
99
+ OAuth2Constants . AuthorizationEndpoint . AuthorizationCodeResponseType ,
100
100
[ OAuth2Constants . ClientIdParameter ] = _clientId ,
101
101
[ OAuth2Constants . AuthorizationEndpoint . StateParameter ] = state ,
102
+ [ OAuth2Constants . AuthorizationEndpoint . PkceChallengeMethodParameter ] =
103
+ OAuth2Constants . AuthorizationEndpoint . PkceChallengeMethodS256 ,
104
+ [ OAuth2Constants . AuthorizationEndpoint . PkceChallengeParameter ] = codeChallenge
102
105
} ;
103
106
104
107
if ( _redirectUri != null )
105
108
{
106
109
queryParams [ OAuth2Constants . RedirectUriParameter ] = _redirectUri . ToString ( ) ;
107
110
}
108
111
112
+ string scopesStr = string . Join ( " " , scopes ) ;
109
113
if ( ! string . IsNullOrWhiteSpace ( scopesStr ) )
110
114
{
111
115
queryParams [ OAuth2Constants . ScopeParameter ] = scopesStr ;
@@ -140,7 +144,7 @@ public async Task<string> GetAuthorizationCodeAsync(IEnumerable<string> scopes,
140
144
throw new OAuth2Exception ( $ "Missing '{ OAuth2Constants . AuthorizationGrantResponse . AuthorizationCodeParameter } ' in response.") ;
141
145
}
142
146
143
- return authCode ;
147
+ return new OAuth2AuthorizationCodeResult ( authCode , codeVerifier ) ;
144
148
}
145
149
146
150
public async Task < OAuth2DeviceCodeResult > GetDeviceCodeAsync ( IEnumerable < string > scopes , CancellationToken ct )
@@ -177,15 +181,21 @@ public async Task<OAuth2DeviceCodeResult> GetDeviceCodeAsync(IEnumerable<string>
177
181
}
178
182
}
179
183
180
- public async Task < OAuth2TokenResult > GetTokenByAuthorizationCodeAsync ( string authorizationCode , CancellationToken ct )
184
+ public async Task < OAuth2TokenResult > GetTokenByAuthorizationCodeAsync ( OAuth2AuthorizationCodeResult authorizationCodeResult , CancellationToken ct )
181
185
{
182
186
var formData = new Dictionary < string , string >
183
187
{
184
188
[ OAuth2Constants . TokenEndpoint . GrantTypeParameter ] = OAuth2Constants . TokenEndpoint . AuthorizationCodeGrantType ,
185
- [ OAuth2Constants . TokenEndpoint . AuthorizationCodeParameter ] = authorizationCode ,
186
- [ OAuth2Constants . ClientIdParameter ] = _clientId ,
189
+ [ OAuth2Constants . TokenEndpoint . AuthorizationCodeParameter ] = authorizationCodeResult . Code ,
190
+ [ OAuth2Constants . TokenEndpoint . PkceVerifierParameter ] = authorizationCodeResult . CodeVerifier ,
191
+ [ OAuth2Constants . ClientIdParameter ] = _clientId
187
192
} ;
188
193
194
+ if ( authorizationCodeResult . CodeVerifier != null )
195
+ {
196
+ formData [ OAuth2Constants . TokenEndpoint . PkceVerifierParameter ] = authorizationCodeResult . CodeVerifier ;
197
+ }
198
+
189
199
if ( _redirectUri != null )
190
200
{
191
201
formData [ OAuth2Constants . RedirectUriParameter ] = _redirectUri . ToString ( ) ;
0 commit comments