...
1
2
3
4
5
6
7 package authhandler
8
9 import (
10 "context"
11 "errors"
12
13 "golang.org/x/oauth2"
14 )
15
16 const (
17
18 codeChallengeKey = "code_challenge"
19 codeChallengeMethodKey = "code_challenge_method"
20
21
22 codeVerifierKey = "code_verifier"
23 )
24
25
26 type PKCEParams struct {
27 Challenge string
28 ChallengeMethod string
29 Verifier string
30 }
31
32
33
34
35 type AuthorizationHandler func(authCodeURL string) (code string, state string, err error)
36
37
38
39
40
41
42 func TokenSourceWithPKCE(ctx context.Context, config *oauth2.Config, state string, authHandler AuthorizationHandler, pkce *PKCEParams) oauth2.TokenSource {
43 return oauth2.ReuseTokenSource(nil, authHandlerSource{config: config, ctx: ctx, authHandler: authHandler, state: state, pkce: pkce})
44 }
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 func TokenSource(ctx context.Context, config *oauth2.Config, state string, authHandler AuthorizationHandler) oauth2.TokenSource {
61 return TokenSourceWithPKCE(ctx, config, state, authHandler, nil)
62 }
63
64 type authHandlerSource struct {
65 ctx context.Context
66 config *oauth2.Config
67 authHandler AuthorizationHandler
68 state string
69 pkce *PKCEParams
70 }
71
72 func (source authHandlerSource) Token() (*oauth2.Token, error) {
73
74 var authCodeUrlOptions []oauth2.AuthCodeOption
75 if source.pkce != nil && source.pkce.Challenge != "" && source.pkce.ChallengeMethod != "" {
76 authCodeUrlOptions = []oauth2.AuthCodeOption{oauth2.SetAuthURLParam(codeChallengeKey, source.pkce.Challenge),
77 oauth2.SetAuthURLParam(codeChallengeMethodKey, source.pkce.ChallengeMethod)}
78 }
79 url := source.config.AuthCodeURL(source.state, authCodeUrlOptions...)
80 code, state, err := source.authHandler(url)
81 if err != nil {
82 return nil, err
83 }
84 if state != source.state {
85 return nil, errors.New("state mismatch in 3-legged-OAuth flow")
86 }
87
88
89 var exchangeOptions []oauth2.AuthCodeOption
90 if source.pkce != nil && source.pkce.Verifier != "" {
91 exchangeOptions = []oauth2.AuthCodeOption{oauth2.SetAuthURLParam(codeVerifierKey, source.pkce.Verifier)}
92 }
93 return source.config.Exchange(source.ctx, code, exchangeOptions...)
94 }
95
View as plain text