Files

200 lines
9.0 KiB
Markdown
Raw Permalink Normal View History

# fingerprint-wizard Specification
## Purpose
The Fingerprint Agent enrollment wizard at `/fingerprint`: offers the operator
only the domains they may capture for, runs two-finger enrollment through the
local fpagent capture agent, and saves the resulting templates to a2loft with
per-call OTP validation.
## Requirements
### Requirement: Permitted domain selection
The wizard SHALL offer, from the hardcoded catalog — UNICAL (`uc`),
Metropolitan Hotel (`mtr`), UCTH (`ucth`), UNICAL Radio International (`uri`),
YOUreka (`yrka`) — only the domains the operator is permitted to capture for.
Permission SHALL be determined after login against the same public portal
OpenFGA verify endpoint fpagent uses (`https://portal.yrka.app/api/openfga/verify`),
requesting the `capture_biometric` relation for `domain:<code>` on subsystem
`idid` for the operator's phone (leading `+` stripped), with a fallback check
against `domain:idid`; the operator is permitted if either check allows. Each
domain's checks SHALL fail closed on error or denial. Catalog checks SHALL run
in parallel, and their results SHALL be cached per operator phone for 5
minutes: session fetches within that window SHALL serve the cached result
without contacting the portal, and all catalog domains SHALL be re-checked in
parallel once that period expires.
#### Scenario: Permitted domain is offered
- **WHEN** the portal verify endpoint allows the operator for a catalog
domain
- **THEN** that domain appears in the wizard's domain selection
#### Scenario: Denied or unavailable domain is hidden
- **WHEN** the portal verify endpoint denies a domain, or the check errors
- **THEN** that domain is not offered
#### Scenario: Fallback object allows capture
- **WHEN** the `domain:<code>` check denies but the `domain:idid` check
allows
- **THEN** the domain is offered
#### Scenario: Cached result within the TTL
- **WHEN** a session fetch occurs within 5 minutes of the last permission
check for the operator
- **THEN** the cached permitted-domain list is served without calling the
portal verify endpoint
#### Scenario: Re-check after the TTL
- **WHEN** a session fetch occurs after the 5-minute cache period has
elapsed
- **THEN** all catalog domains are re-checked and the list reflects current
permissions
#### Scenario: No permitted domains
- **WHEN** no catalog domain is permitted for the operator
- **THEN** the wizard explains they are not permitted to capture
fingerprints for any domain and offers no capture UI
### Requirement: Manual refresh of permitted domains
The wizard SHALL offer a refresh control for the permitted-domain list that
stays disabled until the 5-minute cache period since the last check has
elapsed. Using it SHALL call the app's force-refresh endpoint, update the
offered domains, and update the displayed last-checked time; a refresh
refused because the cache is still fresh SHALL leave the current list and
state unchanged.
#### Scenario: Refresh control locked while the cache is fresh
- **WHEN** fewer than 5 minutes have passed since the permitted-domain set
was last checked
- **THEN** the refresh control is disabled
#### Scenario: Refresh after the cache elapses
- **WHEN** the cache period has elapsed and the operator uses the refresh
control
- **THEN** the wizard calls the force-refresh endpoint and the offered
domains update to the re-checked result
#### Scenario: Refused refresh leaves state unchanged
- **WHEN** a refresh request is refused because the cache is still fresh
- **THEN** the wizard keeps the current domain list and selection
### Requirement: Enrollment captures via fpagent
The wizard SHALL capture fingerprints through fpagent at the URL provided by
the session endpoint, emitting `fprint-enroll` with the session's PocketBase
token, the selected domain code, the finger name, and the staff reference. It
SHALL display capture progress from `fprint-enroll-status` events, record the
returned fingerprint template, image, and enrollment id on
`fprint-enroll-done` with `ok: true`, and surface the failure message and
return to idle on `ok: false`. The wizard SHALL offer cancellation of an
in-flight capture (emitting `fprint-enroll-stop`), SHALL prevent starting a
capture while another is in flight (fpagent serves one enrollment at a time),
and SHALL display the agent version reported on connect.
#### Scenario: Successful capture
- **WHEN** the operator starts a capture for a finger and fpagent reports
`fprint-enroll-done` with `ok: true`
- **THEN** the wizard records the fingerprint, image, and enrollment id for
that finger and displays the enrollment id
#### Scenario: Failed capture
- **WHEN** fpagent reports `fprint-enroll-done` with `ok: false`
(authentication failed, authorization unavailable, not authorized, or
capture failed)
- **THEN** the wizard shows the failure message and returns to the idle
state without recording artifacts
#### Scenario: Cancel in-flight capture
- **WHEN** the operator cancels a capture in progress
- **THEN** the wizard emits the stop event and returns to the idle state
#### Scenario: No concurrent captures
- **WHEN** a capture is in flight
- **THEN** the capture controls for both fingers are disabled until it
completes or is cancelled
### Requirement: Two-finger enrollment flow
The wizard SHALL enroll a profile by: selecting a permitted domain, looking
up staff by reference (token-validated through the a2loft proxy), displaying
the staff details, capturing the right thumb and right index finger, and
saving both fingerprints through the a2loft proxy authenticated with the
session token. The wizard SHALL offer per-finger recapture until save, SHALL
require both fingers before save, and SHALL reset domain-independent state
(reference, staff details, captured artifacts) when starting a new
enrollment.
#### Scenario: Complete enrollment
- **WHEN** the operator looks up staff, captures both fingers, and saves
- **THEN** both fingerprints are saved through the a2loft proxy and the
wizard reports success
#### Scenario: Save requires both fingers
- **WHEN** the operator attempts to save with only one finger captured
- **THEN** the wizard rejects the save and explains both fingers are
required
#### Scenario: New enrollment resets state
- **WHEN** the operator starts a new enrollment after a save
- **THEN** the reference, staff details, and captured finger artifacts are
cleared while the permitted domain list persists
### Requirement: Scanner availability is shown and gates capture
The wizard SHALL consume fpagent's `fprint-device-status`
`{available, device, captureAgentId}` event and SHALL distinguish the agent
being offline (socket not connected), connected with no supported reader
attached (`available` false), and ready (`available` true). Until a status is
received, availability SHALL be treated as unknown. The wizard SHALL NOT start a
capture while no supported reader is attached, SHALL re-enable capture when a
reader becomes available, and SHALL clear availability when the agent
disconnects. Availability means "a supported reader is attached"; it does not
guarantee a capture will succeed.
#### Scenario: Reader attached on connect
- **WHEN** the page connects and fpagent reports `fprint-device-status` with
`available: true`
- **THEN** the wizard shows the agent ready and leaves capture enabled
#### Scenario: No reader attached on connect
- **WHEN** the page connects and fpagent reports `fprint-device-status` with
`available: false`
- **THEN** the wizard shows that the scanner is not connected, explains captures
are disabled, and disables the capture controls
#### Scenario: Unplug blocks capture
- **WHEN** fpagent reports `available: false` while the operator is capturing or
between captures
- **THEN** the wizard disables the capture controls and a capture attempt is
refused
#### Scenario: Replug re-enables capture
- **WHEN** fpagent reports `available: true` after the reader is reconnected
- **THEN** the wizard re-enables the capture controls
#### Scenario: Status absent or old agent
- **WHEN** the connected agent never reports `fprint-device-status`
- **THEN** availability stays unknown and the wizard does not block capture
#### Scenario: Disconnect clears availability
- **WHEN** the agent socket disconnects
- **THEN** the wizard shows the agent offline and does not present the reader as
available
### Requirement: Token authenticated on every a2loft call
The wizard SHALL authenticate each a2loft call — staff lookup and fingerprint
save — with the signed-in operator's PocketBase session token, sent by the
same-origin proxy as an `Authorization: Bearer` credential. The wizard SHALL
NOT collect an OTP.
#### Scenario: Lookup uses the session token
- **WHEN** the operator submits a reference
- **THEN** the wizard calls the staff-lookup proxy and a2loft authenticates the
session token
#### Scenario: Save uses the session token
- **WHEN** the operator saves the captured fingerprints
- **THEN** the wizard calls the fingerprint-save proxy and a2loft authenticates
the session token
#### Scenario: No OTP field
- **WHEN** the wizard page is served
- **THEN** it offers no OTP input for lookup or save