15
15
using Microsoft . AspNetCore . WebUtilities ;
16
16
using Microsoft . Extensions . Logging ;
17
17
using Microsoft . Extensions . Options ;
18
+ using Microsoft . Extensions . Primitives ;
18
19
19
20
namespace AspNet . Security . OAuth . Alipay ;
20
21
@@ -32,17 +33,13 @@ public AlipayAuthenticationHandler(
32
33
{
33
34
}
34
35
36
+ private const string AuthCode = "auth_code" ;
37
+
35
38
protected override Task < HandleRequestResult > HandleRemoteAuthenticateAsync ( )
36
39
{
37
- var query = Request . Query ;
38
- if ( query . TryGetValue ( "auth_code" , out var authCode ) )
40
+ if ( TryStandardizeRemoteAuthenticateQuery ( Request . Query , out var queryString ) )
39
41
{
40
- // The base `HandleRemoteAuthenticateAsync` requires that `Request.Query` must contain the key called `code`,
41
- // which is actually the same as `auth_code` by Alipay's design, but `Request.Query` does not have `Add` operation.
42
- // So here is a trick to get the private `Store` dictionary of `QueryCollection`.
43
- var queryStore = query . ToDictionary ( c => c . Key , c => c . Value , StringComparer . OrdinalIgnoreCase ) ;
44
- queryStore [ "code" ] = authCode ;
45
- Request . QueryString = QueryString . Create ( queryStore ) ;
42
+ Request . QueryString = queryString ;
46
43
}
47
44
48
45
return base . HandleRemoteAuthenticateAsync ( ) ;
@@ -236,6 +233,38 @@ protected override string BuildChallengeUrl([NotNull] AuthenticationProperties p
236
233
return QueryHelpers . AddQueryString ( Options . AuthorizationEndpoint , parameters ) ;
237
234
}
238
235
236
+ private static bool TryStandardizeRemoteAuthenticateQuery ( IQueryCollection query , out QueryString queryString )
237
+ {
238
+ if ( ! query . TryGetValue ( AuthCode , out var authCode ) )
239
+ {
240
+ queryString = default ;
241
+ return false ;
242
+ }
243
+
244
+ // Before: mydomain/signin-alipay?auth_code=xxx&state=xxx&...
245
+ // After: mydomain/signin-alipay?code=xxx&state=xxx&...
246
+ var queryParams = new List < KeyValuePair < string , StringValues > > ( query . Count )
247
+ {
248
+ new ( "code" , authCode )
249
+ } ;
250
+ foreach ( var item in query )
251
+ {
252
+ switch ( item . Key )
253
+ {
254
+ case "code" :
255
+ case AuthCode : // No need in fact, skip it
256
+ break ;
257
+
258
+ default :
259
+ queryParams . Add ( item ) ;
260
+ break ;
261
+ }
262
+ }
263
+
264
+ queryString = QueryString . Create ( queryParams ) ;
265
+ return true ;
266
+ }
267
+
239
268
private static partial class Log
240
269
{
241
270
internal static async Task UserProfileErrorAsync ( ILogger logger , HttpResponseMessage response , CancellationToken cancellationToken )
0 commit comments