15
15
using System . Text . Json ;
16
16
using System . Threading . Tasks ;
17
17
using Microsoft . AspNetCore . Http ;
18
+ using Microsoft . AspNetCore . WebUtilities ;
18
19
using Microsoft . Extensions . Logging ;
19
20
using Microsoft . Extensions . Options ;
20
21
using Microsoft . Extensions . Primitives ;
@@ -30,8 +31,11 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
30
31
public class OpenIdConnectHandler : RemoteAuthenticationHandler < OpenIdConnectOptions > , IAuthenticationSignOutHandler
31
32
{
32
33
private const string NonceProperty = "N" ;
33
-
34
34
private const string HeaderValueEpocDate = "Thu, 01 Jan 1970 00:00:00 GMT" ;
35
+ private const string CodeVerifierKey = "code_verifier" ;
36
+ private const string CodeChallengeKey = "code_challenge" ;
37
+ private const string CodeChallengeMethodKey = "code_challenge_method" ;
38
+ private const string CodeChallengeMethodS256 = "S256" ;
35
39
36
40
private static readonly RandomNumberGenerator CryptoRandom = RandomNumberGenerator . Create ( ) ;
37
41
@@ -366,6 +370,24 @@ private async Task HandleChallengeAsyncInternal(AuthenticationProperties propert
366
370
Scope = string . Join ( " " , properties . GetParameter < ICollection < string > > ( OpenIdConnectParameterNames . Scope ) ?? Options . Scope ) ,
367
371
} ;
368
372
373
+ // https://tools.ietf.org/html/rfc7636
374
+ if ( Options . UsePkse )
375
+ {
376
+ var verifierBytes = new byte [ 32 ] ;
377
+ CryptoRandom . GetBytes ( verifierBytes ) ;
378
+ var codeVerifier = WebEncoders . Base64UrlEncode ( verifierBytes ) ;
379
+
380
+ // Store this for use during the code redemption. See RunAuthorizationCodeReceivedEventAsync.
381
+ properties . Items . Add ( CodeVerifierKey , codeVerifier ) ;
382
+
383
+ using var sha256 = SHA256 . Create ( ) ;
384
+ var challengeBytes = sha256 . ComputeHash ( Encoding . UTF8 . GetBytes ( codeVerifier ) ) ;
385
+ var codeChallenge = WebEncoders . Base64UrlEncode ( challengeBytes ) ;
386
+
387
+ message . Parameters . Add ( CodeChallengeKey , codeChallenge ) ;
388
+ message . Parameters . Add ( CodeChallengeMethodKey , CodeChallengeMethodS256 ) ;
389
+ }
390
+
369
391
// Add the 'max_age' parameter to the authentication request if MaxAge is not null.
370
392
// See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
371
393
var maxAge = properties . GetParameter < TimeSpan ? > ( OpenIdConnectParameterNames . MaxAge ) ?? Options . MaxAge ;
@@ -1097,6 +1119,12 @@ private async Task<AuthorizationCodeReceivedContext> RunAuthorizationCodeReceive
1097
1119
RedirectUri = properties . Items [ OpenIdConnectDefaults . RedirectUriForCodePropertiesKey ]
1098
1120
} ;
1099
1121
1122
+ // PKCE https://tools.ietf.org/html/rfc7636#section-4.5, see HandleChallengeAsyncInternal
1123
+ if ( properties . Items . TryGetValue ( CodeVerifierKey , out var codeVerifier ) )
1124
+ {
1125
+ tokenEndpointRequest . Parameters . Add ( CodeVerifierKey , codeVerifier ) ;
1126
+ }
1127
+
1100
1128
var context = new AuthorizationCodeReceivedContext ( Context , Scheme , Options , properties )
1101
1129
{
1102
1130
ProtocolMessage = authorizationResponse ,
0 commit comments