Difficulty: Advanced
Type: security
Background
The documented QR payload schema (type, version, guildId, resourceId, optional walletAddress/expiresAt) is plain JSON with format/expiry validation, but no described cryptographic integrity check — anyone who can generate JSON in this shape could produce a QR code the app will accept as a legitimate access-check request.
Problem
Without an integrity/authenticity check, a forged or tampered QR code (e.g., with a manipulated expiresAt to appear not-yet-expired, or a resourceId swapped to something the presenter doesn't actually have rights to trigger) is indistinguishable from a legitimately-issued one at the point of scanning, since access is ultimately re-verified server-side but a malicious payload could still mislead the scanning user or waste access-check calls maliciously.
Expected outcome
QR payloads include a signature field (e.g., an HMAC or ECDSA signature over the canonical payload, issued by the backend/guild admin tooling that generates the QR) that the mobile app verifies locally before even submitting an access check, rejecting unsigned or invalidly-signed payloads with a clear "Untrusted QR code" message — while remaining backward-compatible (or explicitly versioned) with the existing unsigned version: 1 payloads.
Suggested implementation
- Design a
version: 2 payload schema adding a signature field, with a documented canonicalization rule for what exactly gets signed (field order, encoding).
- Implement local signature verification in
src/features/access/ using a public key or shared verification approach appropriate for the signing method chosen (document the key-distribution approach, e.g., embedded public key vs. fetched from the backend on app start, with tradeoffs).
- Ensure
version: 1 (unsigned) payloads either continue to be accepted with a clear "unverified" indicator, or are rejected outright — decide and document the policy explicitly, since this is a meaningful security/compatibility tradeoff.
- Add a "Signature verified ✓" / "Untrusted QR code" distinct UI state on the scanner screen.
- Write thorough unit tests: valid signature, tampered payload with a stale signature, missing signature under the new policy, and the
version: 1 compatibility decision.
- Document the new payload version and signing scheme in the README's "QR access check payload" section and in
docs/.
Acceptance criteria
Likely affected files/directories
src/features/access/
app/access-scanner.tsx
docs/
README.md
Difficulty: Advanced
Type: security
Background
The documented QR payload schema (
type,version,guildId,resourceId, optionalwalletAddress/expiresAt) is plain JSON with format/expiry validation, but no described cryptographic integrity check — anyone who can generate JSON in this shape could produce a QR code the app will accept as a legitimate access-check request.Problem
Without an integrity/authenticity check, a forged or tampered QR code (e.g., with a manipulated
expiresAtto appear not-yet-expired, or aresourceIdswapped to something the presenter doesn't actually have rights to trigger) is indistinguishable from a legitimately-issued one at the point of scanning, since access is ultimately re-verified server-side but a malicious payload could still mislead the scanning user or waste access-check calls maliciously.Expected outcome
QR payloads include a signature field (e.g., an HMAC or ECDSA signature over the canonical payload, issued by the backend/guild admin tooling that generates the QR) that the mobile app verifies locally before even submitting an access check, rejecting unsigned or invalidly-signed payloads with a clear "Untrusted QR code" message — while remaining backward-compatible (or explicitly versioned) with the existing unsigned
version: 1payloads.Suggested implementation
version: 2payload schema adding asignaturefield, with a documented canonicalization rule for what exactly gets signed (field order, encoding).src/features/access/using a public key or shared verification approach appropriate for the signing method chosen (document the key-distribution approach, e.g., embedded public key vs. fetched from the backend on app start, with tradeoffs).version: 1(unsigned) payloads either continue to be accepted with a clear "unverified" indicator, or are rejected outright — decide and document the policy explicitly, since this is a meaningful security/compatibility tradeoff.version: 1compatibility decision.docs/.Acceptance criteria
version: 2payloads with a valid signature are accepted and clearly marked as verifiedversion: 1compatibility policy is explicitly implemented and documented, not left ambiguousdocs/updated with the new schema and key-distribution approachLikely affected files/directories
src/features/access/app/access-scanner.tsxdocs/README.md