136 lines
4.2 KiB
Go
136 lines
4.2 KiB
Go
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
|
|
beginTx BeginTX // Database transaction getter 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.
|
|
func CreateGenerator(config GeneratorConfig, txGetter BeginTX) (gen *TokenGenerator, err error) {
|
|
if config.AccessExpireAfter <= 0 {
|
|
return nil, errors.New("accessExpireAfter must be greater than 0")
|
|
}
|
|
if config.RefreshExpireAfter <= 0 {
|
|
return nil, errors.New("refreshExpireAfter must be greater than 0")
|
|
}
|
|
if config.FreshExpireAfter <= 0 {
|
|
return nil, errors.New("freshExpireAfter must be greater than 0")
|
|
}
|
|
if config.TrustedHost == "" {
|
|
return nil, errors.New("trustedHost cannot be an empty string")
|
|
}
|
|
if config.SecretKey == "" {
|
|
return nil, errors.New("secretKey cannot be an empty string")
|
|
}
|
|
|
|
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")
|
|
}
|
|
}
|
|
}
|
|
|
|
return &TokenGenerator{
|
|
accessExpireAfter: config.AccessExpireAfter,
|
|
refreshExpireAfter: config.RefreshExpireAfter,
|
|
freshExpireAfter: config.FreshExpireAfter,
|
|
trustedHost: config.TrustedHost,
|
|
secretKey: config.SecretKey,
|
|
beginTx: txGetter,
|
|
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.beginTx == nil {
|
|
return errors.New("No DB provided, unable to use this function")
|
|
}
|
|
|
|
tx, err := gen.beginTx(ctx)
|
|
if err != nil {
|
|
return pkgerrors.Wrap(err, "failed to begin transaction")
|
|
}
|
|
|
|
tableName := gen.tableConfig.TableName
|
|
currentTime := time.Now().Unix()
|
|
|
|
query := "DELETE FROM " + tableName + " WHERE exp < ?"
|
|
|
|
_, err = tx.Exec(query, currentTime)
|
|
if err != nil {
|
|
return pkgerrors.Wrap(err, "failed to cleanup expired tokens")
|
|
}
|
|
|
|
return nil
|
|
}
|