geocrop-platform./apps/nextgen/docs/DIGITAL_CONSENT.md

96 lines
5.2 KiB
Markdown

# Digital Consent (Sprint 3)
This document describes the digital consent and student transfer approval implementation (Sprint 3).
## Architecture overview
- Server: Node 20 + Express, SQLite via better-sqlite3.
- Frontend: React + Vite, SignatureModal component captures drawings to a PNG data URL.
- USSD: state machine + ussd service. PIN verification is bcrypt-based and persisted on the `users` row (`ussd_pin`, `ussd_failed_attempts`, `ussd_locked_until`).
- Audit: AuditService writes to `audit_logs` table. Consent actions write audit entries.
## Database schema
- New table: `transfer_consents` (created by migration `server/src/database/migrations/knex/2026073000000000_consent_columns.js`). Key columns:
- id (PK integer)
- uid (TEXT UNIQUE)
- student_id (FK -> users.id)
- parent_id (FK -> users.id)
- requested_by (TEXT)
- from_school_id (TEXT)
- target_school (TEXT)
- transfer_request_id (self FK)
- consent_status (TEXT) — values: 'pending','approved','rejected','expired'
- consent_method (TEXT) — 'WEB_SIGNATURE' | 'USSD_PIN'
- signed_at (DATETIME)
- signature_hash (TEXT)
- expires_at (DATETIME)
- created_at, updated_at
Indexes: student_id, parent_id, consent_status, plus a partial index for pending rows.
Notes: field names were chosen to align with existing code patterns (`target_school` used for the destination school, `signed_at` used for approval timestamp).
## Web signature flow
1. Admin initiates a transfer via POST /api/transfers/initiate — this creates a `transfer_consents` pending row and sends instructions to the parent.
2. Parent (authenticated) opens `TransferConsents` page (GET /api/consents/pending) and opens the `SignatureModal` component.
3. SignatureModal captures drawing to a PNG data URL and sends it to POST /api/transfers/:id/consent.
4. Server builds a canonical payload and computes SHA-256 over the canonical string (see signature.service.canonicalize). The server stores only the hex hash in `signature_hash` on the consent row along with `signed_at`.
Security properties:
- The payload includes consent_id (uid), student_id, parent_id, both schools, and approved_at timestamp — so replaying an old PNG alone won't pass verification because the approved_at will differ.
- The signature hash is verified with constant-time comparison when needed (signature.service.verifyConsentHash).
- Parent/student relationship is validated against `parent_students` before approval.
- Atomic UPDATE ensures duplicate approvals are prevented (0 rows affected → conflict).
## USSD approval flow
1. Parent uses USSD and authenticates with student ID and 4-digit PIN.
2. In the Main Menu choose "Transfer Approval"; the USSD state machine picks the next pending consent for the student.
3. Parent enters PIN to approve. The PIN is verified using `ussd.service.verifyPin` (bcrypt). On success the server computes the same SHA-256 payload and marks the consent `approved` with `consent_method = 'USSD_PIN'`.
4. The USSD flow returns `END Transfer approved successfully.` on successful approval.
## API endpoints
- POST /api/transfers/initiate — admin-only; creates pending consent.
- POST /api/transfers/:id/consent — authenticated user (parent or admin override); approves with web signature image in `signature_data_url`.
- GET /api/consents/pending — parent-only; lists pending consents for the authenticated parent.
- POST /api/consents/:id/reject — parent or admin; rejects a pending consent.
- Additional routes: GET /api/transfers/:id/consent and DELETE /api/transfers/:id/consent (for compatibility).
## Signature hashing
- Canonical format: pipe-separated key=value pairs in this exact order:
`consent_id, student_id, parent_id, from_school_id, to_school_id, approved_at`.
- Hash algorithm: SHA-256 hex digest.
- Generation and verification functions live in `server/src/services/signature.service.js`.
## Audit logging
- All consent actions write audit entries using `AuditService` or the local `writeAudit` helper.
- Actions include: `consent_requested`, `consent_approved`, `consent_ussd_approved`, `consent_rejected`, `consent_expired_sweep`.
- Audit rows include user, entity_type, entity_id (consent uid), timestamp, and new_data metadata.
## Testing instructions
- Unit tests for signature hashing were added (Vitest) at `server/test/signature.spec.js`.
- Run the server tests from the `server/` folder:
```bash
cd server
npm install # if dependencies are not yet installed
npm run test
```
- Integration tests for the full consent workflow are recommended (DB-backed); these require a test DB and fixtures. Create tests that run `node src/database/init.js` against a temporary DB path or use in-memory SQLite.
## Open items / caveats
- A small USSD-to-service return-shape mismatch and an audit user reference bug were fixed in `server/src/services/consent.service.js` (USSD approval now returns `{ ok: true, consent }` and audit user uses `consent.parent_id`).
- Full integration tests covering the transfer lifecycle, parent linkage negative cases, and PIN lockout flows are not yet present — recommended next work.
## Contact
For changes to consent formats (canonicalization) or migration overwrite, coordinate with the ops team — existing signed consents rely on the exact canonicalization string.