From 97427a0864ba236518aa122651d77d8cc420bb1c Mon Sep 17 00:00:00 2001 From: bisco Date: Tue, 28 Apr 2026 10:59:01 +0200 Subject: [PATCH] docs: define staff check-in flow --- ...06-staff-check-in-with-token-validation.md | 29 +++++++ docs/api-contract.md | 78 +++++++++++++++++-- docs/booking-flow.md | 37 +++++++-- docs/domain-model.md | 11 ++- docs/security-notes.md | 18 ++++- 5 files changed, 157 insertions(+), 16 deletions(-) create mode 100644 docs/adr/0006-staff-check-in-with-token-validation.md diff --git a/docs/adr/0006-staff-check-in-with-token-validation.md b/docs/adr/0006-staff-check-in-with-token-validation.md new file mode 100644 index 0000000..ef63222 --- /dev/null +++ b/docs/adr/0006-staff-check-in-with-token-validation.md @@ -0,0 +1,29 @@ +# ADR-0006: Staff Check-In with Token Validation + +Date: 2026-04-28 + +## Status + +Accepted + +## Context + +Confirmed reservations need a simple entrance check-in process. Staff should be able to use a mobile-friendly web page to scan a visitor's QR code or enter the token manually. + +QR codes may be shown on smartphones, printed, forwarded, or photographed, so they must not expose personal data. Check-in must be restricted to authenticated staff or admin users and must prevent duplicate entrance. + +## Decision + +Use staff-only Django REST Framework endpoints for QR verification preview and check-in confirmation. + +The QR code will contain only an opaque verification token or URL. The backend will validate the token server-side, require a confirmed reservation, reject duplicate check-ins, and create or update a `CheckIn` record with timestamp and staff user on successful confirmation. + +No Celery, Redis, or separate check-in service is required. + +## Consequences + +- Staff can use a simple mobile web page for scanning or manual token entry. +- QR codes remain privacy-preserving because they do not contain personal data. +- Check-in decisions stay centralized in the Django backend and PostgreSQL state. +- The entrance workflow depends on backend availability at the venue. +- Duplicate check-ins can be blocked with application checks and database constraints. diff --git a/docs/api-contract.md b/docs/api-contract.md index 2db8e16..8f19501 100644 --- a/docs/api-contract.md +++ b/docs/api-contract.md @@ -243,13 +243,58 @@ Status codes: ## Check-In -### Verify QR Code +Check-in endpoints are for authenticated staff or admin users. Staff use a mobile-friendly Angular page to scan the QR code with a device camera or enter the token manually. + +The QR code must contain only an opaque verification token or a verification URL containing that token. The backend resolves and validates the token server-side. + +### QR Verification Preview ```http -POST /api/check-ins/verify/ +POST /api/check-ins/preview/ ``` -Validates a QR token and records check-in. This endpoint is for authenticated staff or an authenticated scanning interface. +Validates a QR token and returns a preview before staff confirms entrance. This endpoint must not create a successful check-in. + +Request: + +```json +{ + "token": "opaque-check-in-token" +} +``` + +Response `200 OK`: + +```json +{ + "status": "valid", + "reservation_id": 123, + "performance_id": 10, + "show_title": "The Open Stage", + "starts_at": "2026-05-15T20:30:00+02:00", + "party_size": 2 +} +``` + +Status codes: + +- `200 OK`: token is valid and reservation can be checked in; +- `400 Bad Request`: token is missing or malformed; +- `401 Unauthorized`: staff authentication is missing; +- `403 Forbidden`: authenticated user cannot preview check-in; +- `404 Not Found`: token is unknown; +- `409 Conflict`: reservation is not confirmed or was already checked in; +- `410 Gone`: token is expired. + +The preview response should include only the minimum information staff need to validate the party. It must not expose unnecessary reservation personal data. + +### Check-In Confirmation + +```http +POST /api/check-ins/confirm/ +``` + +Validates the token again and records successful entrance. Request: @@ -267,7 +312,8 @@ Response `200 OK`: "reservation_id": 123, "performance_id": 10, "party_size": 2, - "checked_in_at": "2026-05-15T19:55:00+02:00" + "checked_in_at": "2026-05-15T19:55:00+02:00", + "checked_in_by": 7 } ``` @@ -276,12 +322,32 @@ Status codes: - `200 OK`: reservation checked in; - `400 Bad Request`: token is missing or malformed; - `401 Unauthorized`: staff authentication is missing; -- `403 Forbidden`: authenticated user cannot perform check-in; +- `403 Forbidden`: authenticated user cannot confirm check-in; - `404 Not Found`: token is unknown; - `409 Conflict`: reservation is not confirmed or was already checked in; - `410 Gone`: token is expired. -The response should include only the minimum information staff need to admit the party. +Successful confirmation creates a `CheckIn` record, or updates an existing incomplete check-in record for the reservation. A reservation cannot have two successful check-ins. + +Error responses should use clear machine-readable states so the staff interface can show simple messages. + +Example `409 Conflict` for duplicate check-in: + +```json +{ + "status": "already_checked_in", + "detail": "This reservation has already been checked in." +} +``` + +Example `409 Conflict` for unconfirmed reservation: + +```json +{ + "status": "reservation_not_confirmed", + "detail": "This reservation is not confirmed." +} +``` ## Administration diff --git a/docs/booking-flow.md b/docs/booking-flow.md index a53a271..4278f79 100644 --- a/docs/booking-flow.md +++ b/docs/booking-flow.md @@ -79,19 +79,27 @@ The QR code must not contain: ## 5. Entrance Check-In -1. Staff opens the authenticated check-in interface. -2. Staff scans the visitor's QR code. -3. The frontend submits the scanned token to the backend. -4. The backend validates: +1. Staff signs in with an account that has check-in permission. +2. Staff opens a mobile-friendly web page for entrance check-in. +3. Staff scans the visitor's QR code with the device camera or enters the QR token manually. +4. The QR code provides only an opaque check-in token or verification URL. +5. The frontend submits the token to the backend for a verification preview. +6. The backend validates: - staff authentication and permission; - token exists and has the `check_in` purpose; - reservation exists; - reservation is confirmed; - token is valid for the performance check-in window if such a window is configured; - reservation has not already been checked in. -5. The backend creates a `CheckIn` record. -6. The backend returns a successful check-in response. -7. Staff admits the visitor party. +7. The backend returns a preview with only the minimum information needed for admission, such as performance, party size, and check-in state. +8. Staff confirms check-in in the mobile web page. +9. The backend validates the token again server-side. +10. The backend creates a `CheckIn` record, or updates an existing incomplete check-in record for the same reservation. +11. The backend stores the check-in timestamp and authenticated staff user. +12. The backend returns a successful check-in response. +13. Staff admits the visitor party. + +The token remains opaque throughout the flow. The QR code must not expose visitor name, email address, phone number, notes, or other personal data. ## Duplicate Check-In @@ -103,6 +111,21 @@ If the same QR code is scanned again: The response should be clear enough for staff to understand that the reservation was already used. +## Check-In Failure States + +Failed validation must return clear error states without creating a successful check-in. + +Expected check-in failures include: + +- missing or malformed token; +- unknown token; +- expired token; +- staff user is not authenticated; +- staff user does not have check-in permission; +- reservation is not confirmed; +- reservation was already checked in; +- token is not valid for the selected performance or check-in window. + ## Capacity Handling Capacity is calculated as: diff --git a/docs/domain-model.md b/docs/domain-model.md index 88021e6..fac8add 100644 --- a/docs/domain-model.md +++ b/docs/domain-model.md @@ -157,9 +157,10 @@ Suggested fields: - `id`: internal identifier; - `reservation`: required unique reference to `Reservation`; - `checked_in_at`: timestamp of successful check-in; -- `checked_in_by`: optional authenticated staff user reference; +- `checked_in_by`: required authenticated staff user reference for successful check-in; - `source`: optional source such as `qr_scan` or `manual`; -- `created_at`: creation timestamp. +- `created_at`: creation timestamp; +- `updated_at`: last update timestamp. Relationships: @@ -168,8 +169,14 @@ Relationships: Rules: +- check-in is performed only by authenticated staff or admin users; +- staff use a mobile-friendly web page to scan the QR code or enter the token manually; +- the QR code contains only an opaque verification token or URL; +- the backend validates the token server-side; - only confirmed reservations can be checked in; - a reservation cannot be checked in twice; +- successful check-in creates a `CheckIn` record, or updates an existing incomplete check-in record for the same reservation; +- successful check-in records must include `checked_in_at` and `checked_in_by`; - failed check-in attempts should return a clear status without changing successful check-in state; - check-in must not expose unnecessary personal data to scanning clients. diff --git a/docs/security-notes.md b/docs/security-notes.md index ce7ea88..1e0f761 100644 --- a/docs/security-notes.md +++ b/docs/security-notes.md @@ -20,6 +20,7 @@ Controls: - collect only data required to manage the reservation; - do not expose reservation personal data through public APIs; - do not include personal data in QR codes; +- keep check-in preview and confirmation responses limited to operational admission data; - avoid logging request bodies from booking and confirmation endpoints; - avoid logging raw tokens; - restrict admin access to staff users who need it. @@ -56,12 +57,27 @@ QR codes must not contain: The check-in endpoint resolves the token server-side and returns only the minimum information staff need. +## Check-In Security + +Check-in is performed by authenticated staff or admin users through a mobile-friendly web page. + +Controls: + +- require staff authentication for QR verification preview and check-in confirmation; +- allow QR scanning and manual token entry in the staff interface; +- validate every token server-side; +- require the reservation to be confirmed before check-in; +- reject duplicate check-in attempts; +- store successful check-in timestamp and staff user; +- return clear validation states without exposing unnecessary personal data; +- do not log raw QR tokens. + ## Authentication and Authorization Required controls: - Django admin requires authenticated staff accounts; -- check-in verification requires authenticated staff or an authenticated scanning client; +- check-in verification preview and confirmation require authenticated staff or admin users; - staff permissions should separate content management from operational check-in when practical; - public APIs must not allow clients to set protected fields such as reservation status, token values, or check-in state.