golang.org/x/oauth2@v0.18.0/google/jwt.go (about) 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 }