A flexible JWT authentication middleware for Gin and Fiber frameworks with bcrypt password hashing and token storage support.
- JWT-based authentication
- Bcrypt password hashing
- Token storage interface (with in-memory implementation)
- Token refresh functionality
- Cookie support
- Flexible configuration
- Middleware for Gin and Fiber frameworks
go get github.com/hsdfat/go-auth-middleware
package main
import (
"github.com/gin-gonic/gin"
"github.com/hsdfat/go-auth-middleware/ginauth"
)
func main() {
r := gin.Default()
// Create users with bcrypt password hashing
users, err := ginauth.CreateUserMapWithBcrypt([]struct {
ID int
Username string
Password string
}{
{ID: 1, Username: "admin", Password: "admin123"},
{ID: 2, Username: "user", Password: "user123"},
})
if err != nil {
log.Fatal("Failed to create users:", err)
}
// Create user provider
userProvider := ginauth.NewMapUserProvider(users)
// Create token storage
tokenStorage := ginauth.NewInMemoryTokenStorage()
// Create auth middleware
authMiddleware := ginauth.New(ginauth.AuthConfig{
SecretKey: "your-secret-key",
TokenStorage: tokenStorage,
UseBcrypt: true,
Authenticator: ginauth.BasicAuthenticator(userProvider),
PayloadFunc: ginauth.BasicPayloadFunc(),
IdentityHandler: ginauth.BasicIdentityHandler(),
})
// Routes
r.POST("/login", authMiddleware.LoginHandler)
r.POST("/logout", authMiddleware.MiddlewareFunc(), authMiddleware.LogoutHandler)
r.POST("/refresh", authMiddleware.MiddlewareFunc(), authMiddleware.RefreshHandler)
// Protected routes
protected := r.Group("/api")
protected.Use(authMiddleware.MiddlewareFunc())
{
protected.GET("/profile", func(c *gin.Context) {
userID := c.MustGet("identity")
c.JSON(200, gin.H{"user_id": userID})
})
}
r.Run(":8080")
}
// For backward compatibility, you can still use the map directly
authMiddleware := ginauth.New(ginauth.AuthConfig{
SecretKey: "your-secret-key",
TokenStorage: tokenStorage,
UseBcrypt: true,
Authenticator: ginauth.LegacyBasicAuthenticator(users), // Legacy approach
PayloadFunc: ginauth.BasicPayloadFunc(),
IdentityHandler: ginauth.BasicIdentityHandler(),
})
type AuthConfig struct {
SecretKey string // JWT signing secret
TokenLookup string // Token lookup method: "header:Authorization,query:token,cookie:jwt"
TokenHeadName string // Token header name: "Bearer"
Realm string // Realm name
IdentityKey string // Identity key for context
IdentityHandler func(c *gin.Context) interface{}
Authenticator func(c *gin.Context) (interface{}, error)
Authorizator func(data interface{}, c *gin.Context) bool
PayloadFunc func(data interface{}) jwt.MapClaims
Unauthorized func(c *gin.Context, code int, message string)
LoginResponse func(c *gin.Context, code int, token string, expire time.Time)
LogoutResponse func(c *gin.Context, code int)
RefreshResponse func(c *gin.Context, code int, token string, expire time.Time)
TimeFunc func() time.Time
Timeout time.Duration
MaxRefresh time.Duration
SendCookie bool
CookieName string
CookieMaxAge int
CookieDomain string
CookieHTTPOnly bool
CookieSameSite http.SameSite
TokenStorage TokenStorage // Token storage interface
UseBcrypt bool // Enable bcrypt password hashing
}
The UserProvider
interface allows you to use any data source for user authentication:
type UserProvider interface {
GetUserByUsername(username string) (*User, error)
}
// Create users with bcrypt password hashing
users, err := ginauth.CreateUserMapWithBcrypt([]struct {
ID int
Username string
Password string
}{
{ID: 1, Username: "admin", Password: "admin123"},
{ID: 2, Username: "user", Password: "user123"},
})
// Create user provider
userProvider := ginauth.NewMapUserProvider(users)
// Use with authenticator
authenticator := ginauth.BasicAuthenticator(userProvider)
You can implement your own database user provider:
type CustomDatabaseUserProvider struct {
db *sql.DB
}
func (d *CustomDatabaseUserProvider) GetUserByUsername(username string) (*ginauth.User, error) {
var user ginauth.User
query := `SELECT id, username, password_hash FROM users WHERE username = $1`
err := d.db.QueryRow(query, username).Scan(&user.ID, &user.Username, &user.PasswordHash)
if err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("user not found")
}
return nil, err
}
return &user, nil
}
// Usage
userProvider := &CustomDatabaseUserProvider{db: db}
authenticator := ginauth.BasicAuthenticator(userProvider)
// Create a single user with bcrypt
user, err := ginauth.CreateUserWithBcrypt(1, "admin", "admin123")
if err != nil {
log.Fatal(err)
}
// Create multiple users with bcrypt
users, err := ginauth.CreateUserMapWithBcrypt([]struct {
ID int
Username string
Password string
}{
{ID: 1, Username: "admin", Password: "admin123"},
{ID: 2, Username: "user", Password: "user123"},
})
// Hash a password
hashedPassword, err := ginauth.HashPassword("myPassword")
if err != nil {
log.Fatal(err)
}
// Check a password against its hash
isValid := ginauth.CheckPasswordHash("myPassword", hashedPassword)
type TokenStorage interface {
StoreToken(tokenID string, token string, expiresAt time.Time) error
GetToken(tokenID string) (string, error)
DeleteToken(tokenID string) error
IsTokenValid(tokenID string) (bool, error)
RevokeAllUserTokens(userID interface{}) error
}
// Create in-memory token storage
tokenStorage := ginauth.NewInMemoryTokenStorage()
// Use with auth middleware
authMiddleware := ginauth.New(ginauth.AuthConfig{
TokenStorage: tokenStorage,
// ... other config
})
You can implement your own token storage by implementing the TokenStorage
interface:
type RedisTokenStorage struct {
client *redis.Client
}
func (r *RedisTokenStorage) StoreToken(tokenID string, token string, expiresAt time.Time) error {
return r.client.Set(context.Background(), tokenID, token, time.Until(expiresAt)).Err()
}
func (r *RedisTokenStorage) GetToken(tokenID string) (string, error) {
return r.client.Get(context.Background(), tokenID).Result()
}
// ... implement other methods
POST /login
Content-Type: application/json
{
"username": "admin",
"password": "admin123"
}
POST /logout
Authorization: Bearer <token>
POST /refresh
Authorization: Bearer <token>
GET /api/profile
Authorization: Bearer <token>
The middleware also supports the Fiber framework through the fiberauth
package:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/hsdfat/go-auth-middleware/fiberauth"
)
func main() {
app := fiber.New()
// Create user provider
users := map[string]fiberauth.User{
"admin": {ID: 1, Username: "admin", Password: "admin123"},
"user": {ID: 2, Username: "user", Password: "user123"},
}
userProvider := fiberauth.NewMapUserProvider(users)
// Create token storage
tokenStorage := fiberauth.NewInMemoryTokenStorage()
// Create auth middleware
authMiddleware := fiberauth.NewFiberAuthMiddleware(fiberauth.AuthConfig{
SecretKey: "your-secret-key",
TokenStorage: tokenStorage,
Authenticator: authenticator(userProvider),
Authorizator: authorizator,
})
// Routes
app.Post("/login", authMiddleware.LoginHandler())
app.Post("/logout", authMiddleware.LogoutHandler())
app.Post("/refresh", authMiddleware.RefreshHandler())
// Protected routes
protected := app.Group("/protected")
protected.Use(authMiddleware.MiddlewareFunc())
{
protected.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "Protected route",
"user_id": c.Locals("identity"),
})
})
}
app.Listen(":3000")
}
The Fiber middleware uses the same configuration structure as the Gin middleware, but with Fiber-specific context types:
type AuthConfig struct {
SecretKey string
TokenLookup string
TokenHeadName string
Realm string
IdentityKey string
IdentityHandler func(c *fiber.Ctx) interface{}
Authenticator func(c *fiber.Ctx) (interface{}, error)
Authorizator func(data interface{}, c *fiber.Ctx) bool
PayloadFunc func(data interface{}) jwt.MapClaims
Unauthorized func(c *fiber.Ctx, code int, message string)
LoginResponse func(c *fiber.Ctx, code int, token string, expire time.Time)
LogoutResponse func(c *fiber.Ctx, code int)
RefreshResponse func(c *fiber.Ctx, code int, token string, expire time.Time)
TimeFunc func() time.Time
Timeout time.Duration
MaxRefresh time.Duration
SendCookie bool
CookieName string
CookieMaxAge int
CookieDomain string
CookieHTTPOnly bool
CookieSameSite string
TokenStorage TokenStorage
UseBcrypt bool
// Token storage configuration
EnableTokenStorage bool
TokenStorageMode string
StoreTokenOnLogin bool
ValidateTokenOnRequest bool
}
See the examples/
directory for complete working examples for both Gin and Fiber frameworks.
github.com/gin-gonic/gin
- Gin web frameworkgithub.com/gofiber/fiber/v2
- Fiber web frameworkgithub.com/golang-jwt/jwt/v5
- JWT handlinggolang.org/x/crypto/bcrypt
- Password hashing
MIT