forked from mirror/jwt
updated documentation. moved migration guide to separate doc
This commit is contained in:
parent
0fbf86c98b
commit
2ed748cd9c
|
@ -0,0 +1,96 @@
|
||||||
|
## Migration Guide from v2 -> v3
|
||||||
|
|
||||||
|
Version 3 adds several new, frequently requested features. To do so, it introduces a few breaking changes. We've worked to keep these as minimal as possible. This guide explains the breaking changes and how you can quickly update your code.
|
||||||
|
|
||||||
|
### `Token.Claims` is now an interface type
|
||||||
|
|
||||||
|
The most requested feature from the 2.0 verison of this library was the ability to provide a custom type to the JSON parser for claims. This was implemented by introducing a new interface, `Claims`, to replace `map[string]interface{}`. We also included two concrete implementations of `Claims`: `MapClaims` and `StandardClaims`.
|
||||||
|
|
||||||
|
`MapClaims` is an alias for `map[string]interface{}` with built in validation behavior. It is the default claims type when using `Parse`. The usage is unchanged except you must type cast the claims property.
|
||||||
|
|
||||||
|
The old example for parsing a token looked like this..
|
||||||
|
|
||||||
|
```go
|
||||||
|
if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil {
|
||||||
|
fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
is now directly mapped to...
|
||||||
|
|
||||||
|
```go
|
||||||
|
if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil {
|
||||||
|
claims := token.Claims.(jwt.MapClaims)
|
||||||
|
fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`StandardClaims` is designed to be embedded in your custom type. You can supply a custom claims type with the new `ParseWithClaims` function. Here's an example of using a custom claims type.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MyCustomClaims struct {
|
||||||
|
User string
|
||||||
|
*StandardClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
if token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, keyLookupFunc); err == nil {
|
||||||
|
claims := token.Claims.(*MyCustomClaims)
|
||||||
|
fmt.Printf("Token for user %v expires %v", claims.User, claims.StandardClaims.ExpiresAt)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `ParseFromRequest` has been moved
|
||||||
|
|
||||||
|
To keep this library focused on the tokens without becoming overburdened with complex request processing logic, `ParseFromRequest` and its new companion `ParseFromRequestWithClaims` have been moved to a subpackage, `request`. The method signatues have also been augmented to receive a new argument: `Extractor`.
|
||||||
|
|
||||||
|
`Extractors` do the work of picking the token string out of a request. The interface is simple and composable.
|
||||||
|
|
||||||
|
This simple parsing example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if token, err := jwt.ParseFromRequest(tokenString, req, keyLookupFunc); err == nil {
|
||||||
|
fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
is directly mapped to:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if token, err := request.ParseFromRequest(tokenString, request.OAuth2Extractor, req, keyLookupFunc); err == nil {
|
||||||
|
fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
There are several concrete `Extractor` types provided for your convenience:
|
||||||
|
|
||||||
|
* `HeaderExtractor` will search a list of headers until one contains content.
|
||||||
|
* `ArgumentExtractor` will search a list of keys in request query and form arguments until one contains content.
|
||||||
|
* `MultiExtractor` will try a list of `Extractors` in order until one returns content.
|
||||||
|
* `AuthorizationHeaderExtractor` will look in the `Authorization` header for a `Bearer` token.
|
||||||
|
* `OAuth2Extractor` searches the places an OAuth2 token would be specified (per the spec): `Authorization` header and `access_token` argument
|
||||||
|
* `PostExtractionFilter` wraps an `Extractor`, allowing you to process the content before it's parsed. A simple example is stripping the `Bearer ` text from a header
|
||||||
|
|
||||||
|
|
||||||
|
### RSA signing methods no longer accept `[]byte` keys
|
||||||
|
|
||||||
|
Due to a [critical vulnerability](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/), we've decided the convenience of accepting `[]byte` instead of `rsa.PublicKey` or `rsa.PrivateKey` isn't worth the risk of misuse.
|
||||||
|
|
||||||
|
To replace this behavior, we've added two helper methods: `ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error)` and `ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error)`. These are just simple helpers for unpacking PEM encoded PKCS1 and PKCS8 keys. If your keys are encoded any other way, all you need to do is convert them to the `crypto/rsa` package's types.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func keyLookupFunc(*Token) (interface{}, error) {
|
||||||
|
// Don't forget to validate the alg is what you expect:
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||||
|
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up key
|
||||||
|
key, err := lookupPublicKey(token.Header["kid"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack key from PEM encoded PKCS8
|
||||||
|
return jwt.ParseRSAPublicKeyFromPEM(key)
|
||||||
|
}
|
||||||
|
```
|
59
README.md
59
README.md
|
@ -2,7 +2,7 @@ A [go](http://www.golang.org) (or 'golang' for search engine friendliness) imple
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](https://travis-ci.org/dgrijalva/jwt-go)
|
[![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](https://travis-ci.org/dgrijalva/jwt-go)
|
||||||
|
|
||||||
**BREAKING CHANGES COMING:*** Version 3.0.0 is almost complete. It will include _a lot_ of changes including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes will be available before 3.0.0 lands. If you would like to have any input befor 3.0.0 is locked, now's the time to review and provide feedback.
|
**BREAKING CHANGES:*** Version 3.0.0 is here. It includes _a lot_ of changes including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code.
|
||||||
|
|
||||||
**NOTICE:** A vulnerability in JWT was [recently published](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). As this library doesn't force users to validate the `alg` is what they expected, it's possible your usage is effected. There will be an update soon to remedy this, and it will likey require backwards-incompatible changes to the API. In the short term, please make sure your implementation verifies the `alg` is what you expect.
|
**NOTICE:** A vulnerability in JWT was [recently published](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). As this library doesn't force users to validate the `alg` is what they expected, it's possible your usage is effected. There will be an update soon to remedy this, and it will likey require backwards-incompatible changes to the API. In the short term, please make sure your implementation verifies the `alg` is what you expect.
|
||||||
|
|
||||||
|
@ -43,63 +43,6 @@ This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull
|
||||||
|
|
||||||
While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v2`. It will do the right thing WRT semantic versioning.
|
While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v2`. It will do the right thing WRT semantic versioning.
|
||||||
|
|
||||||
## Migration Guide from v2 -> v3
|
|
||||||
|
|
||||||
Added the ability to supply a typed object for the claims section of the token.
|
|
||||||
|
|
||||||
Unfortunately this requires a breaking change. A few new methods were added to support this,
|
|
||||||
and the old default of `map[string]interface{}` was changed to `jwt.MapClaims`.
|
|
||||||
|
|
||||||
The old example for creating a token looked like this..
|
|
||||||
|
|
||||||
```go
|
|
||||||
token := jwt.New(jwt.SigningMethodHS256)
|
|
||||||
token.Claims["foo"] = "bar"
|
|
||||||
token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
|
|
||||||
```
|
|
||||||
|
|
||||||
is now directly mapped to...
|
|
||||||
|
|
||||||
```go
|
|
||||||
token := jwt.New(jwt.SigningMethodHS256)
|
|
||||||
claims := token.Claims.(jwt.MapClaims)
|
|
||||||
claims["foo"] = "bar"
|
|
||||||
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
|
|
||||||
```
|
|
||||||
|
|
||||||
However, we added a helper `jwt.NewWithClaims` which accepts a claims object.
|
|
||||||
|
|
||||||
Any type can now be used as the claim object for inside a token so long as it implements the interface `jwt.Claims`.
|
|
||||||
|
|
||||||
So, we added an additional claim type `jwt.StandardClaims` was added.
|
|
||||||
This is intended to be used as a base for creating your own types from,
|
|
||||||
and includes a few helper functions for verifying the claims defined [here](https://tools.ietf.org/html/rfc7519#section-4.1).
|
|
||||||
|
|
||||||
```go
|
|
||||||
claims := jwt.StandardClaims{
|
|
||||||
Audience: "myapi"
|
|
||||||
ExpiresAt: time.Now().Add(time.Hour * 72).Unix(),
|
|
||||||
}
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
||||||
```
|
|
||||||
|
|
||||||
On the other end of usage all of the `jwt.Parse` and friends got a `WithClaims` suffix added to them.
|
|
||||||
|
|
||||||
```go
|
|
||||||
token, err := jwt.Parse(token, keyFunc)
|
|
||||||
claims := token.Claims.(jwt.MapClaim)
|
|
||||||
//like you used to..
|
|
||||||
claims["foo"]
|
|
||||||
claims["bar"]
|
|
||||||
```
|
|
||||||
|
|
||||||
New method usage:
|
|
||||||
```go
|
|
||||||
token, err := jwt.ParseWithClaims(token, &jwt.StandardClaims{}, keyFunc)
|
|
||||||
claims := token.Claims.(jwt.StandardClaims)
|
|
||||||
fmt.Println(claims.IssuedAt)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage Tips
|
## Usage Tips
|
||||||
|
|
||||||
### Signing vs Encryption
|
### Signing vs Encryption
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
* **Compatibility Breaking Changes**
|
* **Compatibility Breaking Changes**
|
||||||
* Dropped support for `[]byte` keys when using RSA signing methods. This convenience feature could contribute to security vulnerabilities involving mismatched key types with signing methods.
|
* Dropped support for `[]byte` keys when using RSA signing methods. This convenience feature could contribute to security vulnerabilities involving mismatched key types with signing methods.
|
||||||
* Signature of `Keyfunc` is now `func(*Token) (interface{}, error)`
|
|
||||||
* `ParseFromRequest` has been moved to `request` subpackage and usage has changed
|
* `ParseFromRequest` has been moved to `request` subpackage and usage has changed
|
||||||
* The `Claims` property on `Token` is now type `Claims` instead of `map[string]interface{}`. The default value is type `MapClaims`, which is an alias to `map[string]interface{}`. This makes it possible to use a custom type when decoding claims.
|
* The `Claims` property on `Token` is now type `Claims` instead of `map[string]interface{}`. The default value is type `MapClaims`, which is an alias to `map[string]interface{}`. This makes it possible to use a custom type when decoding claims.
|
||||||
* Other Additions and Changes
|
* Other Additions and Changes
|
||||||
|
|
|
@ -33,6 +33,7 @@ func (e HeaderExtractor) ExtractToken(req *http.Request) (string, error) {
|
||||||
|
|
||||||
// Extract token from request arguments. This includes a POSTed form or
|
// Extract token from request arguments. This includes a POSTed form or
|
||||||
// GET URL arguments. Argument names are tried in order until there's a match.
|
// GET URL arguments. Argument names are tried in order until there's a match.
|
||||||
|
// This extractor calls `ParseMultipartForm` on the request
|
||||||
type ArgumentExtractor []string
|
type ArgumentExtractor []string
|
||||||
|
|
||||||
func (e ArgumentExtractor) ExtractToken(req *http.Request) (string, error) {
|
func (e ArgumentExtractor) ExtractToken(req *http.Request) (string, error) {
|
||||||
|
|
Loading…
Reference in New Issue