added config options and nonce

This commit is contained in:
ston1th 2025-10-15 21:49:13 +02:00
commit 473d54569c
10 changed files with 146 additions and 85 deletions

74
jwt.go
View file

@ -1,4 +1,4 @@
// Copyright (C) 2018 Marius Schellenberger
// Copyright (C) 2025 Marius Schellenberger
// Package jwt provides a easy to use JSON Web Token and blacklisting library
package jwt
@ -31,6 +31,8 @@ const (
ExpClaim = "exp"
// NbfClaim is the nbf claim name
NbfClaim = "nbf"
// NonceClaim
NonceClaim = "nonce"
// TokenSeparator is the tokens separator char
TokenSeparator = "."
)
@ -57,14 +59,39 @@ var (
ErrInvalidKeySize = errors.New(jwtErr + "invalid secret key size")
)
type JWTOption func(*JWT)
func WithExpiry(expiry time.Duration) JWTOption {
return func(jwt *JWT) {
jwt.expiry = expiry
}
}
func WithBlacklist(blacklist Blacklist) JWTOption {
return func(jwt *JWT) {
jwt.blacklist = blacklist
}
}
func WithSecret(secret io.Reader) JWTOption {
return func(jwt *JWT) {
jwt.secretReader = secret
}
}
func WithNonce() JWTOption {
return func(jwt *JWT) {
jwt.nonce = true
}
}
// JWT represents the JSON Web Token signing and blacklisting infrastructure
type JWT struct {
key []byte
expiry time.Duration
blacklist Blacklist
stopOnce sync.Once
done chan struct{}
secretReader io.Reader
key []byte
expiry time.Duration
blacklist Blacklist
nonce bool
stopOnce sync.Once
done chan struct{}
}
// New returns a new JWT object with the given expiry timeout.
@ -73,14 +100,18 @@ type JWT struct {
// If secret is nil the DefaultSecretReader is used.
// If blacklisting is enabled, the JWT object leaks a goroutine to garbage-collect expired blacklisted tokens.
// Call the Stop() method to exit the goroutine.
func New(expiry time.Duration, blacklist Blacklist, secret io.Reader) (*JWT, error) {
if expiry <= 0 {
expiry = DefaultExpiry
func New(options ...JWTOption) (*JWT, error) {
jwt := &JWT{}
for _, option := range options {
option(jwt)
}
if secret == nil {
secret = DefaultSecretReader
if jwt.expiry <= 0 {
jwt.expiry = DefaultExpiry
}
secret = io.LimitReader(secret, KeySize)
if jwt.secretReader == nil {
jwt.secretReader = DefaultSecretReader
}
secret := io.LimitReader(jwt.secretReader, KeySize)
key := make([]byte, KeySize)
i, err := secret.Read(key)
if err != nil {
@ -89,12 +120,8 @@ func New(expiry time.Duration, blacklist Blacklist, secret io.Reader) (*JWT, err
if i < KeySize {
return nil, ErrInvalidKeySize
}
jwt := &JWT{
key: key,
expiry: expiry,
blacklist: blacklist,
}
if blacklist != nil {
jwt.key = key
if jwt.blacklist != nil {
jwt.done = make(chan struct{})
go jwt.clean()
}
@ -128,7 +155,7 @@ func (jwt *JWT) Invalidate(t *Token) error {
if t.Header.GetString(TypClaim) != Typ {
return ErrNoJWT
}
h := ParseHash(t.Header.GetString(AlgClaim))
h := NewHash(t.Header.GetString(AlgClaim))
if h == nil {
return ErrUnsupportedAlg
}
@ -190,7 +217,7 @@ func (jwt *JWT) Sign(t *Token) (err error) {
if t.Header.GetString(TypClaim) != Typ {
return ErrNoJWT
}
h := ParseHash(t.Header.GetString(AlgClaim))
h := NewHash(t.Header.GetString(AlgClaim))
if h == nil {
return ErrUnsupportedAlg
}
@ -201,6 +228,9 @@ func (jwt *JWT) Sign(t *Token) (err error) {
if _, ok := t.Claims.Get(NbfClaim); !ok {
t.Claims.Set(NbfClaim, NewNbf(now))
}
if jwt.nonce {
t.Claims.Set(NonceClaim, enc.EncodeToString(key16()))
}
head, err := json.Marshal(t.Header)
if err != nil {
return
@ -230,7 +260,7 @@ func (jwt *JWT) Verify(t *Token) error {
if t.Header.GetString(TypClaim) != Typ {
return ErrNoJWT
}
h := ParseHash(t.Header.GetString(AlgClaim))
h := NewHash(t.Header.GetString(AlgClaim))
if h == nil {
return ErrUnsupportedAlg
}