Update JWT documentation for new database interface
Updated all examples to reflect the simplified database API that uses *sql.DB and *sql.Tx directly instead of custom wrapper interfaces. Changes: - Changed GeneratorConfig.DBConn to GeneratorConfig.DB (*sql.DB) - Updated all transaction examples to use db.Begin() directly - Removed references to jwt.NewDBConnection() wrapper - Fixed GORM example to show correct usage with sqlDB - Corrected Bun example - sql.DB should be created first, then passed to both Bun and JWT generator (previously showed incorrect usage) - Updated complete example with proper transaction handling - Simplified API makes it clearer how to integrate with ORMs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
66
JWT.md
66
JWT.md
@@ -47,7 +47,7 @@ func main() {
|
|||||||
FreshExpireAfter: 5, // Tokens stay fresh for 5 minutes
|
FreshExpireAfter: 5, // Tokens stay fresh for 5 minutes
|
||||||
TrustedHost: "example.com",
|
TrustedHost: "example.com",
|
||||||
SecretKey: "your-secret-key-here",
|
SecretKey: "your-secret-key-here",
|
||||||
DBConn: jwt.NewDBConnection(db),
|
DB: db,
|
||||||
DBType: jwt.DatabaseType{
|
DBType: jwt.DatabaseType{
|
||||||
Type: jwt.DatabasePostgreSQL,
|
Type: jwt.DatabasePostgreSQL,
|
||||||
Version: "15",
|
Version: "15",
|
||||||
@@ -92,7 +92,7 @@ type GeneratorConfig struct {
|
|||||||
SecretKey string // Secret key for token signing
|
SecretKey string // Secret key for token signing
|
||||||
|
|
||||||
// Optional fields (for database support)
|
// Optional fields (for database support)
|
||||||
DBConn DBConnection // Database connection (nil to disable revocation)
|
DB *sql.DB // Database connection (nil to disable revocation)
|
||||||
DBType DatabaseType // Database type and version
|
DBType DatabaseType // Database type and version
|
||||||
TableConfig TableConfig // Table configuration
|
TableConfig TableConfig // Table configuration
|
||||||
}
|
}
|
||||||
@@ -162,10 +162,8 @@ token, expiry, err := gen.NewRefresh(42, true)
|
|||||||
Tokens must be validated within a transaction context:
|
Tokens must be validated within a transaction context:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "context"
|
|
||||||
|
|
||||||
// Begin transaction
|
// Begin transaction
|
||||||
tx, err := gen.dbConn.BeginTx(context.Background(), nil)
|
tx, err := db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -209,9 +207,7 @@ ttl := token.TTL // "session" or "exp"
|
|||||||
### Revoking a Token
|
### Revoking a Token
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "context"
|
tx, _ := db.Begin()
|
||||||
|
|
||||||
tx, _ := gen.dbConn.BeginTx(context.Background(), nil)
|
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
// Validate token first
|
// Validate token first
|
||||||
@@ -247,7 +243,7 @@ Tokens can be marked as "fresh" for sensitive operations:
|
|||||||
accessToken, _, _ := gen.NewAccess(userID, true, false) // fresh=true
|
accessToken, _, _ := gen.NewAccess(userID, true, false) // fresh=true
|
||||||
|
|
||||||
// Check freshness
|
// Check freshness
|
||||||
tx, _ := gen.dbConn.BeginTx(context.Background(), nil)
|
tx, _ := db.Begin()
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
token, _ := gen.ValidateAccess(tx, accessToken)
|
token, _ := gen.ValidateAccess(tx, accessToken)
|
||||||
@@ -364,8 +360,8 @@ CREATE INDEX IF NOT EXISTS idx_jwtblacklist_sub ON jwtblacklist(sub);
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get underlying *sql.DB from GORM
|
// Get underlying *sql.DB from GORM
|
||||||
@@ -374,12 +370,21 @@ sqlDB, _ := gormDB.DB()
|
|||||||
|
|
||||||
// Create generator with sql.DB
|
// Create generator with sql.DB
|
||||||
gen, _ := jwt.CreateGenerator(jwt.GeneratorConfig{
|
gen, _ := jwt.CreateGenerator(jwt.GeneratorConfig{
|
||||||
// ... config
|
AccessExpireAfter: 15,
|
||||||
DBConn: jwt.NewDBConnection(sqlDB),
|
RefreshExpireAfter: 1440,
|
||||||
|
FreshExpireAfter: 5,
|
||||||
|
TrustedHost: "example.com",
|
||||||
|
SecretKey: "your-secret-key",
|
||||||
|
DB: sqlDB,
|
||||||
|
DBType: jwt.DatabaseType{
|
||||||
|
Type: jwt.DatabasePostgreSQL,
|
||||||
|
Version: "15",
|
||||||
|
},
|
||||||
|
TableConfig: jwt.DefaultTableConfig(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Use with transactions
|
// Use with transactions
|
||||||
tx, _ := gen.dbConn.BeginTx(context.Background(), nil)
|
tx, _ := sqlDB.Begin()
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
token, _ := gen.ValidateAccess(tx, tokenString)
|
token, _ := gen.ValidateAccess(tx, tokenString)
|
||||||
@@ -391,22 +396,35 @@ tx.Commit()
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
"context"
|
"database/sql"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/dialect/pgdialect"
|
||||||
|
"github.com/uptrace/bun/driver/pgdriver"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get underlying *sql.DB from Bun
|
// Create *sql.DB connection first
|
||||||
bunDB := bun.NewDB(sqldb, pgdialect.New())
|
sqlDB := sql.Open(pgdriver.NewConnector(pgdriver.WithDSN(dsn)))
|
||||||
sqlDB := bunDB.DB // Already *sql.DB
|
|
||||||
|
|
||||||
// Create generator
|
// Pass sql.DB to both Bun and JWT generator
|
||||||
|
bunDB := bun.NewDB(sqlDB, pgdialect.New())
|
||||||
|
|
||||||
|
// Create generator with the same sql.DB
|
||||||
gen, _ := jwt.CreateGenerator(jwt.GeneratorConfig{
|
gen, _ := jwt.CreateGenerator(jwt.GeneratorConfig{
|
||||||
// ... config
|
AccessExpireAfter: 15,
|
||||||
DBConn: jwt.NewDBConnection(sqlDB),
|
RefreshExpireAfter: 1440,
|
||||||
|
FreshExpireAfter: 5,
|
||||||
|
TrustedHost: "example.com",
|
||||||
|
SecretKey: "your-secret-key",
|
||||||
|
DB: sqlDB,
|
||||||
|
DBType: jwt.DatabaseType{
|
||||||
|
Type: jwt.DatabasePostgreSQL,
|
||||||
|
Version: "15",
|
||||||
|
},
|
||||||
|
TableConfig: jwt.DefaultTableConfig(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Use with transactions
|
// Use with transactions
|
||||||
tx, _ := gen.dbConn.BeginTx(context.Background(), nil)
|
tx, _ := sqlDB.Begin()
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
token, _ := gen.ValidateAccess(tx, tokenString)
|
token, _ := gen.ValidateAccess(tx, tokenString)
|
||||||
@@ -444,7 +462,7 @@ func main() {
|
|||||||
FreshExpireAfter: 5,
|
FreshExpireAfter: 5,
|
||||||
TrustedHost: "example.com",
|
TrustedHost: "example.com",
|
||||||
SecretKey: "super-secret-key",
|
SecretKey: "super-secret-key",
|
||||||
DBConn: jwt.NewDBConnection(db),
|
DB: db,
|
||||||
DBType: jwt.DatabaseType{
|
DBType: jwt.DatabaseType{
|
||||||
Type: jwt.DatabasePostgreSQL,
|
Type: jwt.DatabasePostgreSQL,
|
||||||
Version: "15",
|
Version: "15",
|
||||||
@@ -470,7 +488,7 @@ func main() {
|
|||||||
fmt.Printf("Refresh Token: %s (expires: %d)\n", refreshToken, refreshExp)
|
fmt.Printf("Refresh Token: %s (expires: %d)\n", refreshToken, refreshExp)
|
||||||
|
|
||||||
// Validate access token
|
// Validate access token
|
||||||
tx, _ := gen.dbConn.BeginTx(context.Background(), nil)
|
tx, _ := db.Begin()
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
token, err := gen.ValidateAccess(tx, accessToken)
|
token, err := gen.ValidateAccess(tx, accessToken)
|
||||||
@@ -489,7 +507,7 @@ func main() {
|
|||||||
tx.Commit()
|
tx.Commit()
|
||||||
|
|
||||||
// Try to validate revoked token
|
// Try to validate revoked token
|
||||||
tx2, _ := gen.dbConn.BeginTx(context.Background(), nil)
|
tx2, _ := db.Begin()
|
||||||
defer tx2.Rollback()
|
defer tx2.Rollback()
|
||||||
|
|
||||||
_, err = gen.ValidateAccess(tx2, accessToken)
|
_, err = gen.ValidateAccess(tx2, accessToken)
|
||||||
|
|||||||
Reference in New Issue
Block a user