2014-12-28 23:24:54 +03:00
|
|
|
package jwt_test
|
|
|
|
|
|
|
|
import (
|
2023-04-22 20:13:12 +03:00
|
|
|
"crypto/ed25519"
|
|
|
|
"encoding/hex"
|
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
|
|
|
|
2023-02-21 16:32:25 +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() {
|
2023-04-22 20:13:12 +03:00
|
|
|
publicKey, privateKey, err := ed25519.GenerateKey(nil)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2015-08-18 23:28:52 +03:00
|
|
|
|
2015-07-20 19:20:18 +03:00
|
|
|
// Create the Claims
|
2021-08-22 20:23:13 +03:00
|
|
|
claims := &jwt.RegisteredClaims{
|
2023-04-22 20:13:12 +03:00
|
|
|
ExpiresAt: jwt.NewNumericDate(time.Unix(2516239022, 0)),
|
2015-07-20 19:20:18 +03:00
|
|
|
Issuer: "test",
|
|
|
|
}
|
|
|
|
|
2023-04-22 20:13:12 +03:00
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, claims)
|
|
|
|
ss, err := token.SignedString(privateKey)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate the token
|
|
|
|
tk, err := jwt.ParseWithClaims(ss, claims, func(t *jwt.Token) (interface{}, error) {
|
|
|
|
return publicKey, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
issuer, err := tk.Claims.GetIssuer()
|
|
|
|
fmt.Printf("%v %v", issuer, err)
|
|
|
|
|
|
|
|
//Output: test <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() {
|
2023-04-22 20:13:12 +03:00
|
|
|
_, privateKey, err := ed25519.GenerateKey(nil)
|
2015-08-18 23:28:52 +03:00
|
|
|
|
2023-05-16 00:00:29 +03:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2015-08-18 23:28:52 +03:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2023-03-31 14:20:59 +03:00
|
|
|
// Create claims with multiple fields populated
|
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"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-03-31 14:20:59 +03:00
|
|
|
fmt.Printf("foo: %v\n", claims.Foo)
|
|
|
|
|
2021-08-22 20:23:13 +03:00
|
|
|
// 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",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-04-22 20:13:12 +03:00
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, claims)
|
|
|
|
ss, err := token.SignedString(privateKey)
|
|
|
|
|
|
|
|
fmt.Println(len(ss), err)
|
2021-08-22 20:23:13 +03:00
|
|
|
|
2023-04-22 20:13:12 +03:00
|
|
|
// Output: foo: bar
|
|
|
|
// 182 <nil>
|
2015-07-20 19:20:18 +03:00
|
|
|
}
|
|
|
|
|
2023-02-21 16:32:25 +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() {
|
2023-04-22 20:13:12 +03:00
|
|
|
tokenString := "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiZXhwIjoyNTE2MjM5MDIyfQ.JutXls8z2IUxAtUgCV2Ec7WRVKrTYX5gCByB0mGLJw0qC9xah3YwH9E82U3QZAPQOOXAalhEFP92KYEWAyITDw"
|
|
|
|
|
|
|
|
// Corresponding private key is a426d77a4edabfdef2830223c9e94e68c5ba1006d1d7ba2a8277ada9b3f93d5c9939cf856f57ee490076a5cc0104b7ae7d458be275cd1cc6fb91b509413e7f56
|
|
|
|
publicKeyBytes, err := hex.DecodeString("9939cf856f57ee490076a5cc0104b7ae7d458be275cd1cc6fb91b509413e7f56")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
publicKey := ed25519.PublicKey(publicKeyBytes)
|
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) {
|
2023-04-22 20:13:12 +03:00
|
|
|
return publicKey, 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 {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2023-02-21 16:32:25 +03:00
|
|
|
// 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_validationOptions() {
|
2023-04-22 20:13:12 +03:00
|
|
|
tokenString := "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiZXhwIjoyNTE2MjM5MDIyfQ.JutXls8z2IUxAtUgCV2Ec7WRVKrTYX5gCByB0mGLJw0qC9xah3YwH9E82U3QZAPQOOXAalhEFP92KYEWAyITDw"
|
|
|
|
|
|
|
|
// Corresponding private key is a426d77a4edabfdef2830223c9e94e68c5ba1006d1d7ba2a8277ada9b3f93d5c9939cf856f57ee490076a5cc0104b7ae7d458be275cd1cc6fb91b509413e7f56
|
|
|
|
publicKeyBytes, err := hex.DecodeString("9939cf856f57ee490076a5cc0104b7ae7d458be275cd1cc6fb91b509413e7f56")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
publicKey := ed25519.PublicKey(publicKeyBytes)
|
2023-02-21 16:32:25 +03:00
|
|
|
|
|
|
|
type MyCustomClaims struct {
|
|
|
|
Foo string `json:"foo"`
|
|
|
|
jwt.RegisteredClaims
|
|
|
|
}
|
|
|
|
|
|
|
|
token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
|
2023-04-22 20:13:12 +03:00
|
|
|
return publicKey, nil
|
2023-02-21 16:32:25 +03:00
|
|
|
}, jwt.WithLeeway(5*time.Second))
|
|
|
|
|
|
|
|
if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
|
|
|
|
fmt.Printf("%v %v", claims.Foo, claims.RegisteredClaims.Issuer)
|
|
|
|
} else {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output: bar test
|
|
|
|
}
|
|
|
|
|
|
|
|
type MyCustomClaims struct {
|
|
|
|
Foo string `json:"foo"`
|
|
|
|
jwt.RegisteredClaims
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate can be used to execute additional application-specific claims
|
|
|
|
// validation.
|
|
|
|
func (m MyCustomClaims) Validate() error {
|
|
|
|
if m.Foo != "bar" {
|
|
|
|
return errors.New("must be foobar")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 and the function
|
|
|
|
// CustomValidation is implemented.
|
|
|
|
func ExampleParseWithClaims_customValidation() {
|
2023-04-22 20:13:12 +03:00
|
|
|
tokenString := "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiZXhwIjoyNTE2MjM5MDIyfQ.JutXls8z2IUxAtUgCV2Ec7WRVKrTYX5gCByB0mGLJw0qC9xah3YwH9E82U3QZAPQOOXAalhEFP92KYEWAyITDw"
|
|
|
|
|
|
|
|
// Corresponding private key is a426d77a4edabfdef2830223c9e94e68c5ba1006d1d7ba2a8277ada9b3f93d5c9939cf856f57ee490076a5cc0104b7ae7d458be275cd1cc6fb91b509413e7f56
|
|
|
|
publicKeyBytes, err := hex.DecodeString("9939cf856f57ee490076a5cc0104b7ae7d458be275cd1cc6fb91b509413e7f56")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
publicKey := ed25519.PublicKey(publicKeyBytes)
|
2023-02-21 16:32:25 +03:00
|
|
|
|
|
|
|
token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
|
2023-04-22 20:13:12 +03:00
|
|
|
return publicKey, nil
|
2023-02-21 16:32:25 +03:00
|
|
|
}, jwt.WithLeeway(5*time.Second))
|
|
|
|
|
|
|
|
if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
|
|
|
|
fmt.Printf("%v %v", claims.Foo, claims.RegisteredClaims.Issuer)
|
|
|
|
} else {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output: bar test
|
|
|
|
}
|
|
|
|
|
|
|
|
// An example of parsing the error types using errors.Is.
|
2016-04-12 23:18:31 +03:00
|
|
|
func ExampleParse_errorChecking() {
|
|
|
|
// Token from another example. This token is expired
|
2023-04-22 20:13:12 +03:00
|
|
|
var tokenString = "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpc3MiOiJ0ZXN0IiwiZXhwIjoxNTE2MjM5MDIyfQ.XAxYEk_6fWzMe256fXeT-eDR1vo_t4gqkq3eD4lqKHm8VrvhnOBtIrfXAJvMY6S1c5Lb1CIhPAaCe366xsYJDg"
|
|
|
|
|
|
|
|
// Corresponding private key is 378446363ffa1eb526a61f4b250bd24bd60002d5d20e22fa9b20c786e7f5e2ea8fff42935411c4c9bd3772c4a96a710bf6f2ba5508a71fc6155bcd73eb952837
|
|
|
|
publicKeyBytes, err := hex.DecodeString("8fff42935411c4c9bd3772c4a96a710bf6f2ba5508a71fc6155bcd73eb952837")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
publicKey := ed25519.PublicKey(publicKeyBytes)
|
2016-04-12 23:18:31 +03:00
|
|
|
|
|
|
|
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
2023-04-22 20:13:12 +03:00
|
|
|
return publicKey, 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")
|
2023-03-25 01:11:38 +03:00
|
|
|
} else if errors.Is(err, jwt.ErrTokenSignatureInvalid) {
|
|
|
|
// Invalid signature
|
|
|
|
fmt.Println("Invalid signature")
|
2022-01-20 00:55:19 +03:00
|
|
|
} 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
|
|
|
}
|