Merge pull request #128 from dgrijalva/dg/example_code

Move examples into testable files instead of README
This commit is contained in:
Dave Grijalva 2016-04-12 14:36:32 -07:00
commit 561ef5eec0
3 changed files with 125 additions and 62 deletions

View File

@ -7,6 +7,8 @@ A [go](http://www.golang.org) (or 'golang' for search engine friendliness) imple
## What the heck is a JWT? ## What the heck is a JWT?
JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web Tokens.
In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](http://tools.ietf.org/html/rfc4648) encoded. The last part is the signature, encoded the same way. In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](http://tools.ietf.org/html/rfc4648) encoded. The last part is the signature, encoded the same way.
The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used. The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used.
@ -17,37 +19,13 @@ The part in the middle is the interesting bit. It's called the Claims and conta
This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own. This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own.
## Parse and Verify ## Examples
Parsing and verifying tokens is pretty straight forward. You pass in the token and a function for looking up the key. This is done as a callback since you may need to parse the token to find out what signing method and key was used. See [the project documentation](https://godoc.org/github.com/dgrijalva/jwt-go) for examples of usage:
```go * [Simple example of parsing and validating a token](https://godoc.org/github.com/dgrijalva/jwt-go#example_Parse_hmac)
token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) { * [Simple example of building and signing a token](https://godoc.org/github.com/dgrijalva/jwt-go#example_New_hmac)
// Don't forget to validate the alg is what you expect: * [Directory of Examples](https://godoc.org/github.com/dgrijalva/jwt-go#pkg-examples)
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return myLookupKey(token.Header["kid"]), nil
})
if err == nil && token.Valid {
deliverGoodness("!")
} else {
deliverUtterRejection(":(")
}
```
## Create a token
```go
// Create the token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"foo": "bar",
"exp": time.Now().Add(time.Hour * 72).Unix(),
})
// Sign and get the complete encoded token as a string
tokenString, err := token.SignedString(mySigningKey)
```
## Extensions ## Extensions

View File

@ -2,41 +2,20 @@ package jwt_test
import ( import (
"fmt" "fmt"
"time"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"time"
) )
func ExampleParse(myToken string, myLookupKey func(interface{}) (interface{}, error)) { // Example (atypical) using the StandardClaims type by itself to parse a token.
token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) { // The StandardClaims type is designed to be embedded into your custom types
return myLookupKey(token.Header["kid"]) // to provide standard validation features. You can use it alone, but there's
}) // no way to retrieve other fields after parsing.
// See the CustomClaimsType example for intended usage.
if err == nil && token.Valid { func ExampleNewWithClaims_standardClaims() {
fmt.Println("Your token is valid. I like your style.")
} else {
fmt.Println("This token is terrible! I cannot accept this.")
}
}
func ExampleNew() {
// Create the token
token := jwt.New(jwt.SigningMethodRS256)
// Set some claims
claims := token.Claims.(jwt.MapClaims)
claims["foo"] = "bar"
claims["exp"] = time.Unix(0, 0).Add(time.Hour * 1).Unix()
fmt.Printf("<%T> foo:%v exp:%v\n", token.Claims, claims["foo"], claims["exp"])
//Output: <jwt.MapClaims> foo:bar exp:3600
}
func ExampleNewWithClaims() {
mySigningKey := []byte("AllYourBase") mySigningKey := []byte("AllYourBase")
// Create the Claims // Create the Claims
claims := jwt.StandardClaims{ claims := &jwt.StandardClaims{
ExpiresAt: 15000, ExpiresAt: 15000,
Issuer: "test", Issuer: "test",
} }
@ -47,7 +26,9 @@ func ExampleNewWithClaims() {
//Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.QsODzZu3lUZMVdhbO76u3Jv02iYCvEHcYVUI1kOWEU0 <nil> //Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.QsODzZu3lUZMVdhbO76u3Jv02iYCvEHcYVUI1kOWEU0 <nil>
} }
func ExampleNewWithClaims_customType() { // Example creating a token using a custom claims type. The StandardClaim is embedded
// in the custom type to allow for easy encoding, parsing and validation of standard claims.
func ExampleNewWithClaims_customClaimsType() {
mySigningKey := []byte("AllYourBase") mySigningKey := []byte("AllYourBase")
type MyCustomClaims struct { type MyCustomClaims struct {
@ -70,9 +51,48 @@ func ExampleNewWithClaims_customType() {
//Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c <nil> //Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c <nil>
} }
func ExampleParse_errorChecking(myToken string, myLookupKey func(interface{}) (interface{}, error)) { // Example creating a token using a custom claims type. The StandardClaim is embedded
token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) { // in the custom type to allow for easy encoding, parsing and validation of standard claims.
return myLookupKey(token.Header["kid"]) func ExampleParseWithClaims_customClaimsType() {
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c"
type MyCustomClaims struct {
Foo string `json:"foo"`
jwt.StandardClaims
}
// sample token is expired. override time so it parses as valid
at(time.Unix(0, 0), func() {
token, err := jwt.ParseWithClaims(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte("AllYourBase"), nil
}, &MyCustomClaims{})
if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
fmt.Printf("%v %v", claims.Foo, claims.StandardClaims.ExpiresAt)
} else {
fmt.Println(err)
}
})
// Output: bar 15000
}
// Override time value for tests. Restore default value after.
func at(t time.Time, f func()) {
jwt.TimeFunc = func() time.Time {
return t
}
f()
jwt.TimeFunc = time.Now
}
// An example of parsing the error types using bitfield checks
func ExampleParse_errorChecking() {
// Token from another example. This token is expired
var tokenString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c"
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte("AllYourBase"), nil
}) })
if token.Valid { if token.Valid {
@ -90,4 +110,5 @@ func ExampleParse_errorChecking(myToken string, myLookupKey func(interface{}) (i
fmt.Println("Couldn't handle this token:", err) fmt.Println("Couldn't handle this token:", err)
} }
// Output: Timing is everything
} }

64
hmac_example_test.go Normal file
View File

@ -0,0 +1,64 @@
package jwt_test
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"io/ioutil"
"time"
)
// For HMAC signing method, the key can be any []byte. It is recommended to generate
// a key using crypto/rand or something equivalent. You need the same key for signing
// and validating.
var hmacSampleSecret []byte
func init() {
// Load sample key data
if keyData, e := ioutil.ReadFile("test/hmacTestKey"); e == nil {
hmacSampleSecret = keyData
} else {
panic(e)
}
}
// Example creating, signing, and encoding a JWT token using the HMAC signing method
func ExampleNew_hmac() {
// Create a new token object, specifying signing method and the claims
// you would like it to contain.
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"foo": "bar",
"nbf": time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(),
})
// Sign and get the complete encoded token as a string using the secret
tokenString, err := token.SignedString(hmacSampleSecret)
fmt.Println(tokenString, err)
// Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.u1riaD1rW97opCoAuRCTy4w58Br-Zk-bh7vLiRIsrpU <nil>
}
// Example parsing and validating a token using the HMAC signing method
func ExampleParse_hmac() {
// sample token string taken from the New example
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJuYmYiOjE0NDQ0Nzg0MDB9.u1riaD1rW97opCoAuRCTy4w58Br-Zk-bh7vLiRIsrpU"
// Parse takes the token string and a function for looking up the key. The latter is especially
// useful if you use multiple keys for your application. The standard is to use 'kid' in the
// head of the token to identify which key to use, but the parsed token (head and claims) is provided
// to the callback, providing flexibility.
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect:
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return hmacSampleSecret, nil
})
if claims, ok := token.Claims.(*jwt.MapClaims); ok && token.Valid {
fmt.Println((*claims)["foo"], (*claims)["nbf"])
} else {
fmt.Println(err)
}
// Output: bar 1.4444784e+09
}