Authentication
Authentication is the process of verifying who a user is, while authorization determines what they can access. This section covers various authentication methods, token management, and security best practices for HTTP servers.
Authentication Methods
Bearer Token Authentication
Bearer tokens are the most common authentication method for APIs, especially JWT (JSON Web Tokens).
Examples:
JWT Bearer Token Middleware:
| JWT Bearer Token |
|---|
| import http
import json
import strings
import fmt
// JWT authentication middleware
function jwtAuthMiddleware(next function) {
return function(request map, response map) {
var authHeader = request["headers"]["Authorization"]
if authHeader == None or authHeader == "" {
sendUnauthorizedResponse(response, "Missing Authorization header")
return
}
if !strings.HasPrefix(authHeader, "Bearer ") {
sendUnauthorizedResponse(response, "Invalid Authorization header format")
return
}
var token = strings.TrimPrefix(authHeader, "Bearer ")
var claims = validateJWTToken(token)
if claims == None {
sendUnauthorizedResponse(response, "Invalid or expired token")
return
}
// Add user information to request context
request["user"] = claims["user"]
request["userId"] = claims["sub"]
request["roles"] = claims["roles"]
request["authenticated"] = true
next(request, response)
}
}
// JWT token validation
function validateJWTToken(token string) {
// In a real implementation, this would:
// 1. Verify the token signature
// 2. Check expiration
// 3. Validate issuer and audience
// 4. Return decoded claims
// Simplified validation for example
if token == "valid-jwt-token" {
return {
"sub": "user123",
"user": {
"id": 123,
"username": "alice",
"email": "alice@example.com"
},
"roles": ["user", "admin"],
"exp": 1640995200, // Expiration timestamp
"iat": 1640908800 // Issued at timestamp
}
}
return None
}
#### Mux-Level API Key Auth Helper
For simple header-based API key checks you can use the built-in helper
`http.WithAPIKeyAuth`, which attaches an authentication handler directly to a mux.
```harneet title="WithAPIKeyAuth on a Mux"
import http
import fmt
function protectedHandler(request any, response any) {
response.json({"status": "ok", "path": request["path"]})
}
function startAPIKeyServer() {
var server, serverErr = http.NewServer(":8080")
if serverErr != None {
fmt.Printf("Server error: %s\n", serverErr)
return
}
var mux, muxErr = http.NewMux()
if muxErr != None {
fmt.Printf("Mux error: %s\n", muxErr)
return
}
// Require a specific X-API-Key value for all HandleRoute routes on this mux
var authMux, authErr = http.WithAPIKeyAuth(mux, "X-API-Key", "key_123abc")
if authErr != None {
fmt.Printf("WithAPIKeyAuth error: %s\n", authErr)
return
}
var _, routeErr = http.HandleRoute(authMux, "GET", "/protected/{id}", protectedHandler)
if routeErr != None {
fmt.Printf("Route error: %s\n", routeErr)
return
}
var setErr = http.SetHandler(server, authMux)[1]
if setErr != None {
fmt.Printf("SetHandler error: %s\n", setErr)
return
}
fmt.Println("API key protected server starting on :8080")
fmt.Println("Try: curl -H 'X-API-Key: key_123abc' http://localhost:8080/protected/1")
http.ListenAndServe(server)
}
|
Mux-Level Auth and Rate Limit Hooks
In addition to middleware, you can attach hooks directly to a mux that run before each http.HandleRoute handler:
http.WithAuth(mux, authHandler) (mux, error) http.WithRateLimit(mux, rateLimitHandler) (mux, error) http.WithSimpleIPRateLimit(mux, requestsPerMinute) (mux, error)
These hooks are ideal for coarse-grained policies such as "all routes on this mux require authentication" or "limit each client IP to N requests per minute".
| function sendUnauthorizedResponse(response map, message string) {
var errorResponse = {
"error": "Unauthorized",
"message": message,
"status": 401
}
var jsonData, _ = json.Marshal(errorResponse)
http.WriteResponse(response, 401, "application/json", jsonData)
}
|
API Key Authentication
API keys are simple tokens used for service-to-service authentication.
Examples:
API Key Middleware:
| API Key Auth |
|---|
| import http
import json
import fmt
// API key authentication middleware
function apiKeyAuthMiddleware(next function) {
return function(request map, response map) {
var apiKey = getAPIKeyFromRequest(request)
if apiKey == "" {
sendUnauthorizedResponse(response, "Missing API key")
return
}
var keyInfo = validateAPIKey(apiKey)
if keyInfo == None {
sendUnauthorizedResponse(response, "Invalid API key")
return
}
// Add API key information to request
request["apiKey"] = apiKey
request["apiKeyInfo"] = keyInfo
request["authenticated"] = true
next(request, response)
}
}
function getAPIKeyFromRequest(request map) {
var headers = request["headers"]
var query = request["query"]
// Check X-API-Key header
var headerKey = headers["X-API-Key"]
if headerKey != None and headerKey != "" {
return headerKey
}
// Check Authorization header with API key format
var authHeader = headers["Authorization"]
if authHeader != None and strings.HasPrefix(authHeader, "ApiKey ") {
return strings.TrimPrefix(authHeader, "ApiKey ")
}
// Check query parameter
var queryKey = query["api_key"]
if queryKey != None and queryKey != "" {
return queryKey
}
return ""
}
function validateAPIKey(apiKey string) {
// In a real implementation, this would check against a database
var validKeys = {
"key_123abc": {
"id": "key_123abc",
"name": "Production API Key",
"userId": 123,
"permissions": ["read", "write"],
"rateLimit": 1000,
"active": true
},
"key_456def": {
"id": "key_456def",
"name": "Development API Key",
"userId": 456,
"permissions": ["read"],
"rateLimit": 100,
"active": true
}
}
var keyInfo = validKeys[apiKey]
if keyInfo != None and keyInfo["active"] {
return keyInfo
}
return None
}
|
Basic Authentication
HTTP Basic Authentication sends credentials in the Authorization header.
Examples:
Basic Auth Middleware:
| Basic Auth |
|---|
| import http
import json
import strings
import fmt
// Basic authentication middleware
function basicAuthMiddleware(next function) {
return function(request map, response map) {
var authHeader = request["headers"]["Authorization"]
if authHeader == None or !strings.HasPrefix(authHeader, "Basic ") {
sendBasicAuthChallenge(response)
return
}
var credentials = strings.TrimPrefix(authHeader, "Basic ")
var decodedCreds = base64Decode(credentials) // Simplified base64 decode
var parts = strings.Split(decodedCreds, ":")
if len(parts) != 2 {
sendBasicAuthChallenge(response)
return
}
var username = parts[0]
var password = parts[1]
var user = authenticateUser(username, password)
if user == None {
sendBasicAuthChallenge(response)
return
}
// Add user to request context
request["user"] = user
request["authenticated"] = true
next(request, response)
}
}
function sendBasicAuthChallenge(response map) {
http.SetResponseHeader(response, "WWW-Authenticate", "Basic realm=\"API\"")
var errorResponse = {
"error": "Unauthorized",
"message": "Basic authentication required",
"status": 401
}
var jsonData, _ = json.Marshal(errorResponse)
http.WriteResponse(response, 401, "application/json", jsonData)
}
function authenticateUser(username string, password string) {
// In a real implementation, this would check against a database
// with properly hashed passwords
var users = {
"admin": {
"username": "admin",
"passwordHash": "hashed_password_123", // In reality, use bcrypt
"roles": ["admin", "user"],
"active": true
},
"user": {
"username": "user",
"passwordHash": "hashed_password_456",
"roles": ["user"],
"active": true
}
}
var user = users[username]
if user != None and user["active"] and verifyPassword(password, user["passwordHash"]) {
return user
}
return None
}
function verifyPassword(password string, hash string) {
// In a real implementation, use proper password hashing (bcrypt, scrypt, etc.)
return password == "password123" // Simplified for example
}
|
Session-Based Authentication
Session-based authentication uses server-side sessions with cookies.
Examples:
Session Authentication:
| Session Auth |
|---|
| import http
import json
import fmt
// Session-based authentication middleware
function sessionAuthMiddleware(next function) {
return function(request map, response map) {
var sessionId = getSessionIdFromRequest(request)
if sessionId == "" {
sendUnauthorizedResponse(response, "No session found")
return
}
var session = getSession(sessionId)
if session == None or isSessionExpired(session) {
sendUnauthorizedResponse(response, "Invalid or expired session")
return
}
// Update session last accessed time
updateSessionAccess(sessionId)
// Add session and user to request context
request["session"] = session
request["user"] = session["user"]
request["authenticated"] = true
next(request, response)
}
}
function getSessionIdFromRequest(request map) {
var cookies = request["cookies"]
if cookies != None {
var sessionId = cookies["session_id"]
if sessionId != None {
return sessionId
}
}
// Fallback to header
var sessionHeader = request["headers"]["X-Session-ID"]
if sessionHeader != None {
return sessionHeader
}
return ""
}
// Session management functions
var sessions = {} // In-memory session store (use Redis/database in production)
function createSession(user map) {
var sessionId = generateSessionId()
var session = {
"id": sessionId,
"user": user,
"createdAt": getCurrentTimestamp(),
"lastAccessed": getCurrentTimestamp(),
"expiresAt": getCurrentTimestamp() + 3600 // 1 hour
}
sessions[sessionId] = session
return session
}
function getSession(sessionId string) {
return sessions[sessionId]
}
function updateSessionAccess(sessionId string) {
var session = sessions[sessionId]
if session != None {
session["lastAccessed"] = getCurrentTimestamp()
// Extend expiration
session["expiresAt"] = getCurrentTimestamp() + 3600
}
}
function isSessionExpired(session map) {
var expiresAt = session["expiresAt"]
var currentTime = getCurrentTimestamp()
return currentTime > expiresAt
}
function destroySession(sessionId string) {
delete(sessions, sessionId)
}
function generateRandomString() string {
return "abcde"
}
function generateSessionId() {
// In a real implementation, use a cryptographically secure random generator
return "session_" + generateRandomString(32)
}
|
Authorization and Role-Based Access Control
Authorization determines what authenticated users can access.
Examples:
Role-Based Authorization:
| Role-Based Authorization |
|---|
| import http
import json
import fmt
// Role-based authorization middleware
function requireRole(requiredRole string) {
return function(next function) {
return function(request map, response map) {
var user = request["user"]
if user == None {
sendForbiddenResponse(response, "Authentication required")
return
}
var userRoles = user["roles"]
if userRoles == None {
sendForbiddenResponse(response, "No roles assigned")
return
}
var hasRole = false
for role in userRoles {
if role == requiredRole {
hasRole = true
break
}
}
if !hasRole {
sendForbiddenResponse(response, "Insufficient permissions")
return
}
next(request, response)
}
}
}
// Permission-based authorization
function requirePermission(requiredPermission string) {
return function(next function) {
return function(request map, response map) {
var user = request["user"]
if user == None {
sendForbiddenResponse(response, "Authentication required")
return
}
var hasPermission = checkUserPermission(user, requiredPermission)
if !hasPermission {
sendForbiddenResponse(response, "Permission denied")
return
}
next(request, response)
}
}
}
function checkUserPermission(user map, permission string) {
var userRoles = user["roles"]
if userRoles == None {
return false
}
// Check role-based permissions
var rolePermissions = {
"admin": ["read", "write", "delete", "manage_users"],
"editor": ["read", "write"],
"user": ["read"]
}
for role in userRoles {
var permissions = rolePermissions[role]
if permissions != None {
for perm in permissions {
if perm == permission {
return true
}
}
}
}
return false
}
function sendForbiddenResponse(response map, message string) {
var errorResponse = {
"error": "Forbidden",
"message": message,
"status": 403
}
var jsonData, _ = json.Marshal(errorResponse)
http.WriteResponse(response, 403, "application/json", jsonData)
}
// Usage examples
function setupProtectedRoutes() {
var mux = http.NewMux()[0]
// Public routes
http.HandleFunc(mux, "/", homeHandler)
http.HandleFunc(mux, "/login", loginHandler)
// Protected routes with different authorization levels
var userHandler = chainMiddleware([jwtAuthMiddleware, requireRole("user")])(userProfileHandler)
var adminHandler = chainMiddleware([jwtAuthMiddleware, requireRole("admin")])(adminDashboardHandler)
var editorHandler = chainMiddleware([jwtAuthMiddleware, requirePermission("write")])(editContentHandler)
http.HandleFunc(mux, "/profile", userHandler)
http.HandleFunc(mux, "/admin", adminHandler)
http.HandleFunc(mux, "/edit", editorHandler)
return mux
}
|
Token Management
JWT Token Generation and Validation
| JWT Token Management |
|---|
| import http
import json
import fmt
// JWT token generation (simplified)
function generateJWTToken(user map) {
var header = {
"alg": "HS256",
"typ": "JWT"
}
var payload = {
"sub": user["id"],
"username": user["username"],
"email": user["email"],
"roles": user["roles"],
"iat": getCurrentTimestamp(),
"exp": getCurrentTimestamp() + 3600 // 1 hour expiration
}
// In a real implementation, properly encode and sign the JWT
var headerEncoded = base64Encode(json.Marshal(header)[0])
var payloadEncoded = base64Encode(json.Marshal(payload)[0])
var signature = signJWT(headerEncoded + "." + payloadEncoded, "secret-key")
return headerEncoded + "." + payloadEncoded + "." + signature
}
// Token refresh endpoint
function tokenRefreshHandler(request map, response map) {
var refreshToken = getRefreshTokenFromRequest(request)
if refreshToken == "" {
sendUnauthorizedResponse(response, "Missing refresh token")
return
}
var tokenInfo = validateRefreshToken(refreshToken)
if tokenInfo == None {
sendUnauthorizedResponse(response, "Invalid refresh token")
return
}
var user = getUserById(tokenInfo["userId"])
if user == None {
sendUnauthorizedResponse(response, "User not found")
return
}
// Generate new access token
var newAccessToken = generateJWTToken(user)
var newRefreshToken = generateRefreshToken(user["id"])
// Invalidate old refresh token
invalidateRefreshToken(refreshToken)
var tokenResponse = {
"access_token": newAccessToken,
"refresh_token": newRefreshToken,
"token_type": "Bearer",
"expires_in": 3600
}
var jsonData, _ = json.Marshal(tokenResponse)
http.WriteResponse(response, 200, "application/json", jsonData)
}
function generateRefreshToken(userId string) {
var refreshToken = "refresh_" + generateRandomString(64)
// Store refresh token (in database in real implementation)
var tokenInfo = {
"token": refreshToken,
"userId": userId,
"createdAt": getCurrentTimestamp(),
"expiresAt": getCurrentTimestamp() + 604800 // 7 days
}
storeRefreshToken(refreshToken, tokenInfo)
return refreshToken
}
|
Login and Logout Endpoints
Login Handler
| Login Handler |
|---|
| import http
import json
import fmt
// Login endpoint
function loginHandler(request map, response map) {
var method = request["method"]
if method != "POST" {
http.WriteResponse(response, 405, "application/json", "{\"error\": \"Method not allowed\"}")
return
}
var body = request["body"]
var loginData, parseErr = json.Unmarshal(body)
if parseErr != None {
http.WriteResponse(response, 400, "application/json", "{\"error\": \"Invalid JSON\"}")
return
}
var username = loginData["username"]
var password = loginData["password"]
if username == None or password == None {
http.WriteResponse(response, 400, "application/json", "{\"error\": \"Username and password required\"}")
return
}
// Authenticate user
var user = authenticateUser(username, password)
if user == None {
// Add delay to prevent timing attacks
sleep(1000) // 1 second delay
http.WriteResponse(response, 401, "application/json", "{\"error\": \"Invalid credentials\"}")
return
}
// Generate tokens
var accessToken = generateJWTToken(user)
var refreshToken = generateRefreshToken(user["id"])
// Create session (if using session-based auth)
var session = createSession(user)
// Set session cookie
http.SetCookie(response, "session_id", session["id"], {
"httpOnly": true,
"secure": true,
"sameSite": "strict",
"maxAge": 3600
})
var loginResponse = {
"success": true,
"user": {
"id": user["id"],
"username": user["username"],
"email": user["email"],
"roles": user["roles"]
},
"tokens": {
"access_token": accessToken,
"refresh_token": refreshToken,
"token_type": "Bearer",
"expires_in": 3600
}
}
var jsonData, _ = json.Marshal(loginResponse)
http.WriteResponse(response, 200, "application/json", jsonData)
}
// Logout endpoint
function logoutHandler(request map, response map) {
var method = request["method"]
if method != "POST" {
http.WriteResponse(response, 405, "application/json", "{\"error\": \"Method not allowed\"}")
return
}
// Get session ID from cookie
var sessionId = getSessionIdFromRequest(request)
if sessionId != "" {
destroySession(sessionId)
}
// Get refresh token and invalidate it
var body = request["body"]
if body != "" {
var logoutData, parseErr = json.Unmarshal(body)
if parseErr == None {
var refreshToken = logoutData["refresh_token"]
if refreshToken != None {
invalidateRefreshToken(refreshToken)
}
}
}
// Clear session cookie
http.SetCookie(response, "session_id", "", {
"httpOnly": true,
"secure": true,
"sameSite": "strict",
"maxAge": -1 // Expire immediately
})
var logoutResponse = {
"success": true,
"message": "Logged out successfully"
}
var jsonData, _ = json.Marshal(logoutResponse)
http.WriteResponse(response, 200, "application/json", jsonData)
}
|
Security Best Practices
Password Security
| Password Security |
|---|
| import http
import json
// Secure password hashing (conceptual - use proper crypto library)
function hashPassword(password string) {
// In a real implementation, use bcrypt, scrypt, or Argon2
return bcryptHash(password, 12) // Cost factor 12
}
function verifyPassword(password string, hash string) {
return bcryptVerify(password, hash)
}
// Password strength validation
function validatePasswordStrength(password string) {
var errors = []
if len(password) < 8 {
errors = append(errors, "Password must be at least 8 characters long")
}
if !containsUppercase(password) {
errors = append(errors, "Password must contain at least one uppercase letter")
}
if !containsLowercase(password) {
errors = append(errors, "Password must contain at least one lowercase letter")
}
if !containsDigit(password) {
errors = append(errors, "Password must contain at least one digit")
}
if !containsSpecialChar(password) {
errors = append(errors, "Password must contain at least one special character")
}
return errors
}
|
Rate Limiting for Authentication
| Auth Rate Limiting |
|---|
| import http
import fmt
// Authentication rate limiting
function authRateLimitMiddleware(maxAttempts int, windowMinutes int) {
var attempts = {} // Track failed attempts by IP
return function(next function) {
return function(request map, response map) {
var clientIP = getClientIP(request)
var currentTime = getCurrentMinute()
var key = clientIP + ":" + currentTime
var attemptCount = attempts[key]
if attemptCount == None {
attemptCount = 0
}
if attemptCount >= maxAttempts {
var errorResponse = {
"error": "Too Many Attempts",
"message": "Too many authentication attempts. Please try again later.",
"retryAfter": windowMinutes * 60
}
http.SetResponseHeader(response, "Retry-After", windowMinutes * 60)
var jsonData, _ = json.Marshal(errorResponse)
http.WriteResponse(response, 429, "application/json", jsonData)
return
}
// Track this attempt
attempts[key] = attemptCount + 1
next(request, response)
}
}
}
|
CSRF Protection
| CSRF Protection |
|---|
| import http
import json
// CSRF protection middleware
function csrfProtectionMiddleware(next function) {
return function(request map, response map) {
var method = request["method"]
// Only check CSRF for state-changing methods
if method == "POST" or method == "PUT" or method == "DELETE" or method == "PATCH" {
var csrfToken = getCSRFTokenFromRequest(request)
var sessionToken = getCSRFTokenFromSession(request)
if csrfToken == "" or sessionToken == "" or csrfToken != sessionToken {
var errorResponse = {
"error": "CSRF Token Mismatch",
"message": "Invalid or missing CSRF token"
}
var jsonData, _ = json.Marshal(errorResponse)
http.WriteResponse(response, 403, "application/json", jsonData)
return
}
}
next(request, response)
}
}
function getCSRFTokenFromRequest(request map) {
var headers = request["headers"]
// Check X-CSRF-Token header
var headerToken = headers["X-CSRF-Token"]
if headerToken != None {
return headerToken
}
// Check form data (if parsing form data)
var formData = request["formData"]
if formData != None {
var formToken = formData["csrf_token"]
if formToken != None {
return formToken
}
}
return ""
}
|
Complete Authentication Example
| Complete Auth System |
|---|
| import http
import json
import fmt
// Complete authentication setup
function setupAuthenticatedServer() {
var mux = http.NewMux()[0]
// Public routes
http.HandleFunc(mux, "/", homeHandler)
http.HandleFunc(mux, "/login", loginHandler)
http.HandleFunc(mux, "/register", registerHandler)
// Token management
http.HandleFunc(mux, "/refresh", tokenRefreshHandler)
// Protected routes
var protectedMux = http.NewMux()[0]
// User routes (requires authentication)
var userRoutes = chainMiddleware([jwtAuthMiddleware])(userHandler)
http.HandleFunc(protectedMux, "/profile", userRoutes)
http.HandleFunc(protectedMux, "/logout", logoutHandler)
// Admin routes (requires admin role)
var adminRoutes = chainMiddleware([jwtAuthMiddleware, requireRole("admin")])(adminHandler)
http.HandleFunc(protectedMux, "/admin", adminRoutes)
// API routes with rate limiting
var apiRoutes = chainMiddleware([
authRateLimitMiddleware(10, 1), // 10 attempts per minute
apiKeyAuthMiddleware
])(apiHandler)
http.HandleFunc(protectedMux, "/api/", apiRoutes)
// Mount protected routes
http.Handle(mux, "/protected/", http.StripPrefix("/protected", protectedMux))
return mux
}
// Start authenticated server
function startAuthServer() {
var mux = setupAuthenticatedServer()
var server = http.NewServer(":8080")[0]
// Add security middleware
var secureHandler = chainMiddleware([
loggingMiddleware,
corsMiddleware,
securityHeadersMiddleware,
csrfProtectionMiddleware
])(mux)
http.SetHandler(server, secureHandler)
fmt.Println("🔐 Authenticated server starting on :8080")
http.ListenAndServe(server)
}
|
Next Steps