1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
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 // JWTAccessTokenSourceFromJSON uses a Google Developers service account JSON
19 // key file to read the credentials that authorize and authenticate the
20 // requests, and returns a TokenSource that does not use any OAuth2 flow but
21 // instead creates a JWT and sends that as the access token.
22 // The audience is typically a URL that specifies the scope of the credentials.
23 //
24 // Note that this is not a standard OAuth flow, but rather an
25 // optimization supported by a few Google services.
26 // Unless you know otherwise, you should use JWTConfigFromJSON instead.
27 func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) {
28 return newJWTSource(jsonKey, audience, nil)
29 }
30 31 // JWTAccessTokenSourceWithScope uses a Google Developers service account JSON
32 // key file to read the credentials that authorize and authenticate the
33 // requests, and returns a TokenSource that does not use any OAuth2 flow but
34 // instead creates a JWT and sends that as the access token.
35 // The scope is typically a list of URLs that specifies the scope of the
36 // credentials.
37 //
38 // Note that this is not a standard OAuth flow, but rather an
39 // optimization supported by a few Google services.
40 // Unless you know otherwise, you should use JWTConfigFromJSON instead.
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