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