UC-3 Student checks in at an event via QR
The core loop. Everything else exists to make this one tap trustworthy.
| Primary actor | Student |
| Supporting actors | Event Attendant (displays the code) |
| Priority | Must |
| Satisfies | FR-D1, FR-D2, FR-D4, FR-D5, FR-D7, FR-E1 |
| Precondition | The student is signed in, a current student, and enrolled; the task's event is running. |
| Trigger | The student taps "Scan" and captures the event QR, or types the event's 7-character backup code. |
Main success scenario
- The attendant displays the event QR (a signed token, re-minted every 30 seconds) on a projector via the display link or the live-event screen.
- The student scans it in the app.
- The app posts the token with the session cookie.
- The system verifies the signature, expiry and challenge binding, then records a check-in with
method = event_qr, one per student per week. - The week flips to complete, the countdown updates, and a post-check-in tip is shown (UC-6).
Extensions
- 4a. Duplicate scan. 409, "already checked in this week". Nothing is double-counted.
- 4b. Expired code. 400 asking for a rescan of the current code. A slow scan is not a forgery.
- 4c. Invalid, tampered, or another challenge's code. 400, invalid token.
- 4d. Not a current student. 403 before any check-in logic runs.
- No camera or bad lighting. The attendant reads out the backup code; it is accepted through the same path.
- Legacy self-service check-in. Retired. A student's own check-in requires the live event code; staff can verify a student directly (UC-4) or override with an audit row (UC-11).
Acceptance criteria (Gherkin)
| Feature file | Test module |
|---|---|
qr_event_binding.feature |
test_qr_event_binding_bdd.py |
rotating_qr_token.feature |
test_rotating_qr_token_bdd.py |
scanned_qr_only_checkin.feature and frontend/scanned_qr_only_checkin.feature |
test_scanned_qr_only_checkin_bdd.py |
event_backup_code.feature |
test_event_backup_code_bdd.py |
week_sheet_checkin_cta.feature |
test_week_sheet_checkin_cta_bdd.py |
Plain pytest: test_qr_checkin.py, test_rotating_qr_token.py, test_event_backup_code.py, test_task_qr_ttl.py. Frontend: QrScanner.test.tsx.
Views
- Process: UC-3 Event QR check-in
- Logical:
CheckInand its one-per-week constraint in the domain model;services/qrandservices/checkinsin system components
Code: backend/app/services/qr.py, backend/app/services/checkins.py, backend/app/routers/passport.py, frontend/src/components/Passport/QrScanner.tsx.
Operational note: rotating WP_QR_SECRET invalidates every live code; the runbook has the procedure.