1
2
3
4
5 package google
6
7 import (
8 "crypto/rsa"
9 "fmt"
10 "strings"
11 "time"
12
13 "golang.org/x/oauth2"
14 "golang.org/x/oauth2/internal"
15 "golang.org/x/oauth2/jws"
16 )
17
18
19
20
21
22
23
24
25
26
27 func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) {
28 return newJWTSource(jsonKey, audience, nil)
29 }
30
31
32
33
34
35
36
37
38
39
40
41 func JWTAccessTokenSourceWithScope(jsonKey []byte, scope ...string) (oauth2.TokenSource, error) {
42 return newJWTSource(jsonKey, "", scope)
43 }
44
45 func newJWTSource(jsonKey []byte, audience string, scopes []string) (oauth2.TokenSource, error) {
46 if len(scopes) == 0 && audience == "" {
47 return nil, fmt.Errorf("google: missing scope/audience for JWT access token")
48 }
49
50 cfg, err := JWTConfigFromJSON(jsonKey)
51 if err != nil {
52 return nil, fmt.Errorf("google: could not parse JSON key: %v", err)
53 }
54 pk, err := internal.ParseKey(cfg.PrivateKey)
55 if err != nil {
56 return nil, fmt.Errorf("google: could not parse key: %v", err)
57 }
58 ts := &jwtAccessTokenSource{
59 email: cfg.Email,
60 audience: audience,
61 scopes: scopes,
62 pk: pk,
63 pkID: cfg.PrivateKeyID,
64 }
65 tok, err := ts.Token()
66 if err != nil {
67 return nil, err
68 }
69 rts := newErrWrappingTokenSource(oauth2.ReuseTokenSource(tok, ts))
70 return rts, nil
71 }
72
73 type jwtAccessTokenSource struct {
74 email, audience string
75 scopes []string
76 pk *rsa.PrivateKey
77 pkID string
78 }
79
80 func (ts *jwtAccessTokenSource) Token() (*oauth2.Token, error) {
81 iat := time.Now()
82 exp := iat.Add(time.Hour)
83 scope := strings.Join(ts.scopes, " ")
84 cs := &jws.ClaimSet{
85 Iss: ts.email,
86 Sub: ts.email,
87 Aud: ts.audience,
88 Scope: scope,
89 Iat: iat.Unix(),
90 Exp: exp.Unix(),
91 }
92 hdr := &jws.Header{
93 Algorithm: "RS256",
94 Typ: "JWT",
95 KeyID: string(ts.pkID),
96 }
97 msg, err := jws.Encode(hdr, cs, ts.pk)
98 if err != nil {
99 return nil, fmt.Errorf("google: could not encode JWT: %v", err)
100 }
101 return &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp}, nil
102 }
103
View as plain text