jwt.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // Copyright 2014 The oauth2 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. }
  49. // TokenSource returns a JWT TokenSource using the configuration
  50. // in c and the HTTP client from the provided context.
  51. func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource {
  52. return oauth2.ReuseTokenSource(nil, jwtSource{ctx, c})
  53. }
  54. // Client returns an HTTP client wrapping the context's
  55. // HTTP transport and adding Authorization headers with tokens
  56. // obtained from c.
  57. //
  58. // The returned client and its Transport should not be modified.
  59. func (c *Config) Client(ctx context.Context) *http.Client {
  60. return oauth2.NewClient(ctx, c.TokenSource(ctx))
  61. }
  62. // jwtSource is a source that always does a signed JWT request for a token.
  63. // It should typically be wrapped with a reuseTokenSource.
  64. type jwtSource struct {
  65. ctx context.Context
  66. conf *Config
  67. }
  68. func (js jwtSource) Token() (*oauth2.Token, error) {
  69. pk, err := internal.ParseKey(js.conf.PrivateKey)
  70. if err != nil {
  71. return nil, err
  72. }
  73. hc := oauth2.NewClient(js.ctx, nil)
  74. claimSet := &jws.ClaimSet{
  75. Iss: js.conf.Email,
  76. Scope: strings.Join(js.conf.Scopes, " "),
  77. Aud: js.conf.TokenURL,
  78. }
  79. if subject := js.conf.Subject; subject != "" {
  80. claimSet.Sub = subject
  81. // prn is the old name of sub. Keep setting it
  82. // to be compatible with legacy OAuth 2.0 providers.
  83. claimSet.Prn = subject
  84. }
  85. payload, err := jws.Encode(defaultHeader, claimSet, pk)
  86. if err != nil {
  87. return nil, err
  88. }
  89. v := url.Values{}
  90. v.Set("grant_type", defaultGrantType)
  91. v.Set("assertion", payload)
  92. resp, err := hc.PostForm(js.conf.TokenURL, v)
  93. if err != nil {
  94. return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
  95. }
  96. defer resp.Body.Close()
  97. body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))
  98. if err != nil {
  99. return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
  100. }
  101. if c := resp.StatusCode; c < 200 || c > 299 {
  102. return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", resp.Status, body)
  103. }
  104. // tokenRes is the JSON response body.
  105. var tokenRes struct {
  106. AccessToken string `json:"access_token"`
  107. TokenType string `json:"token_type"`
  108. IDToken string `json:"id_token"`
  109. ExpiresIn int64 `json:"expires_in"` // relative seconds from now
  110. }
  111. if err := json.Unmarshal(body, &tokenRes); err != nil {
  112. return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
  113. }
  114. token := &oauth2.Token{
  115. AccessToken: tokenRes.AccessToken,
  116. TokenType: tokenRes.TokenType,
  117. }
  118. raw := make(map[string]interface{})
  119. json.Unmarshal(body, &raw) // no error checks for optional fields
  120. token = token.WithExtra(raw)
  121. if secs := tokenRes.ExpiresIn; secs > 0 {
  122. token.Expiry = time.Now().Add(time.Duration(secs) * time.Second)
  123. }
  124. if v := tokenRes.IDToken; v != "" {
  125. // decode returned id token to get expiry
  126. claimSet, err := jws.Decode(v)
  127. if err != nil {
  128. return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err)
  129. }
  130. token.Expiry = time.Unix(claimSet.Exp, 0)
  131. }
  132. return token, nil
  133. }