feat(cropper): save with the session token — no OTP, domain from a2loft
Consumes the a2loft cropper-token-auth change: the save now sends the
session's PocketBase token as a bearer credential and no phone/otp. The
backend authorizes with OpenFGA crop_image (domain:<code> -> domain:idid),
so an operator no longer needs super-user rights in the profile's domain —
which is what made saves fail with a blanket 'Invalid OTP specified.'.
- the save's domain comes from a2loft's own record of the picture
(dims/picture/{uploadId} -> domainCode), falling back to the queue label,
guarded by a path-safety pattern instead of the catalog check
- the 'core' -> 'uc' alias is deleted: 'core' is a2loft's own pseudo-domain
for a profile picture (special-cased there), so rewriting it changed the
attribution and the ID-card notification
- queue loads are enriched with the profile's domainCode; the panel shows
the profile's domain and, when it differs, the picture target
- the cropper's OTP field, state and parameters are gone; the fingerprint
wizard is untouched (its own OTP flow on its own endpoints)
- tests updated: bearer + no query auth, resolved domain wins, unsafe values
refused, no OTP field served (46 pass)
Requires the a2loft change deployed first.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
# Spec Delta: image-cropper
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### 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 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
|
||||
- **THEN** the route verifies the live gate and forwards the picture to
|
||||
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 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 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
|
||||
- **THEN** their phone is already known to the page without any login form
|
||||
Reference in New Issue
Block a user