Auth API¶
Session¶
In-memory session store with 256-bit random session IDs.
Constants¶
SESSION_ID_LEN = 32— session ID length in bytes (256-bit)
CookieConfig¶
httponly: bool = true— not accessible to JavaScriptsecure: bool = true— only sent over HTTPSsamesite: [:0]const u8 = "Strict"— SameSite policypath: [:0]const u8 = "/"— cookie pathmax_age: u32 = 86400— session max age in seconds (24 hours)
Functions¶
cookieConfig() CookieConfig— return the default cookie config
Session¶
id: [SESSION_ID_LEN]u8— raw session ID bytesdata: std.StringHashMap([]const u8)— session key-value dataexpires_at: i64— Unix timestamp when session expires (0 = no expiry)
Methods¶
session.put(gpa, key, value) !void— set session data (deep-copies key and value)session.get(key) ?[]const u8— get session datasession.isExpired(io) bool— check if session has expiredsession.deinit(gpa)— free session data
SessionStore¶
SessionStore.init(gpa) SessionStore— create an in-memory session storestore.deinit()— free all sessions and the storestore.create(io) Session— create a new session with random ID and default expirystore.createWithExpiry(io, expires_at) Session— create a session with a specific expiry timestampstore.save(session) !void— save a session into the store (deep-copies all data)store.getByHexId(hex_id, io) !?*Session— load a session by hex-encoded cookie value; returns null if expiredstore.get(raw_id, io) !?*Session— load a session by raw ID bytesstore.destroyByHexId(hex_id) !void— destroy a session by hex-encoded IDstore.destroy(raw_id) !void— destroy a session by raw ID bytes
Entropy Source¶
- Linux:
getrandom()syscall - BSD/macOS:
arc4random_buf(when linked with libc) - Other: returns
error.EntropyUnavailable
Password¶
Password hashing and verification using PBKDF2-HMAC-SHA256.
Constants¶
HASH_LEN = 32— hash output lengthSALT_LEN = 16— salt lengthITERATIONS = 100_000— PBKDF2 iteration count
Methods¶
hash(io, gpa, plaintext) ![]const u8— hash a password; returns owned string in format$pbkdf2-sha256$100000$<salt_hex>$<hash_hex>verify(stored_hash, plaintext) !bool— verify a password against a stored hash (constant-time comparison)
PasswordError¶
InvalidHashFormat— stored hash has unexpected formatHashMismatch— (via timing-safe compare)
User¶
User model with hashed password, role, and active status.
Fields¶
username: []const u8— owned usernamepassword_hash: []const u8— owned PBKDF2 hashrole: []const u8 = "user"— user role string (e.g. "user", "admin")is_active: bool = true— whether the account is activegpa: std.mem.Allocator— allocator for owned fields
Methods¶
User.init(io, gpa, username, plaintext) !User— create a user with hashed passworduser.deinit()— free user resourcesuser.authenticate(plaintext) !bool— verify password; returns false if inactiveuser.setRole(role)— set the user's role (deep-copies)user.deactivate()— mark the user inactive
Auth Middleware Functions¶
loginRequired(req, res, next)— guard that requires an authenticated user; redirects to/loginwith 302 ifreq.useris null. Uses*const fn (*Request, *Response) voidnext (not the middlewareNextFn— does not receiveio)superuserRequired(req, res, next)— guard that requires a user with role"admin"; returns 403 if not admin
Built-in Views¶
loginView(req, res)— GET renders a login form with CSRF token; POST processes loginlogoutView(req, res)— destroys session cookie and redirects to/registerView(req, res)— GET renders a registration form with CSRF token; POST processes registration
Full Example¶
const std = @import("std");
const zypher = @import("zypher");
pub fn main(init: std.process.Init) !void {
const io = init.io;
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
// Create a session store
var store = zypher.auth.session.SessionStore.init(gpa.allocator());
defer store.deinit();
// Create a session
var session = store.create(io);
defer session.deinit(gpa.allocator());
// Store data
try session.put(gpa.allocator(), "username", "alice");
try session.put(gpa.allocator(), "role", "admin");
// Save to store
try store.save(&session);
// Hash a password
const hash_str = try zypher.auth.password.hash(io, gpa.allocator(), "my_secret_password");
defer gpa.allocator().free(hash_str);
// Verify
const valid = try zypher.auth.password.verify(hash_str, "my_secret_password");
// Create a User
var user = try zypher.auth.user.User.init(io, gpa.allocator(), "alice", "password123");
defer user.deinit();
try user.setRole("admin");
}