trails/openspec/specs/journal-auth/spec.md
Ullrich Schäfer 9c4c3d6444
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>
2026-04-19 07:46:34 +02:00

1.6 KiB

Purpose

Authentication for the Journal app, including OAuth token storage for external services in the sync_connections table.

Requirements

Requirement: Store external service tokens

The journal auth system SHALL store OAuth tokens for external services alongside user credentials.

Scenario: Wahoo token storage

  • WHEN a user connects their Wahoo account
  • THEN access token, refresh token, expiry time, and Wahoo user ID are stored in the wahoo_tokens table
  • AND tokens are associated with the journal user ID

Requirement: Terms acknowledgement at signup

The registration form SHALL require explicit acknowledgement of the Terms of Service before an account can be created.

Scenario: Checkbox required

  • WHEN a user views the registration form
  • THEN they see a required checkbox labeled "I have read and agree to the Terms of Service, including that trails.cool is in alpha and my data may be reset"
  • AND the checkbox label links to the Terms page

Scenario: Cannot submit without acknowledgement

  • WHEN a user attempts to register without checking the acknowledgement box
  • THEN the form blocks submission and shows a validation message

Scenario: Acknowledgement recorded

  • WHEN a user successfully registers
  • THEN the current timestamp is stored in users.terms_accepted_at
  • AND the version identifier of the Terms the user saw is stored in users.terms_version

Scenario: Missing version rejected

  • WHEN a registration request arrives without a non-empty termsVersion field
  • THEN the server responds with HTTP 400 and does not create a user