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