System Design Interview
Design a Digital Worker Identity & Compliance Platform
Issue, verify, and revoke digital work IDs across thousands of employers, sites, and workers — in real time and offline
The Interview Question
"Design a platform that issues digital work identity cards to employees across industries with mandatory compliance requirements — construction, cleaning, and transport. Employers register their workers, workers carry a digital ID on their phone, and site inspectors verify credentials in real time by scanning a QR code. The system must work even when inspectors have no internet connection, and credentials must be instantly revocable when an employment relationship ends."
This question tests credential system design, public key infrastructure (PKI), offline-first verification, revocation propagation, and integration with government registries. It's a real-world problem at the intersection of identity, labour law, and mobile-first UX.
Step 1: Requirements
Functional
- Employers register workers and their employment details
- Platform issues a digital work ID (QR code + card) to each registered worker
- Inspectors scan QR codes to verify: is this person legally employed by this company?
- IDs must be instantly revocable when employment ends
- Platform integrates with government business registry to validate employer legitimacy
- Workers can carry their ID in a mobile app (no paper required)
- Audit log: every verification recorded (who scanned, when, where)
Non-functional
- 500,000 active workers across 80,000 employers
- Verification must work offline (construction sites often have poor connectivity)
- Revocation must propagate within 5 minutes to all online verifiers
- ID issuance SLA: card available within 2 minutes of employer registration
- GDPR compliant: minimum personal data, right to erasure
- High availability for the verification endpoint: 99.9% uptime
Step 2: The Core Design Problem — Offline Verification
The most interesting challenge: an inspector on a remote construction site with no internet scans a worker's QR code. The platform server is unreachable. The QR code must still be verifiable.
Naive approach (fails offline):
QR code contains: worker_id = "wkr_abc123"
Inspector scans → app calls API: GET /verify/wkr_abc123
→ API checks database
→ returns { valid: true, employer: "Acme AS" }
If API is unreachable → cannot verify → inspector blockedThe solution: cryptographically self-contained credentials
The QR code doesn't just contain an ID — it contains a signed payload that can be verified without calling the server. This is exactly how passports, JWT tokens, and vaccination certificates work.
QR code payload (signed JWT / Verifiable Credential):
{
"sub": "wkr_abc123",
"name": "Lars Hansen",
"employer": "Acme Bygg AS",
"org_number": "987654321",
"industry": "construction",
"issued_at": "2026-03-01T08:00:00Z",
"expires_at": "2026-09-01T08:00:00Z",
"card_version": 42,
"sig": ""
}
Inspector's app verifies:
1. Signature valid? (using platform public key, stored in app at install time)
2. Not expired?
3. card_version ≥ minimum_valid_version for this worker?
(catches revoked credentials — see Step 5) The signature proves the platform issued this credential. No server call needed. Offline verification is instant.
Step 3: Credential Issuance Flow
Employer onboards:
1. Employer registers on platform
→ Platform calls: GET https://data.brreg.no/api/enheter/{org_number}
(Brønnøysund Register — Norwegian business registry API)
→ Validates: company is active, industry matches required registration
→ Creates employer record, issues employer API key
2. Employer registers a worker:
POST /api/workers
Body: { name, national_id_hash, photo_url, employment_start }
Platform:
a. Validates national_id_hash against ID verification service (optional)
b. Creates worker record in PostgreSQL
c. Signs credential payload with platform private key (ECDSA P-256)
d. Encodes as QR code + stores credential in blob storage
e. Sends notification to worker: "Your work ID is ready"
f. SLA: complete within 2 minutes
Worker receives credential:
→ Mobile app downloads signed credential payload
→ Stores locally (works offline from this point)
→ Displays QR code when neededWhy ECDSA P-256 over RSA? ECDSA signatures are 64 bytes vs RSA's 256+ bytes. Smaller QR codes are more reliably scannable. P-256 provides 128-bit security — adequate for work IDs with 6-month validity windows.
Step 4: Architecture
┌────────────────────────────────────────────────────────────────────┐
│ Employer Portal (web) Worker App (mobile) Inspector App │
└──────────┬─────────────────────────┬──────────────────┬───────────┘
│ │ │
┌──────────▼─────────────────────────▼──────────────────▼───────────┐
│ API Gateway │
│ (Auth, rate limiting, TLS termination) │
└────┬───────────────┬───────────────────────────┬───────────────────┘
│ │ │
┌────▼──────┐ ┌──────▼────────┐ ┌─────────────▼──────────────────┐
│ Employer │ │ Worker / │ │ Verify Service │
│ Service │ │ Credential │ │ (online path: checks revocation│
│ │ │ Service │ │ + logs audit event) │
└────┬──────┘ └──────┬────────┘ └─────────────┬──────────────────┘
│ │ │
└────────────────┼───────────────────────────┘
│
┌─────────────────────▼──────────────────────────────────────────────┐
│ PostgreSQL Redis Blob Storage │
│ - employers - revocation cache - QR images │
│ - workers - min_valid_version - signed payloads │
│ - audit_log per worker - employer logos │
│ - revocations - online verif.cache │
└─────────────────────┬──────────────────────────────────────────────┘
│
┌─────────────▼─────────────┐
│ External Integrations │
│ - Brønnøysund Registry │
│ - National ID Lookup │
│ - BankID / eID │
└───────────────────────────┘Step 5: Revocation — The Hard Part
When employment ends, the worker's ID must be invalidated immediately. But their phone still has the signed credential offline. How do you revoke a cryptographically valid credential without server contact?
The problem:
Worker leaves company on Monday.
Employer revokes credential Monday 09:00.
Inspector scans worker's QR Tuesday at a site with no connectivity.
QR signature is still cryptographically valid.
Inspector app cannot reach server to check revocation status.
→ How does the app know the credential was revoked?Solution: versioned credentials + minimum valid version
Every credential contains: card_version (integer, e.g., 42)
When employer revokes a worker:
DB: UPDATE workers SET min_valid_version = 43 WHERE id = wkr_abc123
Redis: SET min_valid:wkr_abc123 43 EX 86400
Inspector app sync (runs on WiFi, periodically):
GET /api/revocations/delta?since={last_sync_timestamp}
Returns: [ { worker_id: wkr_abc123, min_valid_version: 43 }, ... ]
App stores locally: { wkr_abc123: 43 }
Offline scan:
QR shows card_version = 42
App local store: min_valid_version for wkr_abc123 = 43
42 < 43 → REVOKED → show red "ID is no longer valid"Freshness guarantee: inspector app must sync within the last 24 hours to display a "verified" result. If last sync > 24h ago, app shows a warning: "Revocation list is stale — connect to network to refresh."
This bounds the worst-case window: a revoked credential is always rejected within 24 hours on any offline inspector.
Step 6: The Verification Paths
Online verification (inspector has connectivity):
App scans QR
1. Verify cryptographic signature locally (< 1ms)
2. POST /api/verify { credential_payload }
Server checks:
- min_valid_version in Redis (not revoked)
- credential not in explicit revocation list
- employer still active in registry
Returns: { valid: true, worker: {...}, employer: {...} }
3. App writes to local audit buffer
4. Server writes to audit_log (DB)
Total: ~200ms
Offline verification (no connectivity):
App scans QR
1. Verify cryptographic signature locally (< 1ms)
2. Check card_version ≥ local min_valid_version
3. Check credential not expired
Result: VALID or REVOKED (no "unknown" state)
4. Audit event buffered locally → synced when connectivity returns
Total: < 50ms, fully localStep 7: Key Management
The platform's private key is the root of trust for all credentials. Compromise means every issued credential is untrusted.
Key hierarchy:
Root CA (offline, HSM-protected)
└── Signing key (online, rotated every 90 days)
└── Issued credentials (signed, 6-month validity)
Inspector apps ship with:
- Current signing public key
- Previous signing public key (for credentials issued before last rotation)
- Root CA public key (for verifying new signing keys)
Key rotation:
1. Generate new signing key pair
2. Sign new public key with root CA → distribute via app update / OTA config
3. Old key remains valid until all credentials signed with it expire
4. Old private key destroyed after 6-month overlap periodAzure Key Vault stores the online signing key. All signing operations happen server-side via Key Vault's API — the private key never leaves the HSM boundary. This is standard for GDPR-regulated credential systems.
Step 8: GDPR — Minimum Data, Right to Erasure
Work IDs contain just enough data for verification. Full PII stays in the backend, not in the credential.
Credential (inside QR — visible to inspectors):
✓ First name + last name initial ("Lars H.")
✓ Employer name
✓ Industry / card type
✓ Validity dates
✗ National ID number (hash only, never exposed)
✗ Date of birth
✗ Home address
Right to erasure (GDPR Article 17):
Worker requests deletion:
1. Revoke credential immediately (min_valid_version++)
2. Delete worker record from PostgreSQL
3. Anonymise audit log: replace worker_id with "DELETED_wkr_..."
4. Blob storage: delete credential files
5. Redis TTL: entries auto-expire, no action needed
Cannot delete: audit log entries (legal obligation for labour compliance)
→ Anonymised entries satisfy both GDPR erasure AND labour authority audit requirementsStep 9: Database Schema
EMPLOYERS
id UUID PRIMARY KEY
org_number TEXT UNIQUE -- Norwegian org registry number
name TEXT
industry ENUM (construction, cleaning, transport, other)
registry_status ENUM (active, suspended, deregistered)
created_at TIMESTAMPTZ
WORKERS
id UUID PRIMARY KEY
employer_id UUID REFERENCES employers(id)
name TEXT
national_id_hash TEXT (SHA-256, not reversible)
employment_start DATE
employment_end DATE (null if active)
card_version INT DEFAULT 1
min_valid_version INT DEFAULT 1 -- bump on revocation
status ENUM (active, revoked, expired)
credential_url TEXT -- blob storage path to signed QR payload
VERIFICATION_LOG
id UUID PRIMARY KEY
worker_id UUID (nullable after erasure — anonymised)
inspector_id UUID
employer_id UUID
scanned_at TIMESTAMPTZ
location_lat DECIMAL (nullable)
location_lng DECIMAL (nullable)
result ENUM (valid, revoked, expired, signature_invalid)
was_offline BOOLEANWhat the Interviewer Is Actually Testing
- Do you design credentials as cryptographically self-contained (signed payloads) rather than requiring a server round-trip for every scan?
- Do you explain the offline revocation problem specifically — not just "it works offline" but how revoked credentials are detected without connectivity?
- Do you use versioned credentials + minimum valid version (or a comparable revocation list approach) with a bounded staleness window?
- Do you address key management — rotation, HSM storage, trust distribution to inspector apps?
- Do you mention integration with government registries for employer validation?
- Do you handle GDPR right to erasure while preserving legally required audit records?
- Do you separate online and offline verification paths with explicit latency and freshness guarantees for each?
Related Case Studies
Go Deeper
Case studies teach the "what". Our courses teach the "how" — the patterns behind these decisions, built up from first principles.
Explore Courses