Merge branch 'docs/check-in-flow' into develop

This commit is contained in:
2026-04-28 11:12:26 +02:00
5 changed files with 157 additions and 16 deletions

View File

@@ -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.

View File

@@ -243,13 +243,58 @@ Status codes:
## Check-In ## 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 ```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: Request:
@@ -267,7 +312,8 @@ Response `200 OK`:
"reservation_id": 123, "reservation_id": 123,
"performance_id": 10, "performance_id": 10,
"party_size": 2, "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; - `200 OK`: reservation checked in;
- `400 Bad Request`: token is missing or malformed; - `400 Bad Request`: token is missing or malformed;
- `401 Unauthorized`: staff authentication is missing; - `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; - `404 Not Found`: token is unknown;
- `409 Conflict`: reservation is not confirmed or was already checked in; - `409 Conflict`: reservation is not confirmed or was already checked in;
- `410 Gone`: token is expired. - `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 ## Administration

View File

@@ -79,19 +79,27 @@ The QR code must not contain:
## 5. Entrance Check-In ## 5. Entrance Check-In
1. Staff opens the authenticated check-in interface. 1. Staff signs in with an account that has check-in permission.
2. Staff scans the visitor's QR code. 2. Staff opens a mobile-friendly web page for entrance check-in.
3. The frontend submits the scanned token to the backend. 3. Staff scans the visitor's QR code with the device camera or enters the QR token manually.
4. The backend validates: 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; - staff authentication and permission;
- token exists and has the `check_in` purpose; - token exists and has the `check_in` purpose;
- reservation exists; - reservation exists;
- reservation is confirmed; - reservation is confirmed;
- token is valid for the performance check-in window if such a window is configured; - token is valid for the performance check-in window if such a window is configured;
- reservation has not already been checked in. - reservation has not already been checked in.
5. The backend creates a `CheckIn` record. 7. The backend returns a preview with only the minimum information needed for admission, such as performance, party size, and check-in state.
6. The backend returns a successful check-in response. 8. Staff confirms check-in in the mobile web page.
7. Staff admits the visitor party. 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 ## 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. 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 Handling
Capacity is calculated as: Capacity is calculated as:

View File

@@ -157,9 +157,10 @@ Suggested fields:
- `id`: internal identifier; - `id`: internal identifier;
- `reservation`: required unique reference to `Reservation`; - `reservation`: required unique reference to `Reservation`;
- `checked_in_at`: timestamp of successful check-in; - `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`; - `source`: optional source such as `qr_scan` or `manual`;
- `created_at`: creation timestamp. - `created_at`: creation timestamp;
- `updated_at`: last update timestamp.
Relationships: Relationships:
@@ -168,8 +169,14 @@ Relationships:
Rules: 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; - only confirmed reservations can be checked in;
- a reservation cannot be checked in twice; - 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; - 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. - check-in must not expose unnecessary personal data to scanning clients.

View File

@@ -20,6 +20,7 @@ Controls:
- collect only data required to manage the reservation; - collect only data required to manage the reservation;
- do not expose reservation personal data through public APIs; - do not expose reservation personal data through public APIs;
- do not include personal data in QR codes; - 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 request bodies from booking and confirmation endpoints;
- avoid logging raw tokens; - avoid logging raw tokens;
- restrict admin access to staff users who need it. - 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. 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 ## Authentication and Authorization
Required controls: Required controls:
- Django admin requires authenticated staff accounts; - 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; - 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. - public APIs must not allow clients to set protected fields such as reservation status, token values, or check-in state.