import { first, run } from "./db";
import { sha256Hex } from "./crypto";
export type AuthTokenKind =
| "email-verify"
| "email-verify-spent"
| "password-reset"
| "webauthn"
| "pending-login"
| "editor-session"
| "editor-preview"
| "mcp-authz";
type Row = { user_id: string | null; payload: string; created_at: number };
export type ConsumedToken = { userId: string | null; payload: string; createdAt: number };
/** Stores single-use state keyed by the hash of a secret token. */
export async function storeAuthToken(
kind: AuthTokenKind,
token: string,
userId: string | null,
payload: string,
ttlSeconds: number,
): Promise<void> {
const now = Date.now();
await run(
"INSERT INTO auth_tokens (token_hash, kind, user_id, payload, created_at, expires_at) VALUES (?, ?, ?, ?, ?, ?)",
await sha256Hex(token),
kind,
userId,
payload,
now,
now + ttlSeconds * 1000,
);
}
/** Atomically consumes a token: concurrent callers can't both succeed. */
export async function consumeAuthToken(kind: AuthTokenKind, token: string): Promise<ConsumedToken | null> {
const row = await first<Row>(
"DELETE FROM auth_tokens WHERE token_hash = ? AND kind = ? AND expires_at > ? RETURNING user_id, payload, created_at",