jwt.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // Copyright 2014 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 jwt implements the OAuth 2.0 JSON Web Token flow, commonly
  5. // known as "two-legged OAuth 2.0".
  6. //
  7. // See: https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12
  8. package jwt
  9. import (
  10. "encoding/json"
  11. "fmt"
  12. "io"
  13. "io/ioutil"
  14. "net/http"
  15. "net/url"
  16. "strings"
  17. "time"
  18. "golang.org/x/net/context"
  19. "golang.org/x/oauth2"
  20. "golang.org/x/oauth2/internal"
  21. "golang.org/x/oauth2/jws"
  22. )
  23. var (
  24. defaultGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
  25. defaultHeader = &jws.Header{Algorithm: "RS256", Typ: "JWT"}
  26. )
  27. // Config is the configuration for using JWT to fetch tokens,
  28. // commonly known as "two-legged OAuth 2.0".
  29. type Config struct {
  30. // Email is the OAuth client identifier used when communicating with
  31. // the configured OAuth provider.
  32. Email string
  33. // PrivateKey contains the contents of an RSA private key or the
  34. // contents of a PEM file that contains a private key. The provided
  35. // private key is used to sign JWT payloads.
  36. // PEM containers with a passphrase are not supported.
  37. // Use the following command to convert a PKCS 12 file into a PEM.
  38. //
  39. // $ openssl pkcs12 -in key.p12 -out key.pem -nodes
  40. //
  41. PrivateKey []byte
  42. // Subject is the optional user to impersonate.
  43. Subject string
  44. // Scopes optionally specifies a list of requested permission scopes.
  45. Scopes []string
  46. // TokenURL is the endpoint required to complete the 2-legged JWT flow.
  47. TokenURL string
  48. // Expires optionally specifies how long the token is valid for.
  49. Expires time.Duration
  50. }
  51. // TokenSource returns a JWT TokenSource using the configuration
  52. // in c and the HTTP client from the provided context.
  53. func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource {
  54. return oauth2.ReuseTokenSource(nil, jwtSource{ctx, c})
  55. }
  56. // Client returns an HTTP client wrapping the context's
  57. // HTTP transport and adding Authorization headers with tokens
  58. // obtained from c.
  59. //
  60. // The returned client and its Transport should not be modified.
  61. func (c *Config) Client(ctx context.Context) *http.Client {
  62. return oauth2.NewClient(ctx, c.TokenSource(ctx))
  63. }
  64. // jwtSource is a source that always does a signed JWT request for a token.
  65. // It should typically be wrapped with a reuseTokenSource.
  66. type jwtSource struct {
  67. ctx context.Context
  68. conf *Config
  69. }
  70. func (js jwtSource) Token() (*oauth2.Token, error) {
  71. pk, err := internal.ParseKey(js.conf.PrivateKey)
  72. if err != nil {
  73. return nil, err
  74. }
  75. hc := oauth2.NewClient(js.ctx, nil)
  76. claimSet := &jws.ClaimSet{
  77. Iss: js.conf.Email,
  78. Scope: strings.Join(js.conf.Scopes, " "),
  79. Aud: js.conf.TokenURL,
  80. }
  81. if subject := js.conf.Subject; subject != "" {
  82. claimSet.Sub = subject
  83. // prn is the old name of sub. Keep setting it
  84. // to be compatible with legacy OAuth 2.0 providers.
  85. claimSet.Prn = subject
  86. }
  87. if t := js.conf.Expires; t > 0 {
  88. claimSet.Exp = time.Now().Add(t).Unix()
  89. }
  90. payload, err := jws.Encode(defaultHeader, claimSet, pk)
  91. if err != nil {
  92. return nil, err
  93. }
  94. v := url.Values{}
  95. v.Set("grant_type", defaultGrantType)
  96. v.Set("assertion", payload)
  97. resp, err := hc.PostForm(js.conf.TokenURL, v)
  98. if err != nil {
  99. return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
  100. }
  101. defer resp.Body.Close()
  102. body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))
  103. if err != nil {
  104. return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
  105. }
  106. if c := resp.StatusCode; c < 200 || c > 299 {
  107. return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", resp.Status, body)
  108. }
  109. // tokenRes is the JSON response body.
  110. var tokenRes struct {
  111. AccessToken string `json:"access_token"`
  112. TokenType string `json:"token_type"`
  113. IDToken string `json:"id_token"`
  114. ExpiresIn int64 `json:"expires_in"` // relative seconds from now
  115. }
  116. if err := json.Unmarshal(body, &tokenRes); err != nil {
  117. return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
  118. }
  119. token := &oauth2.Token{
  120. AccessToken: tokenRes.AccessToken,
  121. TokenType: tokenRes.TokenType,
  122. }
  123. raw := make(map[string]interface{})
  124. json.Unmarshal(body, &raw) // no error checks for optional fields
  125. token = token.WithExtra(raw)
  126. if secs := tokenRes.ExpiresIn; secs > 0 {
  127. token.Expiry = time.Now().Add(time.Duration(secs) * time.Second)
  128. }
  129. if v := tokenRes.IDToken; v != "" {
  130. // decode returned id token to get expiry
  131. claimSet, err := jws.Decode(v)
  132. if err != nil {
  133. return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err)
  134. }
  135. token.Expiry = time.Unix(claimSet.Exp, 0)
  136. }
  137. return token, nil
  138. }