diff --git a/openspec/changes/cropper-token-auth/.openspec.yaml b/openspec/changes/archive/2026-10-06-cropper-token-auth/.openspec.yaml similarity index 100% rename from openspec/changes/cropper-token-auth/.openspec.yaml rename to openspec/changes/archive/2026-10-06-cropper-token-auth/.openspec.yaml diff --git a/openspec/changes/cropper-token-auth/design.md b/openspec/changes/archive/2026-10-06-cropper-token-auth/design.md similarity index 100% rename from openspec/changes/cropper-token-auth/design.md rename to openspec/changes/archive/2026-10-06-cropper-token-auth/design.md diff --git a/openspec/changes/cropper-token-auth/proposal.md b/openspec/changes/archive/2026-10-06-cropper-token-auth/proposal.md similarity index 100% rename from openspec/changes/cropper-token-auth/proposal.md rename to openspec/changes/archive/2026-10-06-cropper-token-auth/proposal.md diff --git a/openspec/changes/cropper-token-auth/specs/image-cropper/spec.md b/openspec/changes/archive/2026-10-06-cropper-token-auth/specs/image-cropper/spec.md similarity index 100% rename from openspec/changes/cropper-token-auth/specs/image-cropper/spec.md rename to openspec/changes/archive/2026-10-06-cropper-token-auth/specs/image-cropper/spec.md diff --git a/openspec/changes/cropper-token-auth/tasks.md b/openspec/changes/archive/2026-10-06-cropper-token-auth/tasks.md similarity index 100% rename from openspec/changes/cropper-token-auth/tasks.md rename to openspec/changes/archive/2026-10-06-cropper-token-auth/tasks.md diff --git a/openspec/specs/image-cropper/spec.md b/openspec/specs/image-cropper/spec.md index a764ed0..36816ce 100644 --- a/openspec/specs/image-cropper/spec.md +++ b/openspec/specs/image-cropper/spec.md @@ -60,24 +60,33 @@ only for permitted operators. ### Requirement: Saving requires a live gate and an OTP Saving a cropped picture (`/cropper/profile/picture`) SHALL verify the crop -permission live (bypassing the cache) and SHALL require an OTP from the -operator, forwarding the session phone as the validating identity together -with the OTP and the picture's domain to a2loft. A denied or OTP-less save -SHALL not reach a2loft. +permission live (bypassing the cache) and SHALL authenticate the operator with +the session's PocketBase token, sent to a2loft as a bearer credential; no OTP +SHALL be collected or forwarded. The picture's domain SHALL come from a2loft's +own record for the upload (`dims/picture/{uploadId}` → `domainCode`), falling +back to the queue item's label, and SHALL match a path-safe pattern before it +is used in the request. A denied save SHALL not reach a2loft. #### Scenario: Successful save -- **WHEN** a permitted operator saves with an OTP +- **WHEN** a permitted operator saves - **THEN** the route verifies the live gate and forwards the picture to - a2loft with the session phone, OTP, and picture domain + a2loft with the session token as a bearer credential and the profile's + resolved domain #### Scenario: Denied save does not reach a2loft -- **WHEN** the live crop check denies, or the OTP is missing +- **WHEN** the live crop check denies, or the request carries no message body - **THEN** the route answers 403/400 and makes no a2loft call +#### Scenario: Unsafe domain values are refused +- **WHEN** neither a2loft's record nor the queue label yields a path-safe + domain +- **THEN** the save is refused with 400 and a2loft is not contacted + ### Requirement: Unified login — no separate artist login The cropper SHALL NOT implement its own login. The operator's identity comes -from the YOUreka session (phone prefilled), the picture's metadata carries the -domain, and the only credential the operator supplies is the save-time OTP. +from the YOUreka session and is presented to a2loft as the session's PocketBase +token; the picture's own metadata carries the domain. The cropper collects no +other credential — there is no save-time OTP. #### Scenario: Phone prefilled from the session - **WHEN** a signed-in operator opens the cropper