jwt.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  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. package google
  5. import (
  6. "crypto/rsa"
  7. "fmt"
  8. "strings"
  9. "time"
  10. "golang.org/x/oauth2"
  11. "golang.org/x/oauth2/internal"
  12. "golang.org/x/oauth2/jws"
  13. )
  14. // JWTAccessTokenSourceFromJSON uses a Google Developers service account JSON
  15. // key file to read the credentials that authorize and authenticate the
  16. // requests, and returns a TokenSource that does not use any OAuth2 flow but
  17. // instead creates a JWT and sends that as the access token.
  18. // The audience is typically a URL that specifies the scope of the credentials.
  19. //
  20. // Note that this is not a standard OAuth flow, but rather an
  21. // optimization supported by a few Google services.
  22. // Unless you know otherwise, you should use JWTConfigFromJSON instead.
  23. func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) {
  24. return newJWTSource(jsonKey, audience, nil)
  25. }
  26. // JWTAccessTokenSourceWithScope uses a Google Developers service account JSON
  27. // key file to read the credentials that authorize and authenticate the
  28. // requests, and returns a TokenSource that does not use any OAuth2 flow but
  29. // instead creates a JWT and sends that as the access token.
  30. // The scope is typically a list of URLs that specifies the scope of the
  31. // credentials.
  32. //
  33. // Note that this is not a standard OAuth flow, but rather an
  34. // optimization supported by a few Google services.
  35. // Unless you know otherwise, you should use JWTConfigFromJSON instead.
  36. func JWTAccessTokenSourceWithScope(jsonKey []byte, scope ...string) (oauth2.TokenSource, error) {
  37. return newJWTSource(jsonKey, "", scope)
  38. }
  39. func newJWTSource(jsonKey []byte, audience string, scopes []string) (oauth2.TokenSource, error) {
  40. if len(scopes) == 0 && audience == "" {
  41. return nil, fmt.Errorf("google: missing scope/audience for JWT access token")
  42. }
  43. cfg, err := JWTConfigFromJSON(jsonKey)
  44. if err != nil {
  45. return nil, fmt.Errorf("google: could not parse JSON key: %v", err)
  46. }
  47. pk, err := internal.ParseKey(cfg.PrivateKey)
  48. if err != nil {
  49. return nil, fmt.Errorf("google: could not parse key: %v", err)
  50. }
  51. ts := &jwtAccessTokenSource{
  52. email: cfg.Email,
  53. audience: audience,
  54. scopes: scopes,
  55. pk: pk,
  56. pkID: cfg.PrivateKeyID,
  57. }
  58. tok, err := ts.Token()
  59. if err != nil {
  60. return nil, err
  61. }
  62. rts := newErrWrappingTokenSource(oauth2.ReuseTokenSource(tok, ts))
  63. return rts, nil
  64. }
  65. type jwtAccessTokenSource struct {
  66. email, audience string
  67. scopes []string
  68. pk *rsa.PrivateKey
  69. pkID string
  70. }
  71. func (ts *jwtAccessTokenSource) Token() (*oauth2.Token, error) {
  72. iat := time.Now()
  73. exp := iat.Add(time.Hour)
  74. scope := strings.Join(ts.scopes, " ")
  75. cs := &jws.ClaimSet{
  76. Iss: ts.email,
  77. Sub: ts.email,
  78. Aud: ts.audience,
  79. Scope: scope,
  80. Iat: iat.Unix(),
  81. Exp: exp.Unix(),
  82. }
  83. hdr := &jws.Header{
  84. Algorithm: "RS256",
  85. Typ: "JWT",
  86. KeyID: string(ts.pkID),
  87. }
  88. msg, err := jws.Encode(hdr, cs, ts.pk)
  89. if err != nil {
  90. return nil, fmt.Errorf("google: could not encode JWT: %v", err)
  91. }
  92. return &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp}, nil
  93. }