| // Copyright 2015 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package google | |
| import ( | |
| "crypto/rsa" | |
| "fmt" | |
| "strings" | |
| "time" | |
| "golang.org/x/oauth2" | |
| "golang.org/x/oauth2/internal" | |
| "golang.org/x/oauth2/jws" | |
| ) | |
| // JWTAccessTokenSourceFromJSON uses a Google Developers service account JSON | |
| // key file to read the credentials that authorize and authenticate the | |
| // requests, and returns a TokenSource that does not use any OAuth2 flow but | |
| // instead creates a JWT and sends that as the access token. | |
| // The audience is typically a URL that specifies the scope of the credentials. | |
| // | |
| // Note that this is not a standard OAuth flow, but rather an | |
| // optimization supported by a few Google services. | |
| // Unless you know otherwise, you should use JWTConfigFromJSON instead. | |
| func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) { | |
| return newJWTSource(jsonKey, audience, nil) | |
| } | |
| // JWTAccessTokenSourceWithScope uses a Google Developers service account JSON | |
| // key file to read the credentials that authorize and authenticate the | |
| // requests, and returns a TokenSource that does not use any OAuth2 flow but | |
| // instead creates a JWT and sends that as the access token. | |
| // The scope is typically a list of URLs that specifies the scope of the | |
| // credentials. | |
| // | |
| // Note that this is not a standard OAuth flow, but rather an | |
| // optimization supported by a few Google services. | |
| // Unless you know otherwise, you should use JWTConfigFromJSON instead. | |
| func JWTAccessTokenSourceWithScope(jsonKey []byte, scope ...string) (oauth2.TokenSource, error) { | |
| return newJWTSource(jsonKey, "", scope) | |
| } | |
| func newJWTSource(jsonKey []byte, audience string, scopes []string) (oauth2.TokenSource, error) { | |
| if len(scopes) == 0 && audience == "" { | |
| return nil, fmt.Errorf("google: missing scope/audience for JWT access token") | |
| } | |
| cfg, err := JWTConfigFromJSON(jsonKey) | |
| if err != nil { | |
| return nil, fmt.Errorf("google: could not parse JSON key: %v", err) | |
| } | |
| pk, err := internal.ParseKey(cfg.PrivateKey) | |
| if err != nil { | |
| return nil, fmt.Errorf("google: could not parse key: %v", err) | |
| } | |
| ts := &jwtAccessTokenSource{ | |
| email: cfg.Email, | |
| audience: audience, | |
| scopes: scopes, | |
| pk: pk, | |
| pkID: cfg.PrivateKeyID, | |
| } | |
| tok, err := ts.Token() | |
| if err != nil { | |
| return nil, err | |
| } | |
| rts := newErrWrappingTokenSource(oauth2.ReuseTokenSource(tok, ts)) | |
| return rts, nil | |
| } | |
| type jwtAccessTokenSource struct { | |
| email, audience string | |
| scopes []string | |
| pk *rsa.PrivateKey | |
| pkID string | |
| } | |
| func (ts *jwtAccessTokenSource) Token() (*oauth2.Token, error) { | |
| iat := time.Now() | |
| exp := iat.Add(time.Hour) | |
| scope := strings.Join(ts.scopes, " ") | |
| cs := &jws.ClaimSet{ | |
| Iss: ts.email, | |
| Sub: ts.email, | |
| Aud: ts.audience, | |
| Scope: scope, | |
| Iat: iat.Unix(), | |
| Exp: exp.Unix(), | |
| } | |
| hdr := &jws.Header{ | |
| Algorithm: "RS256", | |
| Typ: "JWT", | |
| KeyID: string(ts.pkID), | |
| } | |
| msg, err := jws.Encode(hdr, cs, ts.pk) | |
| if err != nil { | |
| return nil, fmt.Errorf("google: could not encode JWT: %v", err) | |
| } | |
| return &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp}, nil | |
| } |