Selfie attendance with geofencing and server-side time enforcement
Built a biometric attendance flow with geofenced locations, multi-layer timezone resolution, and server-side timestamp enforcement to prevent device-time fraud.
Impact
Device-time fraud prevention
Context
Institutions using Digiicampus needed a modern replacement for paper-based or biometric-terminal attendance. The product team wanted a mobile-first flow: open the app, take a selfie at your assigned location, done. Simple for users, which meant all the hard problems fell to the backend.
Problem
“Simple for users” is a tall order when the backend has to trust a device it can’t trust.
Three adversarial patterns were in scope:
- Location spoofing. A user checks in from home instead of the office.
- Time spoofing. A user manipulates device clock or the request body to back-date an attendance record.
- Identity spoofing. A user’s friend checks in for them.
The system also had to handle the non-adversarial edge cases that always show up in real deployments:
- Users in different timezones from the tenant’s home region.
- Tenants with multiple geofenced locations (e.g., a university with several campuses).
- Users whose device GPS is flaky indoors.
Approach
For location: server-side geofence validation. The client sends current coordinates; the server checks them against the assigned location’s polygon. The client is not trusted to say “I’m at the right place” — only to submit coordinates the server then validates.
For time: the server stamps the record with its own clock, not the client’s. The client sends a request; the server’s ingress timestamp is the authoritative attendance time. The client’s timestamp is recorded separately as metadata for audit, never used for business logic.
For timezone resolution: a layered fallback. Use the geofence’s timezone if the check-in is inside one. Otherwise, fall back to the tenant’s home timezone. Otherwise, use the request’s inferred timezone from its IP. This matters for “was this check-in within the valid window for today’s class?” — the answer depends on which day you’re in.
For identity: a selfie captured at check-in time, stored with the record. Automated comparison against the user’s profile photo is a future enhancement; for now, the selfie is a deterrent and an audit artifact. The presence of the photo alone changes behavior.
Implementation
The check-in request carries: geofence ID (which location), client coordinates, client timestamp, selfie image. The server:
- Validates the geofence ID exists and the user is assigned to it.
- Validates the client coordinates are inside the geofence polygon.
- Resolves the correct timezone using the layered fallback.
- Stamps the attendance record with its own ingress timestamp, localized via the resolved timezone.
- Stores the selfie in S3 with a record-scoped key.
- Writes the attendance row with both server and client timestamps.
The image upload is the slow step. I kept the database write synchronous and the S3 upload asynchronous — the user sees success as soon as the row is committed; the image is backfilled by a worker. If the upload fails, the record is flagged for review, not lost.
Impact
- Device clock manipulation is no longer a valid attack vector — the server’s clock is authoritative.
- Location spoofing requires defeating OS-level GPS, which is a much higher bar than editing a request body.
- Every record has a selfie attached, which has proven to be a strong social deterrent against proxy check-ins even without automated face matching.
- Timezone handling works correctly across campuses and for users traveling outside their tenant’s region.
What I’d do differently: I’d add automated face-matching against the profile photo from day one, gated behind a feature flag per tenant. The hooks are there, but the comparison itself is still manual review.