Token hashing at rest
Token hashing at rest means a session token is never stored as its raw, replayable value, only a derived lookup key sits in the database. Caisson's auth package derives an HMAC-SHA-256 lookup key from the raw token, and the site's better-auth adapter wrap swaps every session query to that key, fail-closed if the HMAC key is missing.
In code
import { createHmac } from "node:crypto";
/**
* HMAC-SHA-256 of `rawToken` keyed by `hmacKey`, hex-encoded (64 lowercase hex characters).
* Deterministic — same inputs always produce the same lookup key, so it doubles as an indexed
* database lookup value. The key never touches the database: a Postgres dump alone cannot be
* reversed back into a usable session cookie without `hmacKey`.
*/
export function deriveTokenLookupKey(
rawToken: string,
hmacKey: string,
): string {
return createHmac("sha256", hmacKey).update(rawToken).digest("hex");
}How it holds
One derived value, no schema change
Instead of adding a second column for a hashed value, the HMAC-SHA-256 lookup key replaces the raw token directly in the existing session.token column better-auth already unique-indexes, a plain indexed equality match on the lookup key, no new migration.
The adapter wrap is the single write/read seam
wrapSessionAdapter attaches at the one construction site in auth-server.ts and only intercepts the session model's token field, every other model, and every session query that doesn't touch token, passes straight through to the underlying better-auth adapter untouched.
Throws loudly on an unrecognized query shape, never guesses
rewriteTokenWhere only rewrites the two where-clause shapes better-auth's session queries build today (a single eq/string value or an in/string-array value); a future better-auth upgrade that changes that shape hits an explicit throw instead of silently producing a broken or unhashed lookup.
Fail-closed boot, not a raw-token fallback
getAuth() throws before starting if DATABASE_URL and BETTER_AUTH_SECRET are configured but SESSION_TOKEN_HMAC_KEY is missing, a forgotten env var crashes boot loudly instead of quietly falling back to storing raw tokens.