The Engineering Challenge: Anti-Fraud at the Browser Edge
The holy grail of event ticketing security is a credential that: 1. Cannot be screenshotted or shared. 2. Runs in a standard mobile web browser without requiring an App Store download. 3. Validates in under 400 milliseconds on mobile 4G at a crowded college gate.
Here is the exact cryptographic architecture behind Spark's DQR:v1 system.
---
1. Token Construction & Time Buckets
Spark partitions time into discrete 60-second windows based on Unix epoch time: $$\text{bucket} = \left\lfloor \frac{\text{current\_epoch}}{60} \right\rfloor$$
The raw payload format is:
``
DQR:v1:<ticket_code>:<bucket>:<signature>
``
`ticket_code`: The unique 8-character ticket identifier (e.g., `TCK-48F1A902`).
bucket: The integer time epoch bucket (e.g., 29841205).
* signature: An HMAC-SHA256 signature calculated over ticket_code || ':' || bucket using the registration’s private signing seed.
---
2. Preventing Client Secret Leakage
In naive dynamic pass implementations, the client browser holds the secret key and generates tokens locally. An attacker can inspect DevTools, extract the secret, and build a script to generate valid passes for friends.
In Spark:
The secret signing seed is strictly isolated inside PostgreSQL.
Table-level SELECT on the seed column is revoked from PUBLIC, anon, and authenticated roles.
When the pass page loads at `/ticket/:code`, the browser calls an unauthenticated, rate-limited RPC: `get_dynamic_qr_token(_ticket_code)`.
The database calculates and returns the current valid token. The browser merely renders the SVG QR code and refreshes it on bucket expiration.
---
3. Drift Tolerance & Gate Verification
At the gate, when the volunteer's scanner reads the token, check_in_ticket executes:
1. Parse the token into code, bucket, and signature.
2. Recompute the expected HMAC signature using the ticket's internal seed.
3. Compare the bucket against the current server time bucket. Spark permits a ±1 bucket tolerance (allowing scans from 60 seconds ago or 60 seconds ahead to account for minor mobile clock drift).
4. If the bucket difference exceeds 1, the gate rejects with EXPIRED_DYNAMIC_PASS.
This provides bank-grade, time-sensitive entry verification entirely within PostgreSQL.
Frequently Asked Questions
How is the dynamic QR token structured?
The token follows the format DQR:v1:<ticket_code>:<time_bucket>:<signature>, where time_bucket is epoch / 60, and signature is an HMAC-SHA256 hash calculated with the ticket's private seed.
Can an attacker forge a dynamic pass if they know their ticket code?
No. The signing seed is stored securely in PostgreSQL and revoked from all public SELECT queries. Without the secret seed, an attacker cannot generate valid HMAC signatures.
Topics covered in this guide:
Next Step: Explore Spark Architecture
Zero commission on tickets. Direct UPI payments to your club account. Instant gate QR passes and automated certificate delivery.