@@ -11,7 +11,11 @@ package oauth2 // import "golang.org/x/oauth2"
11
11
import (
12
12
"bytes"
13
13
"context"
14
+ "crypto/rand"
15
+ "crypto/sha256"
16
+ "encoding/base64"
14
17
"errors"
18
+ "io"
15
19
"net/http"
16
20
"net/url"
17
21
"strings"
@@ -379,3 +383,50 @@ func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
379
383
new : src ,
380
384
}
381
385
}
386
+
387
+ // PKCEParams holds parameters to support PKCE.
388
+ type PKCEParams struct {
389
+ Challenge string // The unpadded, base64-url-encoded string of the encrypted code verifier.
390
+ ChallengeMethod string // The encryption method (ex. S256).
391
+ Verifier string // The original, non-encrypted secret.
392
+ }
393
+
394
+ const (
395
+ // Parameter keys for AuthCodeURL method to support PKCE.
396
+ codeChallengeKey = "code_challenge"
397
+ codeChallengeMethodKey = "code_challenge_method"
398
+
399
+ // Parameter key for Exchange method to support PKCE.
400
+ codeVerifierKey = "code_verifier"
401
+ )
402
+
403
+ func (p * PKCEParams ) AuthCodeOptions () []AuthCodeOption {
404
+ return []AuthCodeOption {
405
+ SetAuthURLParam (codeChallengeKey , p .Challenge ),
406
+ SetAuthURLParam (codeChallengeMethodKey , p .ChallengeMethod )}
407
+ }
408
+
409
+ func (p * PKCEParams ) ExchangeOptions () []AuthCodeOption {
410
+ return []AuthCodeOption {
411
+ SetAuthURLParam (codeVerifierKey , p .Verifier )}
412
+ }
413
+
414
+ func randomString (n int ) string {
415
+ data := make ([]byte , n )
416
+ if _ , err := io .ReadFull (rand .Reader , data ); err != nil {
417
+ panic (err )
418
+ }
419
+ return base64 .StdEncoding .EncodeToString (data )
420
+ }
421
+
422
+ func GeneratePKCEParams () * PKCEParams {
423
+ verifier := randomString (32 )
424
+ sha := sha256 .Sum256 ([]byte (verifier ))
425
+ challenge := base64 .URLEncoding .WithPadding (base64 .NoPadding ).EncodeToString (sha [:])
426
+
427
+ return & PKCEParams {
428
+ Challenge : challenge ,
429
+ ChallengeMethod : "S256" ,
430
+ Verifier : verifier ,
431
+ }
432
+ }
0 commit comments