Session Management01
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 Protection02
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 Management03
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: "[email protected]",
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 Limiting04
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)05
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 Management06
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 Hashing07
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 Headers08
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 Logging09
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 Errors10
| 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 |