Common Broken Authentication in Qr Code Apps: Causes and Fixes
QR code apps often treat the QR payload as “proof of identity,” but a QR code is just data. Authentication breaks when the app or backend accepts that data without proving that the current user, devic
What causes broken authentication in QR code apps
QR code apps often treat the QR payload as “proof of identity,” but a QR code is just data. Authentication breaks when the app or backend accepts that data without proving that the current user, device, session, and transaction are legitimate.
Common technical root causes include:
- Bearer-token QR payloads: The QR code contains a JWT, API key, or session token. Anyone who photographs or screenshots it can reuse it.
- No short expiry: Tickets, coupons, access passes, or login QR codes remain valid for hours, days, or indefinitely.
- Missing server-side validation: The app validates QR data locally and trusts the result instead of calling the backend.
- Weak signatures: QR payloads are signed with predictable secrets, weak HMAC keys, or no signature at all.
- Replayable codes: The backend accepts the same QR token multiple times without checking nonce, scan count, or one-time-use state.
- No device or session binding: A QR code meant for one user or device can be scanned from another device.
- Broken deeplink handling: The app opens
myapp://auth?token=...and logs in the user without confirming the current session, app state, or origin. - Confusing authentication with authorization: A valid QR code proves “this code exists,” not “this user can access this resource.”
Real-world impact
Broken authentication in QR apps creates problems that users notice immediately:
- Account takeover: A leaked login QR code or magic-link token lets another person sign in.
- Ticket fraud: Event, transit, or parking QR codes get reused across multiple entries.
- Payment abuse: Loyalty, coupon, or wallet QR codes are scanned repeatedly for discounts or credits.
- Access control failures: Office, hotel, or warehouse QR badges grant access to the wrong person.
- Support overload: Users report “someone else scanned my code,” “my ticket was already used,” or “I got logged out unexpectedly.”
- Lower app store ratings: QR flows are usually mission-critical. One failed scan at a gate, checkout, or login screen can cause a one-star review.
- Revenue loss: Fraudulent redemptions, duplicate ticket scans, and stolen accounts directly affect GMV, refunds, and chargebacks.
How broken authentication manifests in QR code apps
| Failure mode | What it looks like | Risk |
|---|---|---|
| Screenshotable login QR | User scans a QR code from another device and the app logs in without additional confirmation. | Account takeover. |
| Reusable ticket QR | Same QR code validates successfully at two entrances or two terminals. | Fraud and operational disputes. |
| Expired QR still works | Old access pass, coupon, or reset QR code remains valid after intended expiry. | Unauthorized access. |
| Unsigned QR payload | Anyone can generate a QR with {user_id: 123, role: "admin"} or {coupon: true}. | Privilege escalation. |
| Deeplink token leakage | Auth token is passed through URL, logs, referrers, analytics, or clipboard history. | Session theft. |
| No scan-state check | Backend does not mark QR as consumed, active, expired, or revoked. | Replay attacks. |
| Wrong user bound to QR | QR generated for user A can be scanned while user B is logged in. | Data leakage or account confusion. |
How to detect broken authentication
Start by mapping every QR flow: login, registration, payment, ticket scan, coupon redemption, access badge, password reset, device pairing, and deep link callback.
Manual and tool-based checks
Use these techniques:
- Decode QR payloads with
qrdecode,zbarimg, or browser devtools to inspect whether secrets are embedded. - Proxy traffic with Burp Suite, OWASP ZAP, or Charles Proxy. Confirm every QR scan calls the backend for validation.
- Replay captured requests. Resubmit the same QR token or API request multiple times.
- Test cross-session behavior. Generate a QR in session A, scan it in session B, and verify the backend rejects it.
- Test device binding. Generate a QR on one device, then scan it from another device.
- Modify QR data. Change
user_id,ticket_id,role,amount,coupon_id, orexpires_at, then verify signature rejection. - Check expiry logic. Use time-shifted QR payloads and verify the server rejects expired codes.
- Review deeplinks. Test
myapp://, Universal Links, and App Links with expired, malformed, duplicated, and cross-user tokens.
Automated and autonomous QA
Automate QR authentication tests with Appium for Android and Playwright for web. Tools such as SUSATest can upload an APK or web URL, explore QR-related flows without scripts, and generate regression tests automatically. Its security checks cover OWASP Top 10, API security, and cross-session tracking. The flow tracking feature can verify login, registration, checkout, search, and QR redemption paths with PASS/FAIL verdicts, while coverage analytics show which screens and elements were exercised.
How to fix each example
1. Bearer-token QR login
Do not put raw session tokens or refresh tokens inside QR codes.
Better pattern:
- QR contains a short-lived, single-purpose
login_challenge_id. - Backend creates the challenge with nonce, device info, user context, and expiry.
- Scanning app sends challenge ID plus its own signed device assertion.
- Backend confirms both sides before issuing a session.
POST /qr-login/confirm
{
"challenge_id": "ch_123",
"device_id": "dev_456",
"device_signature": "sig_abc"
}
Reject if the QR challenge is already used, expired, or bound to a different device.
2. Reusable ticket or access QR
A valid QR should not be enough. The backend must enforce state.
if ticket.status != "issued":
reject()
if ticket.expires_at < now:
reject()
if ticket.scan_count > 0:
reject("already_used")
mark_ticket_scanned(ticket_id, scanner_id, timestamp)
For high-risk entry points, add location checks, gate terminal validation, or one-time rotating QR codes.
3. Unsigned or weakly signed QR payload
Sign every QR payload that affects authentication, payment, access, or discounts.
Use:
- HMAC-SHA256 or Ed25519 signatures
- Short expiry
- Audience claim
- Issuer claim
- Unique nonce or
jti - Server-side revocation for sensitive flows
Avoid encoding secrets directly in the QR code. Encode a reference ID instead.
payload = {
"iss": "your-app",
"aud": "qr-auth",
"sub": user_id,
"jti": uuid(),
"exp": now + 300
}
signature = HMAC_SHA256(secret, base64url(payload))
Validate signature, issuer, audience, expiry, and nonce on the server.
4. Broken deeplink authentication
Deeplinks should never log a user in just because a URL opened the app.
Apply these rules:
- Require HTTPS Universal Links or Android App Links.
- Bind tokens to the user session or explicit confirmation flow.
- Expire tokens quickly.
- Use PKCE for OAuth-style flows.
- Never pass access tokens in query strings.
- Clear tokens after one successful use.
- Reject tokens opened from a different logged-in user unless explicitly designed as cross-device login.
if current_user and deeplink_user != current_user:
show_confirmation_or_reject()
if token.used:
reject()
consume_token(token_id)
5. QR generated for the wrong user
Bind QR codes to the authenticated
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free