jwt

package module
v0.0.0-...-62298e2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 17, 2026 License: MIT Imports: 10 Imported by: 2

README

JWT Library

Secure, configurable, test‑friendly JWT utilities for Go: safe revocation, clock skew tolerance, strong key policy, flexible refresh, and minimal API surface.

Installation

go get github.com/simp-lee/jwt

Quick Start

Basic Usage
package main

import (
    "fmt"
    "time"
    "github.com/simp-lee/jwt"
)

func main() {
    // Create JWT service with secret key (minimum 32 chars)
    service, err := jwt.New("your-super-secure-secret-key-that-is-long-enough!")
    if err != nil {
        panic(err)
    }
    defer service.Close()

    // Generate token
    token, err := service.GenerateToken("user123", []string{"admin", "user"}, time.Hour)
    if err != nil {
        panic(err)
    }

    // Validate token
    parsedToken, err := service.ValidateToken(token)
    if err != nil {
        panic(err)
    }

    fmt.Printf("User: %s, Roles: %v\n", parsedToken.UserID, parsedToken.Roles)
}
Advanced Configuration
service, err := jwt.New("your-super-secure-secret-key-that-is-long-enough!",
    jwt.WithCleanupInterval(30*time.Minute),          // Clean expired revocations every 30 min (default: 1h)
    jwt.WithUserRevocationTTL(7*24*time.Hour),        // Remember user revocations for 7 days (default: 30d, must be >= MaxTokenLifetime)
    jwt.WithMaxTokenLifetime(2*time.Hour),            // Limit all tokens to max 2 hours (default: 24h)
    jwt.WithLeeway(2*time.Minute),                    // Allow 2min clock skew between servers (default: 2m, applies to exp/iat/nbf)
    jwt.WithIssuer("my-awesome-app"),                 // Mark tokens as issued by "my-awesome-app"
    jwt.WithAudience("api-users"),                    // Mark tokens as intended for "api-users"
)
if err != nil {
    panic(err)
}
defer service.Close()
Inspect Parsed Token
// Generate token with issuer/audience
tokenString, err := service.GenerateToken("user123", []string{"admin"}, time.Hour)
if err != nil {
    panic(err)
}

// Parse token to see all fields
token, err := service.ParseToken(tokenString)
if err != nil {
    panic(err)
}

fmt.Println("UserID:", token.UserID)
fmt.Println("Roles:", token.Roles)
fmt.Println("ExpiresAt:", token.ExpiresAt)
fmt.Println("IssuedAt:", token.IssuedAt)
fmt.Println("NotBefore:", token.NotBefore)
fmt.Println("TokenID:", token.TokenID)
fmt.Println("Issuer:", token.Issuer)
fmt.Println("Audience:", token.Audience)
fmt.Println("Subject:", token.Subject)  // Maps to UserID
fmt.Println("Raw:", token.Raw)
Refresh Strategies
// Strategy 1: Refresh with original duration (preserves original token's lifetime)
newToken, err := service.RefreshToken(oldToken)

// Strategy 2: Refresh with specified duration (new duration from current time)
newToken, err := service.RefreshTokenExtend(oldToken, 2*time.Hour)

// Both strategies automatically revoke the old token and create a new one

Notes:

  • expiresIn and extendsIn must be > 0, otherwise you'll get ErrTokenCreation.
  • extendsIn must not exceed MaxTokenLifetime or you'll get ErrTokenCreation.
  • Library does not distinguish Access vs Refresh tokens; model that via roles, wrapper metadata, or separate services.
  • Enforce rotation / absolute session caps to avoid endless extension when using the extend strategy.
Convenience
// Validate and parse in one step
// Deprecated: Use ValidateToken instead.
token, err := service.ValidateAndParse(tokenString)

// Check if specific token is revoked (token-level only)
// Does NOT check user-level revocations from RevokeAllUserTokens.
// Use ValidateToken for full revocation checks.
isRevoked := service.IsTokenRevoked(tokenID)

API Reference

Service Creation
New(secretKey string, opts ...Option) (Service, error)

Creates a new JWT service with the specified secret key and options.

Security Requirements:

  • secretKey must be at least 32 characters long
  • Use a cryptographically secure random generator for production keys
  • Store the secret key securely (environment variables, secret management systems)
Configuration Options
// Set cleanup interval for expired revoked tokens
WithCleanupInterval(interval time.Duration)

// Set TTL for user revocation records
// How long to remember that a user's tokens were revoked
// Must be >= MaxTokenLifetime for security (prevents revoked tokens from becoming valid again)
// Example: If set to 7 days, any token issued before revocation remains invalid for 7 days
WithUserRevocationTTL(ttl time.Duration)

// Set maximum allowed token lifetime
// Prevents creation of excessively long-lived tokens for security compliance
// Example: If set to 2 hours, no token can be generated with expiration > 2 hours
WithMaxTokenLifetime(lifetime time.Duration)

// Set clock skew tolerance for token validation
// Allows for small time differences between servers in distributed systems
// Example: If set to 2 minutes, tokens are still valid 2 minutes after expiration
WithLeeway(leeway time.Duration)

// Set JWT issuer claim (iss) - identifies who issued the token
// Validated during token parsing to ensure tokens come from expected source
// Example: "my-app-v1" - helps distinguish tokens from different applications
WithIssuer(issuer string)

// Set JWT audience claim (aud) - identifies who the token is intended for  
// Validated during token parsing to ensure tokens are used by intended recipients
// Example: "api-users" - helps prevent token misuse across different services
WithAudience(audience string)

// Set custom clock for testing
WithClock(clock Clock)
Token Structure
type Token struct {
    UserID    string    `json:"user_id"`
    Roles     []string  `json:"roles"`
    ExpiresAt time.Time `json:"expires_at"`
    IssuedAt  time.Time `json:"issued_at"`
    NotBefore time.Time `json:"not_before,omitempty"`
    TokenID   string    `json:"token_id"`
    Issuer    string    `json:"issuer,omitempty"`
    Audience  string    `json:"audience,omitempty"`
    Subject   string    `json:"subject,omitempty"`   // Maps to UserID for compatibility
    Raw       string    `json:"raw,omitempty"`       // Original token string
}
Service Interface
type Service interface {
    GenerateToken(userID string, roles []string, expiresIn time.Duration) (string, error)
    ValidateToken(tokenString string) (*Token, error)
    ValidateAndParse(tokenString string) (*Token, error)          // Deprecated: Use ValidateToken instead
    RefreshToken(tokenString string) (string, error)             // Preserves original duration
    RefreshTokenExtend(tokenString string, extendsIn time.Duration) (string, error) // Extends with new duration
    RevokeToken(tokenString string) error
    IsTokenRevoked(tokenID string) bool
    ParseToken(tokenString string) (*Token, error)
    RevokeAllUserTokens(userID string) error
    Close()
}
Error Types
var (
    ErrMissingSecretKey = errors.New("jwt: missing secret key")
    ErrWeakSecretKey    = errors.New("jwt: secret key too weak")
    ErrEmptyUserID      = errors.New("jwt: empty user ID")
    ErrTokenCreation    = errors.New("jwt: failed to create token")
    ErrInvalidToken     = errors.New("jwt: invalid token")
    ErrExpiredToken     = errors.New("jwt: token has expired")
    ErrRevokedToken     = errors.New("jwt: token has been revoked")
    ErrInvalidIssuer    = errors.New("jwt: invalid issuer")
    ErrInvalidAudience  = errors.New("jwt: invalid audience")
    ErrServiceClosed    = errors.New("jwt: service is closed")
)

Testing Support

Clock Injection for Testing
type testClock struct {
    now time.Time
}

func (c *testClock) Now() time.Time {
    return c.now
}

// Create service with test clock
service, err := jwt.New("test-secret-key-with-32-characters!",
    jwt.WithClock(&testClock{now: fixedTime}),
)

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

View Source
const (
	// MinSecretLength is the minimum required length for the secret key
	MinSecretLength = 32

	// DefaultCleanupInterval is the default interval for cleaning up expired tokens
	DefaultCleanupInterval = 1 * time.Hour

	// DefaultUserRevocationTTL is the default TTL for user revocation records
	DefaultUserRevocationTTL = 30 * 24 * time.Hour // 30 days

	// DefaultMaxTokenLifetime is the default maximum token lifetime
	DefaultMaxTokenLifetime = 24 * time.Hour // 24 hours

	// DefaultLeeway is the default clock skew leeway for token validation
	DefaultLeeway = 2 * time.Minute
)

Variables

View Source
var (
	ErrMissingSecretKey = errors.New("jwt: missing secret key")
	ErrWeakSecretKey    = errors.New("jwt: secret key too weak")
	ErrEmptyUserID      = errors.New("jwt: empty user ID")
	ErrTokenCreation    = errors.New("jwt: failed to create token")
	ErrInvalidToken     = errors.New("jwt: invalid token")
	ErrExpiredToken     = errors.New("jwt: token has expired")
	ErrRevokedToken     = errors.New("jwt: token has been revoked")
	ErrInvalidIssuer    = errors.New("jwt: invalid issuer")
	ErrInvalidAudience  = errors.New("jwt: invalid audience")
	ErrServiceClosed    = errors.New("jwt: service is closed")
)

Common JWT service errors

Functions

This section is empty.

Types

type Clock

type Clock interface {
	Now() time.Time
}

Clock interface allows for time injection in tests

type Config

type Config struct {
	// SecretKey is the key used for signing and verifying JWT tokens.
	// This should be a secure random string with at least 32 bytes.
	SecretKey string

	// CleanupInterval specifies how often to cleanup expired revoked tokens.
	// Defaults to 1 hour if not set or <= 0.
	CleanupInterval time.Duration

	// UserRevocationTTL specifies how long to keep user revocation records.
	// Defaults to 30 days if not set or <= 0.
	// For security, this should be >= MaxTokenLifetime to prevent revoked tokens
	// from becoming valid again.
	UserRevocationTTL time.Duration

	// MaxTokenLifetime specifies the maximum allowed token lifetime.
	// This is used to ensure UserRevocationTTL is set appropriately.
	// Defaults to 24 hours if not set or <= 0.
	MaxTokenLifetime time.Duration

	// Leeway specifies the time window for clock skew tolerance.
	// Tokens expiring within this window are still considered valid.
	// Defaults to 2 minutes if not set or <= 0.
	Leeway time.Duration

	// Issuer is the JWT issuer claim (iss).
	// If set, tokens will include this issuer and validation will check it.
	Issuer string

	// Audience is the JWT audience claim (aud).
	// If set, tokens will include this audience and validation will check it.
	Audience string

	// Clock allows for time injection, mainly useful for testing.
	// If not set, uses the standard time.Now().
	Clock Clock
}

Config holds the configuration for JWT service.

type Option

type Option func(*Config)

Option is a function that modifies the Config

func WithAudience

func WithAudience(audience string) Option

WithAudience sets the JWT audience claim

func WithCleanupInterval

func WithCleanupInterval(interval time.Duration) Option

WithCleanupInterval sets the cleanup interval for expired tokens

func WithClock

func WithClock(clock Clock) Option

WithClock sets a custom clock implementation (mainly for testing)

func WithIssuer

func WithIssuer(issuer string) Option

WithIssuer sets the JWT issuer claim

func WithLeeway

func WithLeeway(leeway time.Duration) Option

WithLeeway sets the clock skew leeway for token validation

func WithMaxTokenLifetime

func WithMaxTokenLifetime(lifetime time.Duration) Option

WithMaxTokenLifetime sets the maximum token lifetime

func WithUserRevocationTTL

func WithUserRevocationTTL(ttl time.Duration) Option

WithUserRevocationTTL sets the TTL for user revocation records

type Service

type Service interface {
	// GenerateToken creates a new JWT token for the given user ID and roles.
	// The token will expire after the specified duration.
	GenerateToken(userID string, roles []string, expiresIn time.Duration) (string, error)

	// ValidateToken validates a token string and returns the parsed token.
	// It checks token signature, expiration, and revocation status.
	ValidateToken(tokenString string) (*Token, error)

	// ValidateAndParse validates a token and returns the parsed token in one step.
	//
	// Deprecated: will be removed in v3. Use ValidateToken instead.
	ValidateAndParse(tokenString string) (*Token, error)

	// RefreshToken invalidates the old token and generates a new one with the same claims.
	// The new token will have a new expiration time calculated from the current time,
	// preserving the original token's expiration duration.
	RefreshToken(tokenString string) (string, error)

	// RefreshTokenExtend invalidates the old token and generates a new one
	// with the specified expiration duration from the current time.
	// This provides an alternative refresh strategy.
	RefreshTokenExtend(tokenString string, extendsIn time.Duration) (string, error)

	// RevokeToken adds a token to the revocation list.
	// Revoked tokens will fail validation.
	RevokeToken(tokenString string) error

	// IsTokenRevoked checks if an individual token is in the revocation list.
	// Note: this only checks token-level revocations added via RevokeToken.
	// It does NOT check user-level revocations from RevokeAllUserTokens.
	// Use ValidateToken for full revocation checks including user-level revocations.
	IsTokenRevoked(tokenID string) bool

	// ParseToken parses a token without fully validating it.
	// This method extracts claims even from expired tokens.
	ParseToken(tokenString string) (*Token, error)

	// RevokeAllUserTokens revokes all tokens for a specific user.
	// Any token issued before the revocation time will be considered invalid.
	RevokeAllUserTokens(userID string) error

	// Close terminates any background processes of the service.
	// Should be called when the service is no longer needed.
	Close()
}

Service defines the interface for JWT token management operations. This interface provides a complete JWT service with token generation, validation, refresh, and revocation capabilities.

func New

func New(secretKey string, opts ...Option) (Service, error)

New creates a new JWT service with the provided secret key and options.

Security Requirements: - secretKey must be at least 32 characters long and securely randomly generated - Use a cryptographically secure random generator for production keys - Store the secret key securely (environment variables, secret management systems)

Example:

service, err := New("your-super-secure-secret-key-here",
  WithCleanupInterval(30*time.Minute),
  WithIssuer("your-app"),
  WithAudience("your-users"))

type Token

type Token struct {
	UserID    string    `json:"user_id"`
	Roles     []string  `json:"roles"`
	ExpiresAt time.Time `json:"expires_at"`
	IssuedAt  time.Time `json:"issued_at"`
	NotBefore time.Time `json:"not_before,omitempty"`
	TokenID   string    `json:"token_id"`
	Issuer    string    `json:"issuer,omitempty"`
	Audience  string    `json:"audience,omitempty"`
	Subject   string    `json:"subject,omitempty"` // Maps to UserID for compatibility
	Raw       string    `json:"raw,omitempty"`     // Original token string
}

Token represents a parsed JWT token with its claims.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL