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