2014-12-28 23:24:54 +03:00
|
|
|
package jwt_test
|
|
|
|
|
|
|
|
import (
|
2022-01-20 00:55:19 +03:00
|
|
|
"errors"
|
2014-12-28 23:24:54 +03:00
|
|
|
"fmt"
|
2016-04-12 23:33:20 +03:00
|
|
|
"time"
|
2021-05-29 04:26:41 +03:00
|
|
|
|
2022-08-27 12:36:37 +03:00
|
|
|
"github.com/golang-jwt/jwt/v5"
|
2014-12-28 23:24:54 +03:00
|
|
|
)
|
|
|
|
|
2021-08-22 20:23:13 +03:00
|
|
|
// Example (atypical) using the RegisteredClaims type by itself to parse a token.
|
|
|
|
// The RegisteredClaims type is designed to be embedded into your custom types
|
2016-04-12 23:18:31 +03:00
|
|
|
// 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.
|
2021-08-22 20:23:13 +03:00
|
|
|
func ExampleNewWithClaims_registeredClaims() {
|
2015-08-18 23:28:52 +03:00
|
|
|
mySigningKey := []byte("AllYourBase")
|
|
|
|
|
2015-07-20 19:20:18 +03:00
|
|
|
// Create the Claims
|
2021-08-22 20:23:13 +03:00
|
|
|
claims := &jwt.RegisteredClaims{
|
|
|
|
ExpiresAt: jwt.NewNumericDate(time.Unix(1516239022, 0)),
|
2015-07-20 19:20:18 +03:00
|
|
|
Issuer: "test",
|
|
|
|
}
|
|
|
|
|
2015-08-18 23:28:52 +03:00
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
|
|
ss, err := token.SignedString(mySigningKey)
|
|
|
|
fmt.Printf("%v %v", ss, err)
|
2021-08-22 20:23:13 +03:00
|
|
|
//Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0IiwiZXhwIjoxNTE2MjM5MDIyfQ.0XN_1Tpp9FszFOonIBpwha0c_SfnNI22DhTnjMshPg8 <nil>
|
2015-08-18 23:28:52 +03:00
|
|
|
}
|
|
|
|
|
2021-08-22 20:23:13 +03:00
|
|
|
// Example creating a token using a custom claims type. The RegisteredClaims is embedded
|
|
|
|
// in the custom type to allow for easy encoding, parsing and validation of registered claims.
|
2016-04-12 23:18:31 +03:00
|
|
|
func ExampleNewWithClaims_customClaimsType() {
|
2015-08-18 23:28:52 +03:00
|
|
|
mySigningKey := []byte("AllYourBase")
|
|
|
|
|
|
|
|
type MyCustomClaims struct {
|
|
|
|
Foo string `json:"foo"`
|
2021-08-22 20:23:13 +03:00
|
|
|
jwt.RegisteredClaims
|
2015-08-18 23:28:52 +03:00
|
|
|
}
|
|
|
|
|
2021-08-22 20:23:13 +03:00
|
|
|
// Create the claims
|
2015-08-18 23:28:52 +03:00
|
|
|
claims := MyCustomClaims{
|
|
|
|
"bar",
|
2021-08-22 20:23:13 +03:00
|
|
|
jwt.RegisteredClaims{
|
|
|
|
// A usual scenario is to set the expiration time relative to the current time
|
|
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
|
|
|
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
|
|
NotBefore: jwt.NewNumericDate(time.Now()),
|
|
|
|
Issuer: "test",
|
|
|
|
Subject: "somebody",
|
|
|
|
ID: "1",
|
|
|
|
Audience: []string{"somebody_else"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create claims while leaving out some of the optional fields
|
|
|
|
claims = MyCustomClaims{
|
|
|
|
"bar",
|
|
|
|
jwt.RegisteredClaims{
|
|
|
|
// Also fixed dates can be used for the NumericDate
|
|
|
|
ExpiresAt: jwt.NewNumericDate(time.Unix(1516239022, 0)),
|
2015-08-18 23:28:52 +03:00
|
|
|
Issuer: "test",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
|
|
ss, err := token.SignedString(mySigningKey)
|
|
|
|
fmt.Printf("%v %v", ss, err)
|
2021-08-22 20:23:13 +03:00
|
|
|
|
|
|
|
//Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiZXhwIjoxNTE2MjM5MDIyfQ.xVuY2FZ_MRXMIEgVQ7J-TFtaucVFRXUzHm9LmV41goM <nil>
|
2015-07-20 19:20:18 +03:00
|
|
|
}
|
|
|
|
|
New Validation API
Some guidelines in designing the new validation API
* Previously, the `Valid` method was placed on the claim, which was always not entirely semantically correct, since the validity is concerning the token, not the claims. Although the validity of the token is based on the processing of the claims (such as `exp`). Therefore, the function `Valid` was removed from the `Claims` interface and the single canonical way to retrieve the validity of the token is to retrieve the `Valid` property of the `Token` struct.
* The previous fact was enhanced by the fact that most claims implementations had additional exported `VerifyXXX` functions, which are now removed
* All validation errors should be comparable with `errors.Is` to determine, why a particular validation has failed
* Developers want to adjust validation options. Popular options include:
* Leeway when processing exp, nbf, iat
* Not verifying `iat`, since this is actually just an informational claim. When purely looking at the standard, this should probably the default
* Verifying `aud` by default, which actually the standard sort of demands. We need to see how strong we want to enforce this
* Developers want to create their own claim types, mostly by embedding one of the existing types such as `RegisteredClaims`.
* Sometimes there is the need to further tweak the validation of a token by checking the value of a custom claim. Previously, this was possibly by overriding `Valid`. However, this was error-prone, e.g., if the original `Valid` was not called. Therefore, we should provide an easy way for *additional* checks, without by-passing the necessary validations
This leads to the following two major changes:
* The `Claims` interface now represents a set of functions that return the mandatory claims represented in a token, rather than just a `Valid` function. This is also more semantically correct.
* All validation tasks are offloaded to a new (optional) `Validator`, which can also be configured with appropriate options. If no custom validator was supplied, a default one is used.
2022-08-27 13:07:09 +03:00
|
|
|
// Example creating a token using a custom claims type. The RegisteredClaims is embedded
|
2016-04-12 23:33:20 +03:00
|
|
|
// in the custom type to allow for easy encoding, parsing and validation of standard claims.
|
|
|
|
func ExampleParseWithClaims_customClaimsType() {
|
2021-08-22 20:23:13 +03:00
|
|
|
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiYXVkIjoic2luZ2xlIn0.QAWg1vGvnqRuCFTMcPkjZljXHh8U3L_qUjszOtQbeaA"
|
2016-04-12 23:33:20 +03:00
|
|
|
|
|
|
|
type MyCustomClaims struct {
|
|
|
|
Foo string `json:"foo"`
|
2021-08-22 20:23:13 +03:00
|
|
|
jwt.RegisteredClaims
|
2016-04-12 23:33:20 +03:00
|
|
|
}
|
|
|
|
|
2021-08-22 20:23:13 +03:00
|
|
|
token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
|
|
|
|
return []byte("AllYourBase"), nil
|
2016-04-12 23:33:20 +03:00
|
|
|
})
|
|
|
|
|
2021-08-22 20:23:13 +03:00
|
|
|
if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
|
New Validation API
Some guidelines in designing the new validation API
* Previously, the `Valid` method was placed on the claim, which was always not entirely semantically correct, since the validity is concerning the token, not the claims. Although the validity of the token is based on the processing of the claims (such as `exp`). Therefore, the function `Valid` was removed from the `Claims` interface and the single canonical way to retrieve the validity of the token is to retrieve the `Valid` property of the `Token` struct.
* The previous fact was enhanced by the fact that most claims implementations had additional exported `VerifyXXX` functions, which are now removed
* All validation errors should be comparable with `errors.Is` to determine, why a particular validation has failed
* Developers want to adjust validation options. Popular options include:
* Leeway when processing exp, nbf, iat
* Not verifying `iat`, since this is actually just an informational claim. When purely looking at the standard, this should probably the default
* Verifying `aud` by default, which actually the standard sort of demands. We need to see how strong we want to enforce this
* Developers want to create their own claim types, mostly by embedding one of the existing types such as `RegisteredClaims`.
* Sometimes there is the need to further tweak the validation of a token by checking the value of a custom claim. Previously, this was possibly by overriding `Valid`. However, this was error-prone, e.g., if the original `Valid` was not called. Therefore, we should provide an easy way for *additional* checks, without by-passing the necessary validations
This leads to the following two major changes:
* The `Claims` interface now represents a set of functions that return the mandatory claims represented in a token, rather than just a `Valid` function. This is also more semantically correct.
* All validation tasks are offloaded to a new (optional) `Validator`, which can also be configured with appropriate options. If no custom validator was supplied, a default one is used.
2022-08-27 13:07:09 +03:00
|
|
|
fmt.Printf("%v %v", claims.Foo, claims.RegisteredClaims.Issuer)
|
|
|
|
} else {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output: bar test
|
|
|
|
}
|
|
|
|
|
|
|
|
// Example creating a token using a custom claims type and validation options. The RegisteredClaims is embedded
|
|
|
|
// in the custom type to allow for easy encoding, parsing and validation of standard claims.
|
|
|
|
func ExampleParseWithClaims_customValidator() {
|
|
|
|
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiYXVkIjoic2luZ2xlIn0.QAWg1vGvnqRuCFTMcPkjZljXHh8U3L_qUjszOtQbeaA"
|
|
|
|
|
|
|
|
type MyCustomClaims struct {
|
|
|
|
Foo string `json:"foo"`
|
|
|
|
jwt.RegisteredClaims
|
|
|
|
}
|
|
|
|
|
|
|
|
validator := jwt.NewValidator(jwt.WithLeeway(5 * time.Second))
|
|
|
|
token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
|
|
|
|
return []byte("AllYourBase"), nil
|
|
|
|
}, jwt.WithValidator(validator))
|
|
|
|
|
|
|
|
if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
|
2021-08-22 20:23:13 +03:00
|
|
|
fmt.Printf("%v %v", claims.Foo, claims.RegisteredClaims.Issuer)
|
|
|
|
} else {
|
|
|
|
fmt.Println(err)
|
2016-04-12 23:33:20 +03:00
|
|
|
}
|
2021-08-22 20:23:13 +03:00
|
|
|
|
|
|
|
// Output: bar test
|
2016-04-12 23:33:20 +03:00
|
|
|
}
|
|
|
|
|
2016-04-12 23:18:31 +03:00
|
|
|
// 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
|
2014-12-28 23:44:46 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
if token.Valid {
|
|
|
|
fmt.Println("You look nice today")
|
2022-01-20 00:55:19 +03:00
|
|
|
} else if errors.Is(err, jwt.ErrTokenMalformed) {
|
|
|
|
fmt.Println("That's not even a token")
|
|
|
|
} else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) {
|
|
|
|
// Token is either expired or not active yet
|
|
|
|
fmt.Println("Timing is everything")
|
2014-12-28 23:44:46 +03:00
|
|
|
} else {
|
|
|
|
fmt.Println("Couldn't handle this token:", err)
|
|
|
|
}
|
|
|
|
|
2016-04-12 23:18:31 +03:00
|
|
|
// Output: Timing is everything
|
2014-12-28 23:44:46 +03:00
|
|
|
}
|