Store Terms version alongside acceptance timestamp
Reviewer's follow-up: it's not enough to record when a user accepted the Terms; we also need to record which version of the text they saw. Changes: - journal.users gains a nullable `terms_version` text column (nullable so the three pre-existing users without a version are kept as-is). - New apps/journal/app/lib/legal.ts exports TERMS_VERSION as a single source of truth, reused by the legal pages' "Last updated" header and by the registration flow as the value to send/store. - Registration form posts `termsVersion` alongside `termsAccepted` on all three relevant steps (start, finish, register-magic-link). - API route validates that `termsVersion` is a non-empty string on any step that requires terms, and forwards it to the auth server. - auth.server finishRegistration and registerWithMagicLink now take `termsVersion` and persist it on the users row. - journal-auth spec gets a new scenario for version storage and a rejection scenario for missing version. PRIVACY_LAST_UPDATED is also exported from the same module and used by the Privacy page header, keeping both pages on a single legal.ts source of truth for "last updated" labels. Privacy is not per-user stored — it's informational, not contract. Existing users have NULL terms_version; if we ever prompt them to re-accept updated Terms, we can backfill with the version they re-accept at that point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c532a1024b
commit
9c4c3d6444
8 changed files with 69 additions and 10 deletions
|
|
@ -53,6 +53,7 @@ export async function finishRegistration(
|
|||
username: string,
|
||||
response: RegistrationResponseJSON,
|
||||
challenge: string,
|
||||
termsVersion: string,
|
||||
) {
|
||||
const db = getDb();
|
||||
|
||||
|
|
@ -77,6 +78,7 @@ export async function finishRegistration(
|
|||
username,
|
||||
domain,
|
||||
termsAcceptedAt: new Date(),
|
||||
termsVersion,
|
||||
});
|
||||
|
||||
await db.insert(credentials).values({
|
||||
|
|
@ -147,7 +149,11 @@ export async function addPasskeyFinish(
|
|||
|
||||
// --- Registration via Magic Link (no passkey) ---
|
||||
|
||||
export async function registerWithMagicLink(email: string, username: string): Promise<string> {
|
||||
export async function registerWithMagicLink(
|
||||
email: string,
|
||||
username: string,
|
||||
termsVersion: string,
|
||||
): Promise<string> {
|
||||
const db = getDb();
|
||||
|
||||
const [existingEmail] = await db.select().from(users).where(eq(users.email, email));
|
||||
|
|
@ -159,7 +165,14 @@ export async function registerWithMagicLink(email: string, username: string): Pr
|
|||
const userId = randomUUID();
|
||||
const domain = process.env.DOMAIN ?? "localhost";
|
||||
|
||||
await db.insert(users).values({ id: userId, email, username, domain, termsAcceptedAt: new Date() });
|
||||
await db.insert(users).values({
|
||||
id: userId,
|
||||
email,
|
||||
username,
|
||||
domain,
|
||||
termsAcceptedAt: new Date(),
|
||||
termsVersion,
|
||||
});
|
||||
|
||||
// Create magic token for verification
|
||||
const token = randomBytes(32).toString("base64url");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue