feat(intake): token generator and shape check

Phase 8a Task 2. 24 bytes of CSPRNG entropy, base64url-encoded to
exactly 32 URL-safe characters. isIntakeToken matches that exact
shape so the public route can fail fast on bogus input without
hitting the database or charging the rate limiter.
This commit is contained in:
Dave 2026-09-21 01:57:38 +02:00
parent 2937a305c3
commit 36572d99f3

24
src/lib/intake/token.ts Normal file
View file

@ -0,0 +1,24 @@
import { randomBytes } from "node:crypto";
/**
* 24 bytes of entropy, base64url encoded → 32 URL-safe characters.
*
* The token is the link's whole authentication. It must be unguessable and
* must not be derived from anything the public can see (no project id, no
* timestamp, no hash of either). `crypto.randomBytes` satisfies both: it is
* CSPRNG output, and the rest of the system treats the result as opaque.
*
* 24 bytes is 192 bits of entropy. Brute-forcing one token against the
* `(token)` unique index is wasted effort at any realistic rate limit.
*/
export function generateIntakeToken(): string {
return randomBytes(24).toString("base64url");
}
const TOKEN_PATTERN = /^[A-Za-z0-9_-]{32}$/;
/** True when `value` is the shape the generator produces. Used by the
* public route to reject obviously-bogus input early. */
export function isIntakeToken(value: unknown): value is string {
return typeof value === "string" && TOKEN_PATTERN.test(value);
}