From 1e43e967323eebb65c166664105abbdba4651a39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Fri, 8 May 2026 02:02:24 +0200 Subject: [PATCH] Archive mobile-terms-gate + sync spec delta Co-Authored-By: Claude Opus 4.7 (1M context) --- .../2026-05-08-mobile-terms-gate}/proposal.md | 0 .../specs/journal-auth/spec.md | 0 .../2026-05-08-mobile-terms-gate}/tasks.md | 2 +- openspec/specs/journal-auth/spec.md | 16 ++++++++++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) rename openspec/changes/{mobile-terms-gate => archive/2026-05-08-mobile-terms-gate}/proposal.md (100%) rename openspec/changes/{mobile-terms-gate => archive/2026-05-08-mobile-terms-gate}/specs/journal-auth/spec.md (100%) rename openspec/changes/{mobile-terms-gate => archive/2026-05-08-mobile-terms-gate}/tasks.md (94%) diff --git a/openspec/changes/mobile-terms-gate/proposal.md b/openspec/changes/archive/2026-05-08-mobile-terms-gate/proposal.md similarity index 100% rename from openspec/changes/mobile-terms-gate/proposal.md rename to openspec/changes/archive/2026-05-08-mobile-terms-gate/proposal.md diff --git a/openspec/changes/mobile-terms-gate/specs/journal-auth/spec.md b/openspec/changes/archive/2026-05-08-mobile-terms-gate/specs/journal-auth/spec.md similarity index 100% rename from openspec/changes/mobile-terms-gate/specs/journal-auth/spec.md rename to openspec/changes/archive/2026-05-08-mobile-terms-gate/specs/journal-auth/spec.md diff --git a/openspec/changes/mobile-terms-gate/tasks.md b/openspec/changes/archive/2026-05-08-mobile-terms-gate/tasks.md similarity index 94% rename from openspec/changes/mobile-terms-gate/tasks.md rename to openspec/changes/archive/2026-05-08-mobile-terms-gate/tasks.md index f8328ab..b400edc 100644 --- a/openspec/changes/mobile-terms-gate/tasks.md +++ b/openspec/changes/archive/2026-05-08-mobile-terms-gate/tasks.md @@ -10,4 +10,4 @@ ## 3. Verification - [x] 3.1 Run `pnpm typecheck && pnpm lint && pnpm test` — all green. -- [ ] 3.2 Open draft PR against `main`. +- [x] 3.2 Open draft PR against `main` (#366). diff --git a/openspec/specs/journal-auth/spec.md b/openspec/specs/journal-auth/spec.md index c2e8bdf..625db15 100644 --- a/openspec/specs/journal-auth/spec.md +++ b/openspec/specs/journal-auth/spec.md @@ -59,3 +59,19 @@ Logged-in users whose stored `terms_version` does not match the currently-publis #### Scenario: returnTo is restricted to same-origin paths - **WHEN** a `returnTo` value is not a same-origin absolute path (missing leading `/`, or starting with `//`) - **THEN** the server redirects to `/` instead, preventing open-redirect abuse + +### Requirement: Terms gate applies to bearer-token API requests +Authenticated API requests carrying an OAuth2 bearer token SHALL be subject to the same Terms-version gate as cookie-session requests. When the authenticated user's stored `terms_version` is NULL or differs from the current `TERMS_VERSION`, the API chokepoint (`requireApiUser`) SHALL reject the request with HTTP 403 and a structured JSON body `{ error, code: "TERMS_OUTDATED", currentTermsVersion: }` instead of returning the resource. Cookie-session web traffic continues to be handled by the root-loader redirect to `/auth/accept-terms`; only the API path uses the structured 403. + +#### Scenario: Stale bearer token receives structured 403 +- **WHEN** a request to `/api/v1/*` arrives with a valid bearer token whose user has `terms_version` NULL or different from the current `TERMS_VERSION` +- **THEN** the server responds with HTTP 403 and JSON `{ error: , code: "TERMS_OUTDATED", currentTermsVersion: }` +- **AND** the requested resource is not returned + +#### Scenario: Up-to-date bearer token is allowed through +- **WHEN** a request to `/api/v1/*` arrives with a valid bearer token whose user's `terms_version` matches the current `TERMS_VERSION` +- **THEN** the request proceeds normally + +#### Scenario: Anonymous API request still 401 +- **WHEN** a request to `/api/v1/*` arrives without a bearer token (or with an invalid one) +- **THEN** the server responds with HTTP 401 `UNAUTHORIZED` as before — the Terms check only applies after authentication succeeds