Refactor database interface to use *sql.DB directly
Simplified the database layer by removing custom interface wrappers and using standard library *sql.DB and *sql.Tx types directly. Changes: - Removed DBConnection and DBTransaction interfaces from database.go - Removed NewDBConnection() wrapper function - Updated TokenGenerator to use *sql.DB instead of DBConnection - Updated all validation and revocation methods to accept *sql.Tx - Updated TableManager to work with *sql.DB directly - Updated all tests to use db.Begin() instead of custom wrappers - Fixed GeneratorConfig.DB field (was DBConn) - Updated documentation in doc.go with correct API usage Benefits: - Simpler API with fewer abstractions - Works directly with database/sql standard library - Compatible with GORM (via gormDB.DB()) and Bun (share same *sql.DB) - Easier to understand and maintain - No unnecessary wrapper layers Breaking changes: - GeneratorConfig.DBConn renamed to GeneratorConfig.DB - Removed NewDBConnection() function - pass *sql.DB directly - ValidateAccess/ValidateRefresh now accept *sql.Tx instead of DBTransaction - Token.Revoke/CheckNotRevoked now accept *sql.Tx instead of DBTransaction 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
136
jwt/generator.go
136
jwt/generator.go
@@ -1,62 +1,130 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
pkgerrors "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type TokenGenerator struct {
|
||||
accessExpireAfter int64 // Access Token expiry time in minutes
|
||||
refreshExpireAfter int64 // Refresh Token expiry time in minutes
|
||||
freshExpireAfter int64 // Token freshness expiry time in minutes
|
||||
trustedHost string // Trusted hostname to use for the tokens
|
||||
secretKey string // Secret key to use for token hashing
|
||||
dbConn *sql.DB // Database handle for token blacklisting
|
||||
accessExpireAfter int64 // Access Token expiry time in minutes
|
||||
refreshExpireAfter int64 // Refresh Token expiry time in minutes
|
||||
freshExpireAfter int64 // Token freshness expiry time in minutes
|
||||
trustedHost string // Trusted hostname to use for the tokens
|
||||
secretKey string // Secret key to use for token hashing
|
||||
db *sql.DB // Database connection for token blacklisting
|
||||
tableConfig TableConfig // Table configuration
|
||||
tableManager *TableManager // Table lifecycle manager
|
||||
}
|
||||
|
||||
// GeneratorConfig holds configuration for creating a TokenGenerator.
|
||||
type GeneratorConfig struct {
|
||||
// AccessExpireAfter is the access token expiry time in minutes.
|
||||
AccessExpireAfter int64
|
||||
|
||||
// RefreshExpireAfter is the refresh token expiry time in minutes.
|
||||
RefreshExpireAfter int64
|
||||
|
||||
// FreshExpireAfter is the token freshness expiry time in minutes.
|
||||
FreshExpireAfter int64
|
||||
|
||||
// TrustedHost is the trusted hostname to use for the tokens.
|
||||
TrustedHost string
|
||||
|
||||
// SecretKey is the secret key to use for token hashing.
|
||||
SecretKey string
|
||||
|
||||
// DB is the database connection. Can be nil to disable token revocation.
|
||||
// When using ORMs like GORM or Bun, pass the underlying *sql.DB.
|
||||
DB *sql.DB
|
||||
|
||||
// DBType specifies the database type and version for proper table management.
|
||||
// Only required if DB is not nil.
|
||||
DBType DatabaseType
|
||||
|
||||
// TableConfig configures the blacklist table name and behavior.
|
||||
// Only required if DB is not nil.
|
||||
TableConfig TableConfig
|
||||
}
|
||||
|
||||
// CreateGenerator creates and returns a new TokenGenerator using the provided configuration.
|
||||
// All expiry times should be provided in minutes.
|
||||
// trustedHost and secretKey strings must be provided.
|
||||
// dbConn can be nil, but doing this will disable token revocation
|
||||
func CreateGenerator(
|
||||
accessExpireAfter int64,
|
||||
refreshExpireAfter int64,
|
||||
freshExpireAfter int64,
|
||||
trustedHost string,
|
||||
secretKey string,
|
||||
dbConn *sql.DB,
|
||||
) (gen *TokenGenerator, err error) {
|
||||
if accessExpireAfter <= 0 {
|
||||
func CreateGenerator(config GeneratorConfig) (gen *TokenGenerator, err error) {
|
||||
if config.AccessExpireAfter <= 0 {
|
||||
return nil, errors.New("accessExpireAfter must be greater than 0")
|
||||
}
|
||||
if refreshExpireAfter <= 0 {
|
||||
if config.RefreshExpireAfter <= 0 {
|
||||
return nil, errors.New("refreshExpireAfter must be greater than 0")
|
||||
}
|
||||
if freshExpireAfter <= 0 {
|
||||
if config.FreshExpireAfter <= 0 {
|
||||
return nil, errors.New("freshExpireAfter must be greater than 0")
|
||||
}
|
||||
if trustedHost == "" {
|
||||
if config.TrustedHost == "" {
|
||||
return nil, errors.New("trustedHost cannot be an empty string")
|
||||
}
|
||||
if secretKey == "" {
|
||||
if config.SecretKey == "" {
|
||||
return nil, errors.New("secretKey cannot be an empty string")
|
||||
}
|
||||
|
||||
if dbConn != nil {
|
||||
err := dbConn.Ping()
|
||||
if err != nil {
|
||||
return nil, errors.New("Failed to ping database")
|
||||
var tableManager *TableManager
|
||||
if config.DB != nil {
|
||||
// Create table manager
|
||||
tableManager = NewTableManager(config.DB, config.DBType, config.TableConfig)
|
||||
|
||||
// Create table if AutoCreate is enabled
|
||||
if config.TableConfig.AutoCreate {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err = tableManager.CreateTable(ctx)
|
||||
if err != nil {
|
||||
return nil, pkgerrors.Wrap(err, "failed to create blacklist table")
|
||||
}
|
||||
}
|
||||
|
||||
// Setup automatic cleanup if enabled
|
||||
if config.TableConfig.EnableAutoCleanup {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err = tableManager.SetupAutoCleanup(ctx)
|
||||
if err != nil {
|
||||
return nil, pkgerrors.Wrap(err, "failed to setup automatic cleanup")
|
||||
}
|
||||
}
|
||||
// TODO: check if jwtblacklist table exists
|
||||
// TODO: create jwtblacklist table if not existing
|
||||
}
|
||||
|
||||
return &TokenGenerator{
|
||||
accessExpireAfter: accessExpireAfter,
|
||||
refreshExpireAfter: refreshExpireAfter,
|
||||
freshExpireAfter: freshExpireAfter,
|
||||
trustedHost: trustedHost,
|
||||
secretKey: secretKey,
|
||||
dbConn: dbConn,
|
||||
accessExpireAfter: config.AccessExpireAfter,
|
||||
refreshExpireAfter: config.RefreshExpireAfter,
|
||||
freshExpireAfter: config.FreshExpireAfter,
|
||||
trustedHost: config.TrustedHost,
|
||||
secretKey: config.SecretKey,
|
||||
db: config.DB,
|
||||
tableConfig: config.TableConfig,
|
||||
tableManager: tableManager,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Cleanup manually removes expired tokens from the blacklist table.
|
||||
// This method should be called periodically if automatic cleanup is not enabled,
|
||||
// or can be called on-demand regardless of automatic cleanup settings.
|
||||
func (gen *TokenGenerator) Cleanup(ctx context.Context) error {
|
||||
if gen.db == nil {
|
||||
return errors.New("No DB provided, unable to use this function")
|
||||
}
|
||||
|
||||
tableName := gen.tableConfig.TableName
|
||||
currentTime := time.Now().Unix()
|
||||
|
||||
query := "DELETE FROM " + tableName + " WHERE exp < ?"
|
||||
|
||||
_, err := gen.db.ExecContext(ctx, query, currentTime)
|
||||
if err != nil {
|
||||
return pkgerrors.Wrap(err, "failed to cleanup expired tokens")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user