

GourdianToken
βProduction-ready JWT lifecycle management for Go applicationsβ
A Go library that handles the complete JWT lifecycle β creation, verification, revocation, and race-free refresh rotation with replay detection β behind one interface, with five pluggable storage backends and eleven signing algorithms.
Installation Guide
The core library is a single module; each storage backend has its own optional driver dependency.
Requirements
The library targets Go 1.24 and uses semantic import versioning (/v2).
https://go.dev/dl/Recommended production backend for rotation and revocation state.
go get github.com/redis/go-redis/v9For PostgreSQL, MySQL, or SQLite storage via GORM.
go get gorm.io/gorm gorm.io/driver/postgresFor MongoDB storage with transactional rotation.
go get go.mongodb.org/mongo-driverInstall the core module
Works on every platform Go supports.
go get github.com/gourdian25/gourdiantoken/v2@latestPick a storage backend
Stateless (nil storage) needs nothing extra. For stateful features install the matching driver and use the corresponding factory: NewGourdianTokenMakerWithRedis, ...WithGorm, ...WithMongo, or ...WithMemory.
Quick Start Guide
From zero to signed-and-verified tokens in three steps, using HMAC with no storage backend.
Install the package
Add the v2 module to your project. Note the /v2 suffix β v2 uses Go semantic import versioning.
go get github.com/gourdian25/gourdiantoken/v2@latestπ‘ Tips
- β’Requires Go 1.24 or higher
- β’Redis, GORM, and MongoDB drivers are only needed if you use those backends
Create a config and a token maker
DefaultGourdianTokenConfig gives secure defaults (HS256, 30-minute access tokens, 7-day refresh tokens). The no-storage maker is stateless β perfect for development and stateless microservices.
ctx := context.Background()
// Secure key: minimum 32 bytes for HMAC
config := gourdiantoken.DefaultGourdianTokenConfig(
"your-secret-key-at-least-32-bytes-long",
)
maker, err := gourdiantoken.NewGourdianTokenMakerNoStorage(ctx, config)
if err != nil {
log.Fatal(err)
}π‘ Tips
- β’Keys shorter than 32 bytes are rejected at startup
- β’Rotation and revocation require a storage backend β they stay disabled in no-storage mode
Create and verify a token
Identifiers are opaque strings in v2 β pass any non-empty user ID; it does not need to be a UUID.
accessToken, err := maker.CreateAccessToken(
ctx,
"user-42", // userID β any non-empty string
"john.doe@example.com", // username
[]string{"user", "admin"}, // roles
"session-1", // sessionID
)
if err != nil {
log.Fatal(err)
}
claims, err := maker.VerifyAccessToken(ctx, accessToken.Token)
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %s Roles: %v\n", claims.Username, claims.Roles)You're all set! Check out the detailed usage guide below for more advanced features.
Usage Guide
From basic token pairs to production rotation, revocation, and middleware.
Issue an access/refresh token pair
Access tokens carry roles for authorization; refresh tokens are identical minus roles and are used only to mint new pairs.
accessToken, err := maker.CreateAccessToken(
ctx, userID, "alice", []string{"user"}, sessionID,
)
refreshToken, err := maker.CreateRefreshToken(
ctx, userID, "alice", sessionID,
)
fmt.Println(accessToken.Token) // signed JWT string
fmt.Println(accessToken.ExpiresAt)Notes
- Responses expose Token, Subject, SessionID, IssuedAt, and ExpiresAt
Verify tokens and branch on failure modes
v2 exposes sentinel errors, so callers can distinguish an expired token from a revoked or replayed one with errors.Is.
claims, err := maker.VerifyAccessToken(ctx, tokenString)
switch {
case err == nil:
// authenticated: claims.Subject, claims.Roles, claims.SessionID
case errors.Is(err, gourdiantoken.ErrTokenExpired):
// ask the client to refresh
case errors.Is(err, gourdiantoken.ErrTokenRevoked):
// token was revoked (logout) β force re-login
default:
// invalid signature, malformed token, ...
}API Reference
The GourdianTokenMaker interface (all seven methods) and the factory constructors.
CreateAccessToken
Creates a signed access token carrying the user's roles. All identifiers are opaque strings.
Signature
CreateAccessToken(ctx context.Context, userID, username string, roles []string, sessionID string) (*AccessTokenResponse, error)Parameters
userIDstringRequiredAny non-empty user identifier β becomes the sub claim
usernamestringRequiredHuman-readable identity β becomes the usr claim
roles[]stringRequiredAuthorization roles β become the rls claim
sessionIDstringSession identifier (may be empty) β becomes the sid claim
Returns
*AccessTokenResponse, errorExamples
resp, err := maker.CreateAccessToken(ctx, "user-42", "alice", []string{"admin"}, "sess-1")CreateRefreshToken
Creates a signed refresh token β identical claims to an access token minus roles.
Signature
CreateRefreshToken(ctx context.Context, userID, username, sessionID string) (*RefreshTokenResponse, error)Returns
*RefreshTokenResponse, errorExamples
resp, err := maker.CreateRefreshToken(ctx, "user-42", "alice", "sess-1")VerifyAccessToken
Validates signature, expiry, required claims, the mle lifetime ceiling, and (when revocation is enabled) revocation state.
Signature
VerifyAccessToken(ctx context.Context, tokenString string) (*AccessTokenClaims, error)Returns
*AccessTokenClaims, errorExamples
claims, err := maker.VerifyAccessToken(ctx, token)VerifyRefreshToken
Like VerifyAccessToken but for refresh tokens; also detects already-rotated tokens (replay).
Signature
VerifyRefreshToken(ctx context.Context, tokenString string) (*RefreshTokenClaims, error)Returns
*RefreshTokenClaims, errorExamples
claims, err := maker.VerifyRefreshToken(ctx, token)RevokeAccessToken
Marks an access token revoked until its natural expiry (stored as a SHA-256 hash). Requires RevocationEnabled and a storage backend.
Signature
RevokeAccessToken(ctx context.Context, token string) errorReturns
errorExamples
err := maker.RevokeAccessToken(ctx, token)RevokeRefreshToken
Revokes a refresh token β the storage-backed logout primitive.
Signature
RevokeRefreshToken(ctx context.Context, token string) errorReturns
errorExamples
err := maker.RevokeRefreshToken(ctx, token)RotateRefreshToken
Atomically exchanges a refresh token for a new one. The new token is created first; the old token is then marked rotated via a compare-and-swap, so concurrent rotations can't both win and failures never strand the user.
Signature
RotateRefreshToken(ctx context.Context, oldToken string) (*RefreshTokenResponse, error)Returns
*RefreshTokenResponse, errorExamples
newToken, err := maker.RotateRefreshToken(ctx, oldToken)
if errors.Is(err, gourdiantoken.ErrTokenRotated) { /* replay detected */ }DefaultGourdianTokenConfig
Secure-default config factory: HS256, 30-minute access / 24-hour ceiling, 7-day refresh / 30-day ceiling, rotation and revocation off.
Signature
DefaultGourdianTokenConfig(symmetricKey string) GourdianTokenConfigReturns
GourdianTokenConfigExamples
config := gourdiantoken.DefaultGourdianTokenConfig(key)NewGourdianTokenMakerNoStorage
Stateless maker with no repository β rotation and revocation must be disabled in config.
Signature
NewGourdianTokenMakerNoStorage(ctx context.Context, config GourdianTokenConfig, opts ...Option) (GourdianTokenMaker, error)Returns
GourdianTokenMaker, errorExamples
maker, err := gourdiantoken.NewGourdianTokenMakerNoStorage(ctx, config)NewGourdianTokenMakerWithMemory
Maker backed by a mutex-guarded in-memory store β ideal for tests and single-instance apps.
Signature
NewGourdianTokenMakerWithMemory(ctx context.Context, config GourdianTokenConfig, opts ...Option) (GourdianTokenMaker, error)Returns
GourdianTokenMaker, errorExamples
maker, err := gourdiantoken.NewGourdianTokenMakerWithMemory(ctx, config)NewGourdianTokenMakerWithRedis
Maker backed by Redis β the recommended production backend.
Signature
NewGourdianTokenMakerWithRedis(ctx context.Context, config GourdianTokenConfig, redisClient *redis.Client, opts ...Option) (GourdianTokenMaker, error)Returns
GourdianTokenMaker, errorExamples
maker, err := gourdiantoken.NewGourdianTokenMakerWithRedis(ctx, config, redisClient)NewGourdianTokenMakerWithGorm
Maker backed by any GORM-supported SQL database (Postgres, MySQL, SQLite).
Signature
NewGourdianTokenMakerWithGorm(ctx context.Context, config GourdianTokenConfig, db *gorm.DB, opts ...Option) (GourdianTokenMaker, error)Returns
GourdianTokenMaker, errorExamples
maker, err := gourdiantoken.NewGourdianTokenMakerWithGorm(ctx, config, db)NewGourdianTokenMakerWithMongo
Maker backed by MongoDB. Set transactionsEnabled=true on replica sets for fully transactional rotation.
Signature
NewGourdianTokenMakerWithMongo(ctx context.Context, config GourdianTokenConfig, mongoDB *mongo.Database, transactionsEnabled bool, opts ...Option) (GourdianTokenMaker, error)Returns
GourdianTokenMaker, errorExamples
maker, err := gourdiantoken.NewGourdianTokenMakerWithMongo(ctx, config, db, true)Examples Gallery
Real-world code examples and use cases
Stateless HMAC setup
beginnerThe smallest possible integration β secure defaults, no storage, no external services.
config := gourdiantoken.DefaultGourdianTokenConfig(
"your-secret-key-at-least-32-bytes-long",
)
maker, err := gourdiantoken.NewGourdianTokenMakerNoStorage(ctx, config)
if err != nil {
log.Fatal(err)
}
token, _ := maker.CreateAccessToken(ctx, "user-1", "alice", []string{"user"}, "")
claims, _ := maker.VerifyAccessToken(ctx, token.Token)Graceful shutdown with Close()
beginnerStorage-backed makers run cleanup goroutines β stop them explicitly on shutdown. Close() is idempotent.
maker, err := gourdiantoken.NewGourdianTokenMakerWithRedis(ctx, config, redisClient)
if err != nil {
log.Fatal(err)
}
defer func() {
if closer, ok := maker.(gourdiantoken.GourdianTokenMakerCloser); ok {
_ = closer.Close()
}
}()Frequently Asked Questions
6 questions answered
Still have questions?
Check out the source code or open an issue on GitHub
AI-readable content
Learn more βThis content is available via the AI Content API as JSON or token-efficient Markdown. Feed it directly into LLM workflows.
/api/content/docs/gourdiantokenRelated Content
Explore related articles, projects, and tools.