A comprehensive, production-ready JWT authentication and authorization middleware for Go applications built with Gin framework. Features role-based access control (RBAC), token refresh mechanisms, session management, and user registration with role-based restrictions.
- Features
- Installation
- Quick Start
- Authentication
- Authorization
- Configuration
- Interfaces
- Examples
- API Reference
- JWT-based Authentication: Secure token-based authentication using HS256 algorithm
- Token Pairs: Access tokens (short-lived) and refresh tokens (long-lived) for improved security
- Password Hashing: Bcrypt password hashing for secure credential storage
- Session Management: Track and manage user sessions with IP and user agent information
- Cookie Support: Optional cookie-based token storage with configurable expiration
- User Registration: Self-service registration with role-based restrictions
- Role-Based Registration: Control which roles can be assigned during registration
- User Providers: Flexible interface for custom user data sources
- User Creator: Extensible interface for user creation and validation
- Email & Username Validation: Uniqueness checking for user credentials
- Role-Based Authorization: Restrict endpoints to specific user roles
- Permission System: Fine-grained permission-based access control
- Custom Authorizers: Implement custom authorization logic per endpoint
- Multiple Session Support: Track multiple concurrent sessions per user or enforce single session mode
- Automatic Token Refresh: Securely refresh access tokens using refresh tokens
- Session Tracking: Monitor active sessions with timestamps and device information
- Token Revocation: Revoke tokens on logout with cleanup mechanisms
- Concurrent Session Limits: Control maximum concurrent sessions per user
- Single Session Mode: Enforce single session per user when needed
- Automatic Cleanup: Periodically cleanup expired tokens and sessions
- Brute Force Protection: Optional protection against login attempts
- Session Activity Tracking: Monitor and update session activity
- User Active Status: Validate user account status before operations
- Configurable Timeouts: Control access and refresh token expiration times
- Secure Cookies: HttpOnly, Secure, and SameSite cookie attributes
go get github.com/hsdfat/go-auth-middlewarepackage main
import (
"log"
"time"
"github.com/gin-gonic/gin"
"github.com/hsdfat/go-auth-middleware/core"
"github.com/hsdfat/go-auth-middleware/ginauth"
)
func main() {
r := gin.Default()
// Create initial users
users := map[string]core.User{
"admin": {
ID: "1",
Username: "admin",
Email: "admin@example.com",
PasswordHash: ginauth.HashPassword("admin123"),
Role: "admin",
IsActive: true,
},
}
// Create providers
userProvider := core.NewMapUserProvider(users)
userCreator := NewMapUserCreator(users) // Custom implementation
tokenStorage := core.NewInMemoryTokenStorage()
// Create auth middleware
authMiddleware := ginauth.NewEnhanced(ginauth.EnhancedAuthConfig{
SecretKey: "your-access-token-secret",
RefreshSecretKey: "your-refresh-token-secret",
AccessTokenTimeout: 15 * time.Minute,
RefreshTokenTimeout: 7 * 24 * time.Hour,
TokenLookup: "header:Authorization,cookie:jwt",
TokenHeadName: "Bearer",
Realm: "go-auth",
IdentityKey: "identity",
// Enable registration
EnableRegistration: true,
RegisterableRoles: []string{"user"}, // Only 'user' role can self-register
DefaultRole: "user",
// Providers
TokenStorage: tokenStorage,
UserProvider: userProvider,
UserCreator: userCreator,
// Authentication
Authenticator: ginauth.CreateEnhancedAuthenticator(userProvider),
RoleAuthorizator: ginauth.CreateRoleAuthorizator("admin", "user", "moderator"),
})
// Public endpoints
r.POST("/auth/login", authMiddleware.LoginHandler)
r.POST("/auth/register", authMiddleware.RegisterHandler)
r.POST("/auth/refresh", authMiddleware.RefreshHandler)
// Protected endpoints
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})
})
protected.POST("/logout", authMiddleware.LogoutHandler)
protected.GET("/sessions", authMiddleware.GetUserSessionsHandler)
}
r.Run(":8080")
}Endpoint: POST /auth/login
Request:
{
"username": "admin",
"password": "admin123"
}Response:
{
"success": true,
"code": 200,
"data": {
"access_token": "eyJhbGc...",
"refresh_token": "eyJhbGc...",
"access_token_expires_at": 1701363442,
"refresh_token_expires_at": 1702000000,
"token_type": "Bearer",
"user": {
"id": "1",
"username": "admin",
"email": "admin@example.com",
"role": "admin"
}
}
}Endpoint: POST /auth/register
Request:
{
"username": "newuser",
"email": "newuser@example.com",
"password": "securepass123"
}Optional: Request with specific role (if allowed):
{
"username": "newuser",
"email": "newuser@example.com",
"password": "securepass123",
"role": "user"
}Response:
{
"success": true,
"code": 201,
"message": "User registered successfully",
"data": {
"user_id": "123456",
"username": "newuser",
"email": "newuser@example.com",
"role": "user",
"created_at": 1701360000
}
}Features:
- Username and email validation for uniqueness
- Automatic password hashing with bcrypt
- Role-based registration restrictions
- Configurable default role for new users
- Validation of minimum password length (default: 8 characters)
Endpoint: POST /auth/refresh
Request (Body):
{
"refresh_token": "eyJhbGc..."
}Alternative (Cookie or Header):
- Cookie:
refresh_tokencookie automatically extracted - Header:
X-Refresh-Tokenheader supported
Response:
{
"success": true,
"code": 200,
"data": {
"access_token": "eyJhbGc...",
"refresh_token": "eyJhbGc...",
"access_token_expires_at": 1701363442,
"refresh_token_expires_at": 1702000000,
"token_type": "Bearer"
}
}Endpoint: POST /auth/logout
Authentication: Required (Bearer token)
Response:
{
"success": true,
"code": 200,
"message": "Successfully logged out"
}Logout All Sessions:
Endpoint: POST /auth/logout-all
Response:
{
"success": true,
"code": 200,
"message": "Successfully logged out from all devices"
}Restrict endpoints to specific roles:
// Create role authorizer
roleAuthorizator := ginauth.CreateRoleAuthorizator("admin", "moderator")
authMiddleware := ginauth.NewEnhanced(ginauth.EnhancedAuthConfig{
// ... other config ...
RoleAuthorizator: roleAuthorizator,
})
// Admin-only routes
adminGroup := r.Group("/api/admin")
adminGroup.Use(authMiddleware.MiddlewareFunc())
adminGroup.Use(requireRole("admin"))
{
adminGroup.GET("/users", listUsers)
adminGroup.POST("/users", createUser)
}// Custom authorizer function
customAuthorizator := func(data interface{}, c *gin.Context) bool {
claims := data.(*core.Claims)
// Custom logic: allow only if user created more than 1 day ago
return time.Since(claims.IssuedAt.Time) > 24*time.Hour
}
authMiddleware := ginauth.NewEnhanced(ginauth.EnhancedAuthConfig{
// ... other config ...
Authorizator: customAuthorizator,
})protected := r.Group("/api")
protected.Use(authMiddleware.MiddlewareFunc())
{
// Require admin role for this specific endpoint
protected.DELETE("/dangerous", requireRole("admin"), dangerousHandler)
}
func requireRole(requiredRole string) gin.HandlerFunc {
return func(c *gin.Context) {
userRole := c.MustGet("user_role").(string)
if userRole != requiredRole {
c.JSON(http.StatusForbidden, gin.H{
"error": "insufficient permissions",
})
c.Abort()
return
}
c.Next()
}
}type EnhancedAuthConfig struct {
// JWT Configuration
SecretKey string // Secret for access tokens
RefreshSecretKey string // Secret for refresh tokens
AccessTokenTimeout time.Duration // Access token expiry (default: 15m)
RefreshTokenTimeout time.Duration // Refresh token expiry (default: 7d)
// Token Lookup Configuration
TokenLookup string // "header:Authorization,query:token,cookie:jwt"
TokenHeadName string // "Bearer"
// General Configuration
Realm string // Realm name for WWW-Authenticate
IdentityKey string // Key for storing user ID in context
TimeFunc func() time.Time // Time function (for testing)
// Cookie Configuration
SendCookie bool // Enable cookie storage
CookieName string // Access token cookie name
RefreshCookieName string // Refresh token cookie name
CookieMaxAge int // Cookie max age in seconds
CookieDomain string // Cookie domain
CookieSecure bool // HTTPS only
CookieHTTPOnly bool // JavaScript access disabled
CookieSameSite http.SameSite // SameSite attribute
// Storage and Providers
TokenStorage core.TokenStorage // Token storage implementation
UserProvider core.UserProvider // User data provider
UserCreator core.UserCreator // User creation provider
RoleProvider core.RoleProvider // Role management (optional)
SessionManager core.SessionManager // Session management (optional)
// Registration Configuration
EnableRegistration bool // Enable registration endpoint
RegisterableRoles []string // Roles assignable during registration
DefaultRole string // Default role for new users
// Authentication and Authorization Functions
Authenticator func(c *gin.Context) (*core.User, error)
Authorizator func(data interface{}, c *gin.Context) bool
RoleAuthorizator func(role string, c *gin.Context) bool
IdentityHandler func(c *gin.Context) interface{}
// Response Functions
Unauthorized func(c *gin.Context, code int, message string)
LoginResponse func(c *gin.Context, code int, tokenPair core.TokenPair, user *core.User)
LogoutResponse func(c *gin.Context, code int, message string)
RefreshResponse func(c *gin.Context, code int, tokenPair core.TokenPair)
RegisterResponse func(c *gin.Context, code int, user *core.User)
// Security Configuration
EnableBruteForceProtection bool // Brute force protection
MaxLoginAttempts int // Max attempts before lockout
LockoutDuration time.Duration // Lockout duration
RequireEmailVerification bool // Email verification required
EnableTwoFactor bool // Two-factor authentication
// Session Configuration
MaxConcurrentSessions int // Max concurrent sessions per user
SingleSessionMode bool // Only one session per user
// Token Configuration
EnableTokenRevocation bool // Revoke tokens on logout
CleanupInterval time.Duration // Token cleanup interval
}Retrieve user information:
type UserProvider interface {
GetUserByUsername(username string) (*User, error)
GetUserByID(userID string) (*User, error)
GetUserByEmail(email string) (*User, error)
UpdateUserLastLogin(userID string, lastLogin time.Time) error
IsUserActive(userID string) (bool, error)
}Create and validate users:
type UserCreator interface {
CreateUser(user *User) error
UserExists(username string, email string) (bool, error)
IsUsernameAvailable(username string) (bool, error)
IsEmailAvailable(email string) (bool, error)
}Manage tokens and sessions:
type TokenStorage interface {
// Access token methods
StoreTokenPair(sessionID string, accessToken, refreshToken string,
accessExpiresAt, refreshExpiresAt time.Time, userID string) error
GetAccessToken(sessionID string) (string, error)
GetRefreshToken(sessionID string) (string, error)
// Token validation
IsAccessTokenValid(sessionID string) (bool, error)
IsRefreshTokenValid(sessionID string) (bool, error)
// Token management
DeleteTokenPair(sessionID string) error
RefreshTokenPair(sessionID string, newAccessToken, newRefreshToken string,
accessExpiresAt, refreshExpiresAt time.Time) error
// User session management
RevokeAllUserTokens(userID string) error
GetUserActiveSessions(userID string) ([]string, error)
// Session tracking
StoreUserSession(session UserSession) error
GetUserSession(sessionID string) (*UserSession, error)
UpdateSessionActivity(sessionID string, lastActivity time.Time) error
DeleteUserSession(sessionID string) error
// Cleanup expired tokens
CleanupExpiredTokens() error
}Fine-grained role and permission management:
type RoleProvider interface {
GetUserRoles(userID string) ([]string, error)
HasRole(userID string, role string) (bool, error)
HasPermission(userID string, permission string) (bool, error)
GetRolePermissions(role string) ([]string, error)
}See examples/jwt/main.go for a complete working example with:
- User login with JWT tokens
- User registration with role-based restrictions
- Token refresh mechanism
- Session management
- Role-based access control
- Multiple concurrent sessions
Run the example:
cd examples/jwt
go run main.goTest Registration:
curl -X POST http://localhost:8080/auth/register \
-H "Content-Type: application/json" \
-d '{
"username": "newuser",
"email": "newuser@example.com",
"password": "securepass123"
}'Test Login:
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "admin123"
}'Access Protected Route:
curl -X GET http://localhost:8080/api/profile \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"- Method: POST
- Endpoint:
/auth/login - Authentication: None (public)
- Purpose: Authenticate user and return access/refresh tokens
- Customization: Via
Authenticatorconfig function
- Method: POST
- Endpoint:
/auth/register - Authentication: None (public)
- Purpose: Register new user with optional role assignment
- Customization: Via
EnableRegistration,RegisterableRoles,UserCreator
- Method: POST
- Endpoint:
/auth/refresh - Authentication: None (public, but requires valid refresh token)
- Purpose: Get new access token using refresh token
- Customization: Via
RefreshResponseconfig function
- Method: POST
- Endpoint:
/auth/logout - Authentication: Required
- Purpose: Logout current session
- Customization: Via
LogoutResponseconfig function
- Method: POST
- Endpoint:
/auth/logout-all - Authentication: Required
- Purpose: Logout from all devices/sessions
- Method: GET
- Endpoint:
/auth/sessions - Authentication: Required
- Purpose: List active sessions for current user
After middleware validation, the following variables are available in the request context:
identity: User ID (configured viaIdentityKey)user_email: User emailuser_role: User roleusername: UsernameSESSION_ID: Current session IDJWT_PAYLOAD: Full JWT claims
200 OK: Successful authentication/refresh/logout201 Created: Successful registration400 Bad Request: Invalid request data401 Unauthorized: Missing or invalid credentials/tokens403 Forbidden: Insufficient permissions or role restrictions409 Conflict: Username/email already exists during registration500 Internal Server Error: Server error
- Secret Keys: Use strong, randomly generated secret keys for both access and refresh tokens
- HTTPS: Always use HTTPS in production
- Token Timeouts: Keep access token timeout short (15-30 minutes) and refresh token timeout reasonable (7 days)
- Cookie Security: Enable
CookieSecureandCookieHTTPOnlyin production - CORS: Configure CORS appropriately for your frontend
- Rate Limiting: Implement rate limiting on authentication endpoints
- Token Storage: In production, use a proper database instead of in-memory storage
MIT