Features
Security extension capabilities
Session Management
The session system provides server-side session storage with cookie-based tracking.
Session Store
The in-memory SessionStore manages session lifecycle with full CRUD operations:
store := app.Container().MustResolve("security:sessions").(security.SessionStore)
// Create a new session
session, _ := security.NewSession("user-123", 24*time.Hour)
store.Create(ctx, session, 24*time.Hour)
// Retrieve a session by ID
session, err := store.Get(ctx, sessionID)
if err != nil {
// security.ErrSessionNotFound or security.ErrSessionExpired
}
// Store data in the session
session.SetData("preferences", map[string]string{"theme": "dark"})
store.Update(ctx, session, 24*time.Hour)
// Refresh session expiry
store.Touch(ctx, sessionID, 24*time.Hour)
// Delete a single session or all sessions for a user
store.Delete(ctx, sessionID)
store.DeleteByUserID(ctx, "user-123")
// Cleanup expired sessions (returns count removed)
removed, _ := store.Cleanup(ctx)
// Count active sessions
count, _ := store.Count(ctx)Session Object
Each Session carries user identity, expiry information, and arbitrary data:
ID-- cryptographically generated session identifier.UserID-- the authenticated user this session belongs to.Data--map[string]anyfor storing arbitrary session data (preferences, cart, etc.).CreatedAt,ExpiresAt,LastAccessedAt-- lifecycle timestamps.IPAddress,UserAgent-- optionally tracked whenTrackIPAddress/TrackUserAgentis enabled.IsExpired()-- checks if the session has passed its expiry time.IsValid()-- checks session is non-nil, has a user ID, and is not expired.Touch()-- refreshesLastAccessedAtto the current time.
Cookie-Based Tracking
The CookieManager handles session cookie operations with configurable security options:
cookieManager := app.Container().MustResolve("security:cookies").(*security.CookieManager)
// Set a cookie
cookieManager.SetCookie(w, "session_id", "abc123", &security.CookieOptions{
Secure: true,
HttpOnly: true,
SameSite: security.SameSiteLax,
MaxAge: 86400,
Path: "/",
})
// Read a cookie
value, err := cookieManager.GetCookie(r, "session_id")
// Delete a cookie
cookieManager.DeleteCookie(w, "session_id")
// Check if cookie exists
exists := cookieManager.HasCookie(r, "session_id")Session Middleware
Session middleware automatically loads the session from the cookie and makes it available in the request context:
// Manual middleware application
app.Use(security.SessionMiddleware(security.SessionMiddlewareOptions{
Store: store,
CookieManager: cookieManager,
Config: sessionConfig,
}))
// Require session (returns 401 if no valid session)
app.Use(security.RequireSessionSimple())
// Require session with custom handler
app.Use(security.RequireSession(func(ctx forge.Context) error {
return ctx.Redirect(http.StatusFound, "/login")
}))Session Helpers
Convenience functions for common session operations in handlers:
// Create session and set cookie in one call
session, err := security.CreateSession(ctx, w, userID, store, cookieManager, config, metadata)
// Destroy session and clear cookie
security.DestroySession(ctx, w, store, cookieManager, "forge_session")
// Read session from Forge context
session, ok := security.GetSessionFromForgeContext(ctx)
// Read session from standard context
session, ok := security.GetSession(stdCtx)
// Update session data and persist
security.UpdateSession(stdCtx, store, ttl)CSRF Protection
Token-based CSRF protection using the double-submit cookie pattern for stateless validation.
csrf := app.Container().MustResolve("security:csrf").(*security.CSRFProtection)
// Generate a new CSRF token
token, _ := csrf.GenerateToken()
// Set and validate tokens
csrf.SetToken(sessionID, token)
err := csrf.ValidateToken(sessionID, providedToken)
// Returns: ErrCSRFTokenMissing, ErrCSRFTokenInvalid, or ErrCSRFTokenExpired
// Clean up
csrf.DeleteToken(sessionID)The CSRF middleware automatically validates tokens on non-safe methods:
app.Use(security.CSRFMiddleware(csrf, cookieManager))- Token extraction from configurable location (default:
X-CSRF-Tokenheader). - Safe methods excluded from checks: GET, HEAD, OPTIONS, TRACE.
- Token length configurable (default 32 bytes).
- Token TTL configurable (default 12 hours).
- Skip paths to exclude specific routes from CSRF checks.
JWT Management
Full JSON Web Token lifecycle management with support for multiple signing algorithms.
Token Generation and Validation
jwtManager := app.Container().MustResolve("security:jwt").(*security.JWTManager)
// Generate an access token
token, err := jwtManager.GenerateToken(&security.JWTClaims{
UserID: "user-123",
Username: "alice",
Email: "alice@example.com",
Roles: []string{"admin", "editor"},
Metadata: map[string]any{"team": "engineering"},
})
// Validate and parse a token
claims, err := jwtManager.ValidateToken(tokenString)
// err may be: ErrJWTMissing, ErrJWTInvalid, ErrJWTExpired, ErrJWTNotYetValid
fmt.Println(claims.UserID, claims.Roles)
// Generate a refresh token (longer TTL, user ID only)
refreshToken, err := jwtManager.GenerateRefreshToken("user-123")JWT Middleware
// Apply JWT middleware to a route group
api := app.Group("/api")
api.Use(security.JWTMiddleware(jwtManager))
// Access claims in handlers
api.GET("/me", func(ctx forge.Context) error {
claims, ok := security.GetJWTClaims(ctx)
if !ok {
return ctx.String(http.StatusUnauthorized, "missing claims")
}
return ctx.JSON(http.StatusOK, claims)
})
// Require specific roles
api.Use(security.RequireRoles("admin", "editor"))Supported Signing Methods
| Algorithm | Type | Key |
|---|---|---|
| HS256, HS384, HS512 | HMAC | Shared secret |
| RS256, RS384, RS512 | RSA | Private/Public key pair |
JWT Claims
The JWTClaims struct embeds jwt.RegisteredClaims and adds application-specific fields:
UserID,Username,Email-- user identity.Roles-- list of role strings for RBAC.Metadata-- arbitrary key-value data.- Standard JWT fields:
Issuer,Audience,ExpiresAt,NotBefore,IssuedAt,ID.
Rate Limiting
Per-IP token-bucket rate limiter that tracks request counts per time window.
limiter := app.Container().MustResolve("security:ratelimit").(*security.MemoryRateLimiter)
// Apply rate limiting middleware
app.Use(security.RateLimitMiddleware(limiter))Features:
- Per-IP tracking -- each client IP gets its own rate limit bucket.
- Configurable window -- requests per time window (default: 100 requests per minute).
- Response headers -- automatically sets
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset, andRetry-Afterheaders. - Skip paths -- exclude health and metrics endpoints from rate limiting.
- Memory-based -- in-memory tracking with automatic cleanup of expired entries.
- Per-user rate limiting -- optional per-user limits via
WithRateLimitPerUser().
CORS (Cross-Origin Resource Sharing)
Full CORS support with preflight handling and configurable policies.
corsManager := app.Container().MustResolve("security:cors").(*security.CORSManager)
app.Use(security.CORSMiddleware(corsManager))Features:
- Origin validation -- wildcard (
*) or explicit list of allowed origins. - Method filtering -- configurable allowed HTTP methods.
- Header control -- configurable allowed and exposed headers.
- Preflight handling -- automatic OPTIONS response with proper CORS headers.
- Max age -- browser preflight cache duration (default 3600 seconds).
- Credentials -- configurable
Access-Control-Allow-Credentials. - Private network -- optional
Access-Control-Allow-Private-Networksupport. - Secure preset --
SecureCORSConfig(origins)for production-hardened defaults.
// Secure CORS configuration for production
secureCfg := security.SecureCORSConfig([]string{
"https://app.example.com",
"https://admin.example.com",
})
corsManager := security.NewCORSManager(secureCfg, logger)API Key Management
Generate, validate, and manage API keys with scope-based authorization.
apiKeyManager := app.Container().MustResolve("security:apikeys").(*security.APIKeyManager)
// Generate a new API key with prefix
key, _ := apiKeyManager.GenerateAPIKey("prod")
// Register an API key with metadata
apiKeyManager.CreateAPIKey(&security.APIKeyInfo{
Key: key,
Name: "CI/CD Pipeline",
UserID: "service-account-1",
Scopes: []string{"deploy", "read"},
Metadata: map[string]any{"env": "production"},
})
// Validate a key (in custom logic)
info, err := apiKeyManager.ValidateAPIKey(ctx, key)
// Returns ErrAPIKeyMissing, ErrAPIKeyInvalid, ErrAPIKeyExpired, or ErrAPIKeyRevoked
// Check key capabilities
if info.HasScope("deploy") { /* allowed */ }
if info.IsExpired() { /* reject */ }
// Key lifecycle management
apiKeyManager.UpdateLastUsed(key)
apiKeyManager.RevokeAPIKey(key)
apiKeyManager.DeleteAPIKey(key)
// List all keys for a user
keys := apiKeyManager.ListAPIKeys("service-account-1")API Key Middleware
app.Use(security.APIKeyMiddleware(apiKeyManager))
// Read key info in handlers
info, ok := security.GetAPIKeyInfo(ctx)
// Require specific scopes
app.Use(security.RequireScopes("deploy", "write"))Key Hashing
For storing API keys securely in a database:
hasher := app.Container().MustResolve("security:passwords").(*security.PasswordHasher)
// Hash a key for storage
hash, err := security.HashAPIKey(key, hasher)
// Verify a key against its stored hash
match, err := security.VerifyAPIKeyHash(key, hash, hasher)
// Constant-time comparison for raw key comparison
equal := security.ConstantTimeCompare(key1, key2)Password Hashing
Production-grade password hashing with Argon2id (default) or bcrypt, plus password strength utilities.
Hashing and Verification
hasher := app.Container().MustResolve("security:passwords").(*security.PasswordHasher)
// Hash a password
hash, err := hasher.Hash("user-password")
// Verify a password against its hash
match, err := hasher.Verify("user-password", hash)
// Check if a hash needs rehashing (after config change)
needsRehash, _ := hasher.NeedsRehash(hash)
if needsRehash {
newHash, _ := hasher.Hash("user-password")
// Update stored hash
}Password Strength
// Check password strength (returns a score)
strength := security.CheckPasswordStrength("myP@ssw0rd!")
// PasswordVeryWeak, PasswordWeak, PasswordFair, PasswordStrong, PasswordVeryStrong
// Validate against a policy
err := security.ValidatePassword(password,
8, // minLength
72, // maxLength
true, // requireUpper
true, // requireLower
true, // requireDigit
true, // requireSpecial
)
// Generate a random password
randomPw, _ := security.GenerateRandomPassword(24)Algorithms
| Algorithm | Default | Notes |
|---|---|---|
| Argon2id | Yes | Memory-hard, recommended for new projects. Configurable memory, iterations, parallelism, salt/key length. |
| Bcrypt | No | Widely supported, configurable cost (default 12). Max password length 72 bytes. |
Security Headers
Automatic injection of security-related HTTP headers.
headersManager := app.Container().MustResolve("security:headers").(*security.SecurityHeadersManager)
app.Use(security.SecurityHeadersMiddleware(headersManager))Headers set by default:
| Header | Default Value |
|---|---|
X-Frame-Options | DENY |
X-Content-Type-Options | nosniff |
X-XSS-Protection | 1; mode=block |
Strict-Transport-Security | max-age=31536000; includeSubDomains |
Referrer-Policy | strict-origin-when-cross-origin |
Content-Security-Policy | Configurable via CSPBuilder |
CSP Builder
Build Content Security Policy headers programmatically:
csp := security.NewCSPBuilder().
DefaultSrc("'self'").
ScriptSrc("'self'", "https://cdn.example.com").
StyleSrc("'self'", "'unsafe-inline'").
ImgSrc("'self'", "data:", "https:").
ConnectSrc("'self'", "https://api.example.com").
FrameAncestors("'none'").
Build()Additional options:
- Remove Server header -- strips the
Serverresponse header. - Remove X-Powered-By -- strips framework identification headers.
- Custom headers -- add arbitrary security headers.
- Secure preset --
SecureHeadersPreset()for production-hardened defaults.
Audit Logging
Structured audit logging for authentication events, data access, and administrative actions.
auditLogger := app.Container().MustResolve("security:audit").(*security.AuditLogger)
// Apply audit middleware to log all HTTP requests
app.Use(security.AuditMiddleware(auditLogger))Event Categories
Four helper functions log events with structured fields:
// Authentication events (login, logout, token refresh)
security.LogAuthEvent(auditLogger, ctx, "login_success", "success", "user-123", metadata)
// Data access events (read, write, delete)
security.LogDataEvent(auditLogger, ctx, "record_deleted", "success", "user-123", "users/456", metadata)
// Security events (suspicious activity, policy violations)
security.LogSecurityEvent(auditLogger, ctx, "brute_force_detected", "warning", "5 failed attempts from 10.0.0.1", metadata)
// Administrative actions (user management, config changes)
security.LogAdminEvent(auditLogger, ctx, "role_changed", "success", "admin-1", "promote_user", metadata)Audit Entry Structure
Each AuditEntry contains:
Timestamp,EventType,Category,Result-- when, what, and outcome.UserID,Username,Email-- who performed the action.Method,Path,StatusCode,Duration-- HTTP request details.IPAddress,UserAgent-- client identification.RequestBody,ResponseBody-- optionally captured (configurable).Metadata-- arbitrary structured data.
Audit Levels
| Level | What is logged |
|---|---|
"none" | Nothing |
"auth" | Authentication events only |
"all" | All requests and events |
Sensitive headers (Authorization, Cookie, X-API-Key) and fields are automatically redacted from audit logs.
Sentinel Errors
| Category | Errors |
|---|---|
| Session | ErrSessionNotFound, ErrSessionExpired, ErrInvalidSession |
| Cookie | ErrCookieNotFound, ErrInvalidCookie |
| CSRF | ErrCSRFTokenMissing, ErrCSRFTokenInvalid, ErrCSRFTokenExpired |
| JWT | ErrJWTMissing, ErrJWTInvalid, ErrJWTExpired, ErrJWTNotYetValid |
| API Key | ErrAPIKeyMissing, ErrAPIKeyInvalid, ErrAPIKeyExpired, ErrAPIKeyRevoked |
| Password | ErrInvalidHash, ErrIncompatibleVersion, ErrPasswordTooLong |
How is this guide?