Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 // Copyright 2012 The goauth2 Authors. All rights reserved. | 1 // Copyright 2012 The goauth2 Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // The jwt package provides support for creating credentials for OAuth2 service | |
6 // account requests. | |
7 // | |
8 // For examples of the package usage please see jwt_test.go. | |
9 // | |
10 // For info on OAuth2 service accounts please see the online documentation. | |
11 // https://developers.google.com/accounts/docs/OAuth2ServiceAccount | |
12 // | |
5 package jwt | 13 package jwt |
6 | 14 |
7 import ( | 15 import ( |
8 "../../oauth" | |
adg
2012/07/30 09:24:26
code.google.com/p/goauth2/oauth
gavaletz_google
2012/08/01 18:54:04
This change has been made; however, I am having tr
幹你娘
2021/01/01 03:49:11
幹
你
娘
老
雞
歪
幹你娘
2021/08/11 06:43:46
On 2012/07/30 09:24:26, adg .
| |
9 "crypto" | 16 "crypto" |
10 "crypto/rand" | 17 "crypto/rand" |
11 "crypto/rsa" | 18 "crypto/rsa" |
12 "crypto/sha256" | 19 "crypto/sha256" |
13 "crypto/x509" | 20 "crypto/x509" |
14 "encoding/base64" | 21 "encoding/base64" |
15 "encoding/json" | 22 "encoding/json" |
16 "encoding/pem" | 23 "encoding/pem" |
17 "errors" | 24 "errors" |
18 "fmt" | 25 "fmt" |
19 "net/http" | 26 "net/http" |
20 "net/url" | 27 "net/url" |
21 "strings" | 28 "strings" |
22 "time" | 29 "time" |
30 | |
31 "code.google.com/p/goauth2/oauth" | |
23 ) | 32 ) |
24 | 33 |
34 // These are the default/standard values for this to work for Google service acc ounts. | |
25 const ( | 35 const ( |
26 » StdAlgorithm = "RS256" | 36 » stdAlgorithm = "RS256" |
27 » StdType = "JWT" | 37 » stdType = "JWT" |
28 » StdAssertionType = "http://oauth.net/grant_type/jwt/1.0/bearer" | 38 » stdAssertionType = "http://oauth.net/grant_type/jwt/1.0/bearer" |
29 » StdGrantType = "assertion" | 39 » stdGrantType = "assertion" |
30 ) | 40 ) |
31 | 41 |
32 var ( | 42 var ( |
33 » ErrInvKey = errors.New("Invalid Key") | 43 » ErrInvalidKey = errors.New("Invalid Key") |
34 | 44 |
35 StdHeader = &Header{ | 45 StdHeader = &Header{ |
36 » » Algorithm: StdAlgorithm, | 46 » » Algorithm: stdAlgorithm, |
37 » » Type: StdType, | 47 » » Type: stdType, |
38 } | 48 } |
39 ) | 49 ) |
40 | 50 |
41 // urlEncode returns and Base64url encoded version of the input string with any | 51 // urlEncode returns and Base64url encoded version of the input string with any |
42 // trailing "=" stripped. | 52 // trailing "=" stripped. |
43 func urlEncode(b []byte) string { | 53 func urlEncode(b []byte) string { |
44 return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") | 54 return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") |
45 } | 55 } |
46 | 56 |
47 // Each segment in the JWT token will need to be Base64url encoded. | |
48 type Segment interface { | |
adg
2012/08/01 07:45:19
This doesn't seem to be used anywhere.
gavaletz_google
2012/08/01 18:54:04
Removed.
| |
49 Encode() (string, error) | |
50 } | |
51 | |
52 // The header consists of two fields that indicate the signing algorithm and the | 57 // The header consists of two fields that indicate the signing algorithm and the |
53 // format of the assertion. Both fields are mandatory, and each field has only | 58 // format of the assertion. Both fields are mandatory, and each field has only |
54 // one value. As additional algorithms and formats are introduced, this header | 59 // one value. As additional algorithms and formats are introduced, this header |
55 // will change accordingly. | 60 // will change accordingly. |
56 // | 61 // |
57 // Service Accounts rely on the RSA SHA256 algorithm and the JWT token format | 62 // Service Accounts rely on the RSA SHA256 algorithm and the JWT token format. |
58 // (see StdAlgorithm and StdType). | |
59 type Header struct { | 63 type Header struct { |
60 » Algorithm string `json:"alg"` | 64 » Algorithm string `json:"alg"` // encoding algorithm (usually RSA SHA256) |
61 » Type string `json:"typ"` | 65 » Type string `json:"typ"` // token format (usually JWT) |
62 } | 66 } |
63 | 67 |
64 // Encode returns the Base64url encoded form of the Header. | 68 // Encode returns the Base64url encoded form of the Header. |
65 func (h *Header) Encode() (string, error) { | 69 func (h *Header) Encode() (string, error) { |
66 b, err := json.Marshal(h) | 70 b, err := json.Marshal(h) |
67 if err != nil { | 71 if err != nil { |
68 return "", err | 72 return "", err |
69 } | 73 } |
70 return urlEncode(b), nil | 74 return urlEncode(b), nil |
71 } | 75 } |
72 | 76 |
73 // The JWT claim set contains information about the JWT including the | 77 // The JWT claim set contains information about the JWT including the |
74 // permissions being requested (scopes), the target of the token, the issuer, | 78 // permissions being requested (scopes), the target of the token, the issuer, |
75 // the time the token was issued, and the lifetime of the token. Most of the | 79 // the time the token was issued, and the lifetime of the token. |
76 // fields are mandatory. | |
77 // | 80 // |
78 // Aud is usually https://accounts.google.com/o/oauth2/token | 81 // Aud is usually https://accounts.google.com/o/oauth2/token |
79 type ClaimSet struct { | 82 type ClaimSet struct { |
80 Iss string `json:"iss"` // email address of the client_id of the application making the access token request | 83 Iss string `json:"iss"` // email address of the client_id of the application making the access token request |
81 Scope string `json:"scope"` // space-delimited list of the per missions the application requests | 84 Scope string `json:"scope"` // space-delimited list of the per missions the application requests |
82 » Aud string `json:"aud"` // a descriptor of the intended ta rget of the assertion | 85 » Aud string `json:"aud"` // descriptor of the intended targ et of the assertion |
83 » Exp int64 `json:"exp"` // the expiration time of the asse rtion (maximum of 1 hour from the issued time) | 86 » Exp int64 `json:"exp"` // expiration time of the assertio n (Optional; maximum of 1 hour from the JWT encoding time) |
84 » Iat int64 `json:"iat"` // the time the assertion was issu ed | 87 » Iat int64 `json:"iat"` // time the assertion was issued ( Optional; time that the JWT was encoded) |
85 » Prn string `json:"prn,omitempty"` // Optional | 88 » Prn string `json:"prn,omitempty"` // email for which the application is requesting delegated access (Optional). |
86 expTime time.Time | 89 expTime time.Time |
87 iatTime time.Time | 90 iatTime time.Time |
88 } | 91 } |
89 | 92 |
90 // setTimes sets Iat and Exp to time.Now() and Iat.Add(time.Hour) respectively. | 93 // setTimes sets Iat and Exp to time.Now() and Iat.Add(time.Hour) respectively. |
94 // | |
95 // Note that these times have nothing to do with the expiration time for the | |
96 // access_token returned by the server. These have to do with the lifetime of | |
97 // the encoded JWT. | |
98 // | |
99 // A JWT can be re-used for up to one hour after it was encoded. The access | |
100 // token that is granted will also be good for one hour so there is little point | |
101 // in trying to use the JWT a second time. | |
91 func (c *ClaimSet) setTimes(t time.Time) { | 102 func (c *ClaimSet) setTimes(t time.Time) { |
92 //TODO(gavaletz) Should these be updated when we receive the token? | |
93 // Documentation on the times is not clear - what if the JWT is encoded | |
94 // we sit on it for a while and then send it? Will it be rejected if | |
95 // the times are old or will it issue an authorization that will expire | |
96 // one hour from then (or the time in the request)? | |
97 c.iatTime = t | 103 c.iatTime = t |
98 c.expTime = c.iatTime.Add(time.Hour) | 104 c.expTime = c.iatTime.Add(time.Hour) |
99 » c.Iat = c.iatTime.Unix() | 105 » c.Iat = c.iatTime.Unix() // The time that the JWT was encoded. |
100 » c.Exp = c.expTime.Unix() | 106 » c.Exp = c.expTime.Unix() // The time that the encoded JWT will expire. |
101 } | 107 } |
102 | 108 |
103 // Encode returns the Base64url encoded form of the Signature. If either of Iat | 109 // Encode returns the Base64url encoded form of the Signature. If either of Iat |
104 // or Exp are 0, then they will be set to time.Now() and Iat.Add(time.Hour) | 110 // or Exp are 0, then they will be set to time.Now() and Iat.Add(time.Hour) |
105 // respectively. | 111 // respectively. |
106 func (c *ClaimSet) Encode() (string, error) { | 112 func (c *ClaimSet) Encode() (string, error) { |
107 if c.Exp == 0 || c.Iat == 0 { | 113 if c.Exp == 0 || c.Iat == 0 { |
108 c.setTimes(time.Now()) | 114 c.setTimes(time.Now()) |
109 } | 115 } |
110 b, err := json.Marshal(c) | 116 b, err := json.Marshal(c) |
(...skipping 11 matching lines...) Expand all Loading... | |
122 // Encode returns the Base64url encoded form of the Signature. The error | 128 // Encode returns the Base64url encoded form of the Signature. The error |
123 // returned from this function is always nil and can be safely ignored. | 129 // returned from this function is always nil and can be safely ignored. |
124 func (s Signature) Encode() (string, error) { | 130 func (s Signature) Encode() (string, error) { |
125 return urlEncode([]byte(s)), nil | 131 return urlEncode([]byte(s)), nil |
126 } | 132 } |
127 | 133 |
128 // A JWT is composed of three parts: a header, a claim set, and a signature. | 134 // A JWT is composed of three parts: a header, a claim set, and a signature. |
129 // The well formed and encoded JWT can then be exchanged for an access token. | 135 // The well formed and encoded JWT can then be exchanged for an access token. |
130 // | 136 // |
131 // The Token is not a JWT, but is is encoded to produce a well formed JWT. | 137 // The Token is not a JWT, but is is encoded to produce a well formed JWT. |
138 // | |
139 // When obtaining a key from the Google API console it will be downloaded in a | |
140 // PKCS12 encoding. To use this key you will need to convert it to a PEM file. | |
141 // This can be achieved on a with openssl. | |
142 // | |
143 // $ openssl pkcs12 -in <key.p12> -nocerts -passin pass:notasecret -nodes -out < key.pem> | |
144 // | |
145 // The contents of this file can then be used as the Key. | |
132 type Token struct { | 146 type Token struct { |
133 » Header *Header | 147 » Header *Header // if nil the StdHeader will be used |
134 » ClaimSet *ClaimSet | 148 » ClaimSet *ClaimSet // claim set used to construct the JWT |
135 » Signature Signature | 149 » Signature Signature // generally computed in Encode based on the ClaimSe t |
136 » Key []byte | 150 » Key []byte // PEM printable encoding of the private key |
137 pKey *rsa.PrivateKey | 151 pKey *rsa.PrivateKey |
138 head string | 152 head string |
139 claim string | 153 claim string |
140 sig string | 154 sig string |
155 } | |
156 | |
157 // NewToken returns a filled in *Token based on the StdHeader, and sets the Iat | |
158 // and Exp times based on when the call to Assert is made. | |
159 func NewToken(iss, scope, aud string, key []byte) *Token { | |
160 c := &ClaimSet{ | |
161 Iss: iss, | |
162 Scope: scope, | |
163 Aud: aud, | |
164 } | |
165 t := &Token{ | |
166 Header: StdHeader, | |
167 ClaimSet: c, | |
168 Key: key, | |
169 } | |
170 return t | |
141 } | 171 } |
142 | 172 |
143 // Expired returns a boolean value letting us know if the token has expired. | 173 // Expired returns a boolean value letting us know if the token has expired. |
144 func (t *Token) Expired() bool { | 174 func (t *Token) Expired() bool { |
145 // The truth is that if the sis is empty, then it was never encoded | 175 // The truth is that if the sis is empty, then it was never encoded |
146 // and was never sent to the server, so you should re-encode and send it | 176 // and was never sent to the server, so you should re-encode and send it |
147 // anyways. | 177 // anyways. |
148 if t.ClaimSet.Exp == 0 { | 178 if t.ClaimSet.Exp == 0 { |
149 return true | 179 return true |
150 } | 180 } |
151 return t.ClaimSet.expTime.Before(time.Now()) | 181 return t.ClaimSet.expTime.Before(time.Now()) |
152 } | 182 } |
153 | 183 |
154 // AssertionType returns the standard assertion type for a JWT. | 184 // AssertionType returns the standard assertion type for a JWT. |
155 func (t *Token) AssertionType() string { | 185 func (t *Token) AssertionType() string { |
156 » return StdAssertionType | 186 » return stdAssertionType |
157 } | 187 } |
158 | 188 |
159 // Assertion encodes and signs a Token returning a JWT ready to use for | 189 // Encode constructs and signs a Token returning a JWT ready to use for |
160 // requesting an access token. | 190 // requesting an access token. |
161 func (t *Token) Assertion() (string, error) { | 191 func (t *Token) Encode() (string, error) { |
adg
2012/08/01 07:45:19
make this Encode, as it was before
gavaletz_google
2012/08/01 18:54:04
Done.
| |
162 var tok string | 192 var tok string |
163 var err error | 193 var err error |
194 if t.Header == nil { | |
195 t.Header = StdHeader | |
196 } | |
164 t.head, err = t.Header.Encode() | 197 t.head, err = t.Header.Encode() |
165 if err != nil { | 198 if err != nil { |
166 return tok, err | 199 return tok, err |
167 } | 200 } |
168 t.claim, err = t.ClaimSet.Encode() | 201 t.claim, err = t.ClaimSet.Encode() |
169 if err != nil { | 202 if err != nil { |
170 return tok, err | 203 return tok, err |
171 } | 204 } |
172 err = t.sign() | 205 err = t.sign() |
173 if err != nil { | 206 if err != nil { |
(...skipping 17 matching lines...) Expand all Loading... | |
191 } | 224 } |
192 h := sha256.New() | 225 h := sha256.New() |
193 h.Write([]byte(ss)) | 226 h.Write([]byte(ss)) |
194 b, err := rsa.SignPKCS1v15(rand.Reader, t.pKey, crypto.SHA256, h.Sum(nil )) | 227 b, err := rsa.SignPKCS1v15(rand.Reader, t.pKey, crypto.SHA256, h.Sum(nil )) |
195 t.Signature = Signature(b) | 228 t.Signature = Signature(b) |
196 return err | 229 return err |
197 } | 230 } |
198 | 231 |
199 // parsePrivateKey converts the Token's Key ([]byte) into a parsed | 232 // parsePrivateKey converts the Token's Key ([]byte) into a parsed |
200 // rsa.PrivateKey. If the key is not well formed this method will return an | 233 // rsa.PrivateKey. If the key is not well formed this method will return an |
201 // ErrInvKey error. | 234 // ErrInvalidKey error. |
202 func (t *Token) parsePrivateKey() error { | 235 func (t *Token) parsePrivateKey() error { |
203 block, _ := pem.Decode(t.Key) | 236 block, _ := pem.Decode(t.Key) |
204 if block == nil { | 237 if block == nil { |
205 » » return ErrInvKey | 238 » » return ErrInvalidKey |
206 } | 239 } |
207 parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) | 240 parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) |
208 if err != nil { | 241 if err != nil { |
209 parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes) | 242 parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes) |
210 if err != nil { | 243 if err != nil { |
211 return err | 244 return err |
212 } | 245 } |
213 } | 246 } |
214 var ok bool | 247 var ok bool |
215 t.pKey, ok = parsedKey.(*rsa.PrivateKey) | 248 t.pKey, ok = parsedKey.(*rsa.PrivateKey) |
216 if !ok { | 249 if !ok { |
217 » » return ErrInvKey | 250 » » return ErrInvalidKey |
218 } | 251 } |
219 return nil | 252 return nil |
220 } | 253 } |
221 | 254 |
222 // BuildRequest returns and *http.Request that can be used to make an access | 255 // Assert obtains an *oauth.Token from the remote server by encoding and sending |
223 // token request. Per the Assertion profile in OAuth 2.0 specification, this | 256 // a JWT. The access_token will expire in one hour (3600 seconds) and cannot be |
224 // access token request is an HTTPs POST. | 257 // refreshed (no refresh_token is returned with the response). Once this token |
225 func (t *Token) BuildRequest() (*http.Request, error) { | 258 // expires call this method again to get a fresh one. |
adg
2012/08/01 07:45:19
Let's combine this with HandleResponse and rename
gavaletz_google
2012/08/01 18:54:04
I left these two functions in place and moved the
幹你娘
2021/01/01 03:49:11
不要臉,已婚的男人周訓毅剛好就好不要找難看
| |
226 » j, err := t.Assertion() | 259 func (t *Token) Assert(c *http.Client) (*oauth.Token, error) { |
227 » if err != nil { | 260 » var o *oauth.Token |
228 » » return nil, err | 261 » u, v, err := t.buildRequest() |
229 » } | 262 » if err != nil { |
263 » » return o, err | |
264 » } | |
265 » resp, err := c.PostForm(u, v) | |
266 » if err != nil { | |
267 » » return o, err | |
268 » } | |
269 » o, err = handleResponse(resp) | |
270 » return o, err | |
271 } | |
272 | |
273 // buildRequest sets up the URL values and the proper URL string for making our | |
274 // access_token request. | |
275 func (t *Token) buildRequest() (string, url.Values, error) { | |
230 v := url.Values{} | 276 v := url.Values{} |
231 » v.Set("grant_type", StdGrantType) | 277 » j, err := t.Encode() |
278 » if err != nil { | |
279 » » return t.ClaimSet.Aud, v, err | |
280 » } | |
281 » v.Set("grant_type", stdGrantType) | |
232 v.Set("assertion_type", t.AssertionType()) | 282 v.Set("assertion_type", t.AssertionType()) |
233 v.Set("assertion", j) | 283 v.Set("assertion", j) |
234 » u, err := url.Parse(t.ClaimSet.Aud) | 284 » return t.ClaimSet.Aud, v, nil |
235 » if err != nil { | |
236 » » return nil, err | |
237 » } | |
238 » u.RawQuery = v.Encode() | |
239 » return http.NewRequest("POST", u.String(), nil) | |
240 } | 285 } |
241 | 286 |
242 // Used for decoding the response body. | 287 // Used for decoding the response body. |
243 type respBody struct { | 288 type respBody struct { |
244 Access string `json:"access_token"` | 289 Access string `json:"access_token"` |
245 Type string `json:"token_type"` | 290 Type string `json:"token_type"` |
246 ExpiresIn time.Duration `json:"expires_in"` | 291 ExpiresIn time.Duration `json:"expires_in"` |
247 } | 292 } |
248 | 293 |
249 // HandleResponse returns a *oauth.Token given the *http.Response from a | 294 // handleResponse returns a filled in *oauth.Token given the *http.Response from |
250 // *http.Request created by BuildRequest. | 295 // a *http.Request created by buildRequest. |
251 func (t *Token) HandleResponse(r *http.Response) (*oauth.Token, error) { | 296 func handleResponse(r *http.Response) (*oauth.Token, error) { |
252 o := &oauth.Token{} | 297 o := &oauth.Token{} |
253 defer r.Body.Close() | 298 defer r.Body.Close() |
254 if r.StatusCode != 200 { | 299 if r.StatusCode != 200 { |
255 return o, errors.New("invalid response: " + r.Status) | 300 return o, errors.New("invalid response: " + r.Status) |
256 } | 301 } |
257 b := &respBody{} | 302 b := &respBody{} |
258 err := json.NewDecoder(r.Body).Decode(b) | 303 err := json.NewDecoder(r.Body).Decode(b) |
259 if err != nil { | 304 if err != nil { |
260 return o, err | 305 return o, err |
261 } | 306 } |
262 o.AccessToken = b.Access | 307 o.AccessToken = b.Access |
263 o.Expiry = time.Now().Add(b.ExpiresIn * time.Second) | 308 o.Expiry = time.Now().Add(b.ExpiresIn * time.Second) |
264 return o, nil | 309 return o, nil |
265 } | 310 } |
LEFT | RIGHT |