# Facial Attendance Implementation ## 1. Purpose This document describes the facial-attendance feature implemented in the HRIS: what was added, how the system works, how to install and operate it, its privacy and security controls, how to test it, and its current limitations. The feature supports two kiosk modes: 1. **1:1 verification** — the employee selects their enrolled record from a branch-scoped dropdown, then the captured face is compared only with that employee's active enrollment. 2. **1:N identification** — the employee does not enter an ID. The captured face is compared with active enrollments assigned to the authorized kiosk's branch. The 1:N mode is separately controlled and is disabled by default because it has greater privacy and false-identification risk than 1:1 verification. This implementation provides technical compliance controls. It does not, by itself, establish that a deployment is lawful. The organization remains responsible for DPO/legal review, its Privacy Impact Assessment (PIA), lawful basis, employee notice, proportionality assessment, retention decisions, and data-subject processes. ## 2. What was implemented ### Administration - A Tailwind-based Facial Attendance administration page at `/facial-attendance-admin`. - Brand-scoped privacy and verification settings. - A DPO/legal approval attestation required when enabling the feature. - A separate opt-in switch for face-only 1:N identification. - Supervised employee enrollment and template replacement. - Immediate template revocation with cryptographic erasure. - Browser/device authorization for one selected branch. - Device expiration, audit history, and remote revocation. - Configurable template retention, event retention, match, ambiguity, liveness, and anti-spoof thresholds. ### Login-page kiosk - An attendance panel is shown only when the browser has a valid kiosk authorization cookie and facial attendance is enabled. - In 1:1 mode, the employee selects from active facial enrollments in the authorized kiosk's branch before scanning. - In 1:N mode, an operator opens the kiosk session once. Employees then scan without an ID or repeated button presses. - The automatic session scans, displays a successful result for 2.5 seconds, and prepares for the next employee. Failed scans retry after five seconds. - Closing the facial-attendance window stops the camera and pending retries. - The kiosk displays the approved privacy notice, DPO contact, and non-biometric fallback. - A live-person eye-close check, anti-spoof score, and liveness score are required. - The server records the time using the `Asia/Manila` timezone. ### Performance work - Facial models preload in the background after an authorized kiosk loads. - Warm-up is limited to face models rather than the full Human model suite. - The unnecessary iris model is disabled; blink/eye-close detection uses the face mesh. - Camera processing uses a 480 x 480 ideal resolution. - Capture is split into a lightweight eye-close stage and one full descriptor, liveness, and anti-spoof stage instead of running every model on every frame. - Full-stage models use the library's supported asynchronous execution path. - Per-person descriptor and assurance results are not reused across employees. - The UI asks the employee to close both eyes for approximately one second, which is more reliable than a quick blink on slow hardware. - Failed model loading can be retried instead of leaving a permanently rejected in-memory loading promise. - Successful results display separate local-scan and server-match durations for performance diagnosis. ## 3. Architecture ```mermaid flowchart LR A[Admin UI] -->|policy, device, enrollment| B[PHP admin endpoints] K[Login kiosk] -->|challenge and face embedding| P[PHP kiosk endpoints] B --> D[(MariaDB)] P --> D K --> H[Human models in browser] H -->|embedding and assurance scores only| P P --> T[Existing attendance table and schedule calculations] ``` The browser performs camera-frame processing. Raw frames are not sent to PHP and are not stored. PHP receives a numerical face embedding, liveness score, anti-spoof score, and eye-close result. Enrolled embeddings are encrypted before being written to MariaDB. ## 4. Main files ### Frontend | File | Responsibility | | --- | --- | | `frontend/src/facial-attendance/FacialAttendanceAdmin.jsx` | Policy settings, device authorization, enrollment, revocation, and audit UI | | `frontend/src/facial-attendance/FacialKioskPanel.jsx` | Login-page kiosk and 1:1/1:N workflow | | `frontend/src/facial-attendance/FaceCapture.jsx` | Camera lifecycle, progress UI, and capture control | | `frontend/src/facial-attendance/facialEngine.js` | Human model configuration, preload, liveness gesture, and embedding generation | | `frontend/public/face-models/` | Locally served face detection, mesh, descriptor, liveness, and anti-spoof models | | `frontend/src/App.jsx` | Admin route registration | | `frontend/src/authentication/login.jsx` | Kiosk panel placement on the login page | ### Backend | File | Responsibility | | --- | --- | | `backend/facial_attendance/_common.php` | Shared validation, encryption, similarity, kiosk-origin, cookie, scope, and audit helpers | | `backend/facial_attendance/device_status.php` | Reports whether the current browser is an active kiosk | | `backend/facial_attendance/challenge.php` | Creates one-use, two-minute verification challenges | | `backend/facial_attendance/punch.php` | Validates assurance signals, performs matching, and records an event | | `backend/facial_attendance/attendance_apply.php` | Writes the next punch slot and reuses existing schedule/penalty calculations | | `backend/facial_attendance/admin/` | Authenticated administration endpoints | | `backend/scripts/install_facial_attendance.php` | Idempotent schema installer | | `backend/scripts/purge_facial_attendance_data.php` | Retention and expired-data cleanup job | | `backend/server/privacy_config.example.php` | Encryption-key and trusted-origin configuration example | ## 5. Database design The migrations are: - `backend/migrations/2026_08_03_facial_attendance.sql` - `backend/migrations/2026_08_04_facial_identification.sql` | Table | Purpose | | --- | --- | | `facial_attendance_settings` | Brand-level enablement, approved notice references, retention, and thresholds | | `facial_attendance_devices` | Branch-bound kiosk authorization, token hash, status, and expiry | | `employee_face_templates` | Encrypted biometric templates and enrollment/retention metadata | | `facial_attendance_challenges` | Short-lived, one-use verification challenges | | `facial_attendance_events` | Accepted/rejected attempt metadata without raw images or embeddings | | `facial_attendance_audit` | Administrative actions such as settings updates, enrollment, and revocation | Important defaults: - Facial attendance: disabled. - 1:N identification: disabled. - Similarity threshold: `0.55`. - Identification ambiguity margin: `0.08`. - Liveness threshold: `0.60`. - Anti-spoof threshold: `0.60`. - Template and event retention: 365 days. Thresholds must be validated using the organization's actual cameras, environment, and representative employee population. Do not lower them merely to eliminate rejection errors without measuring false-accept risk. ## 6. Enrollment flow 1. An authorized admin selects an active employee in the current brand. 2. The privacy notice and fallback are explained to the employee. 3. The admin records the employee acknowledgment. 4. The browser captures a live face and generates a numerical embedding locally. 5. PHP verifies the liveness and anti-spoof thresholds. 6. PHP encrypts the embedding with AES-256-GCM. 7. Any previous active enrollment for that employee is revoked and its encrypted payload is erased. 8. The new template is saved with the notice version, lawful-basis reference, acknowledgment time, enrolling admin, branch, and expiry date. 9. The enrollment is added to the audit log. No raw enrollment photograph is stored by this implementation. ## 7. Device authorization An admin authorizes the current browser from the Facial Attendance page and assigns it to exactly one active branch. - The server creates a random 32-byte kiosk token. - Only its SHA-256 hash is stored in the database. - The raw value is placed in an `HttpOnly` cookie. - Same-origin cookies use `SameSite=Strict`. - Explicitly approved cross-origin HTTPS deployments use `Secure; SameSite=None`. - Authorization expires after the selected period, from 1 to 365 days. - Revocation immediately changes the database state to `revoked`. The cookie authorizes attendance-kiosk operations only; it is not an admin login. ## 8. Punch flows ### 1:1 employee-ID verification ```mermaid sequenceDiagram participant E as Employee participant K as Kiosk browser participant S as PHP server participant DB as MariaDB E->>K: Select enrolled employee from branch dropdown K->>S: Request challenge S->>DB: Confirm active branch-scoped enrollment S-->>K: One-use challenge E->>K: Complete live face scan K->>S: Embedding + assurance + challenge S->>DB: Decrypt that employee's template S->>S: Perform 1:1 comparison S->>DB: Record next attendance slot and event S-->>K: Attendance result ``` ### 1:N face-only identification 1. The facial-attendance window is opened once; no employee ID is requested. 2. The server creates a device-bound challenge with no employee ID. 3. The browser submits the embedding and assurance values. 4. The server loads active, unexpired templates only for the kiosk's company, brand, and assigned branch. 5. Candidate embeddings are decrypted in server memory and compared. 6. The best score must meet the similarity threshold. 7. The best score must lead the second-best score by at least the configured ambiguity margin. 8. An uncertain result is rejected without disclosing candidate identities. 9. A successful result supplies the matched employee to the normal attendance write flow. The branch search is capped at 500 active templates. This prevents an unbounded synchronous comparison workload and forces a different architecture before larger deployments. ## 9. Attendance behavior Accepted punches update the existing `attendance` record in this order: 1. Morning time in 2. Morning time out 3. Afternoon time in 4. Afternoon time out The implementation reuses the HRIS work-schedule and late/undertime calculation helpers. After all four slots are complete, additional punches are rejected. Accepted punches from the same employee and kiosk are also rejected as duplicates for 60 seconds. ## 10. Security and privacy controls - Admin endpoints require an authenticated admin role and selected brand scope. - Kiosk requests require a valid, active, unexpired device cookie. - Kiosk access is limited to the device's company, brand, and branch. - Cross-origin kiosk requests are denied unless the exact origin is configured. - Public kiosk requests require the expected kiosk request header. - Challenges expire after two minutes, can be used only once, and store only a hash of the secret challenge token. - Challenge creation is limited to 10 attempts per device per minute. - Embeddings are validated for type, length, finite values, and safe bounds. - Templates are encrypted using AES-256-GCM with a dedicated key. - Raw camera frames are not uploaded or stored. - Rejected 1:N scans do not reveal the nearest employee. - Server timestamps are authoritative. - Administrative and verification events are recorded for review. - A non-biometric attendance fallback remains visible to employees. ## 11. Installation and configuration ### Prerequisite check and migration From the repository root: ```powershell php backend/scripts/check_facial_attendance_prerequisites.php php backend/scripts/install_facial_attendance.php ``` The installer is idempotent and applies both facial-attendance migrations. ### Encryption configuration Generate a dedicated random secret: ```powershell php -r "echo base64_encode(random_bytes(32)), PHP_EOL;" ``` Copy `backend/server/privacy_config.example.php` to the ignored local file `backend/server/privacy_config.local.php`, then replace the example key. Do not commit the real key and do not reuse the application's authentication secret. For separate production frontend/backend origins, add only the exact trusted HTTPS frontend origin to `facial_attendance_allowed_origins`. ### Development Run the frontend from `frontend`: ```powershell npm run dev ``` The Vite `/api` proxy forwards requests to the local Apache backend and forwards the original host information. The development service worker is disabled and old registrations are cleared to prevent stale asset errors. ### Production requirements - Serve the camera page over HTTPS. - Keep the encryption key outside version control. - Configure the exact frontend origin if frontend and PHP use different hosts. - Restrict filesystem and database access to the application account. - Run the retention task daily. - Back up required audit/attendance data according to approved policy, but do not extend biometric retention merely because general backups are longer. ## 12. Administration procedure 1. Select the intended brand. 2. Open **Settings -> Facial Attendance**. 3. Enter the approved notice version and text, lawful-basis reference, PIA reference, DPO contact, fallback, and retention periods. 4. Configure thresholds based on a documented pilot. 5. Enable facial attendance, confirm approval, and save. 6. If approved for 1:N, separately enable **Face-only kiosk identification**, confirm that approval explicitly covers it, and save again. 7. Enroll authorized employees under supervision. 8. On the physical kiosk browser, authorize the device for its actual branch. 9. Log out. The facial-attendance panel will be available on the login page. The policy validator rejects obvious placeholder content. The privacy notice must contain at least 160 characters, fallback instructions at least 40 characters, retention must be 1–3650 days, and the encryption key must be ready before enablement. ## 13. Employee kiosk procedure 1. Open **Facial attendance** on the login page. 2. Read or expand the displayed privacy notice when needed. 3. Stand in front of the camera; the active kiosk session starts the scan. 4. Face the light and keep only one person in view. 5. Move close enough for the face to occupy a useful part of the camera square. 6. When prompted, close both eyes for approximately one second, then open them. 7. Wait for the server-confirmed employee name and punch slot. 8. Use the displayed fallback if scanning cannot be completed. The camera operates only while the clearly displayed automatic kiosk session is open. It stops during result confirmation and when the window is closed. A successful result remains visible for 2.5 seconds before the next scan starts; a failed server verification retries after five seconds. ## 14. Retention and revocation Schedule this command daily: ```powershell php backend/scripts/purge_facial_attendance_data.php ``` It performs the following actions in one transaction: - Cryptographically erases expired template ciphertext, IVs, and tags. - Deletes challenges that have been expired for more than one day. - Deletes verification events according to each brand's event-retention setting. Manual revocation erases the active template payload immediately while retaining minimum metadata needed to demonstrate the revocation and notice history. ## 15. Troubleshooting ### The employee dropdown is shown instead of face-only scanning The 1:N setting is off. The dropdown intentionally lists only active, facially enrolled employees in the authorized kiosk's branch. In the current brand's Facial Attendance settings, enable **Face-only kiosk identification (1:N)**, confirm approval, save, log out, and hard-refresh the login page. ### Facial attendance is not installed Run: ```powershell php backend/scripts/install_facial_attendance.php ``` ### Facial attendance is not enabled Complete all policy fields, configure the encryption key, enable the main feature, confirm DPO/legal approval, and save before authorizing a device. ### A valid device name and branch are required Enter a device name and select an active branch belonging to the currently selected brand. If the branch list is empty, verify the branch's company, brand, and active status. ### Cross-origin kiosk requests are not allowed Prefer the Vite `/api` proxy during local development. For an intentional separate-origin production deployment, use HTTPS and add the exact frontend origin to the ignored local privacy configuration. Do not use a wildcard. ### Verification challenge could not be created Check the PHP error log for the underlying database error. Confirm that both migrations ran, the kiosk cookie maps to an active device, and the employee or branch has an active unexpired enrollment. ### Model loading is slow The first browser visit downloads and initializes approximately 10 MB of facial models. Keep the login page open briefly before the first scan. Later scans in the same session use the preloaded engine. Production should serve static model files with appropriate caching over HTTPS. ### Eye-close check or face verification times out - Face a steady light; avoid strong backlighting. - Clean the camera lens. - Keep one face visible. - Move closer to the camera. - Remove reflective glare where practical. - Close both eyes for a full second instead of making a quick blink. - Use the fallback rather than repeatedly attempting a scan. The timeout message distinguishes an undetected eye-close gesture from low face-quality/liveness confidence. ### Vite reports a missing `vite.svg` Hard-refresh the page and clear any old service worker/site data once. The development build no longer registers the PWA service worker, preventing old cached manifests from requesting deleted Vite starter assets. ## 16. Verification performed during implementation The implementation was checked using: - Idempotent migration execution against the local MariaDB database. - Schema checks for the identification settings and nullable no-ID challenge. - PHP syntax validation for the facial-attendance endpoints. - Targeted ESLint checks for the facial-attendance React modules. - A complete Vite production build. - SQL collation fixes for employee/template joins. - Manual configuration checks for policy enablement, enrollment, and device state. A production pilot must additionally measure false accepts, false rejects, demographic performance, camera/environment differences, peak punch latency, fallback use, administrator access, revocation, retention, and incident handling. ## 17. Known limitations and future improvements - Browser liveness and anti-spoof models are MVP controls, not certified presentation-attack detection. - 1:N identification performs a linear server-side comparison and is limited to 500 active branch templates. - First-time model loading depends on kiosk hardware and network/static-file speed. - Matching thresholds are configurable but require a documented pilot; the application cannot choose a legally or statistically correct threshold for every workforce. - Hands-free scanning runs only inside the intentionally opened kiosk session; it is not a hidden background camera and does not survive closing the window. - The current attendance rule fills four slots sequentially. Organizations with different shift semantics should revise and test the punch-slot policy. - High-risk or large deployments should assess dedicated on-premises biometric matching and vetted liveness services, including processor, security, and cross-border data-transfer obligations. ## 18. Recommended acceptance checklist - [ ] DPO/legal approval and PIA are documented. - [ ] The notice explicitly describes the chosen 1:1 or 1:N operation. - [ ] A genuinely usable non-biometric fallback is available. - [ ] The dedicated encryption key is configured and recoverably managed. - [ ] HTTPS and exact allowed origins are configured. - [ ] Each kiosk is assigned to the correct branch and has an expiry date. - [ ] Only intended employees are enrolled and acknowledgments are recorded. - [ ] False accepts/rejects are measured with representative participants. - [ ] Four punch slots and schedule calculations are verified. - [ ] Duplicate, ambiguous, spoof, expired challenge, revoked device, and revoked-template cases are tested. - [ ] The retention job is scheduled and monitored. - [ ] DPO request, incident, and revocation procedures are tested. - [ ] Kiosk staff know when to direct employees to the fallback.