What
The verifyChallenge method checks redis.exists() at line 81 and only deletes the challenge at line 150, after a long processing window (XDR decoding, signature verification, database queries). Two concurrent verify requests for the same address can both pass the exists check before either deletes the key. Both then successfully verify and both get JWTs issued.
Why
An attacker can obtain multiple JWTs from a single SEP-10 challenge, enabling token multiplication.
Scope
- Use an atomic check-and-delete operation (e.g., Redis
GETDEL or a Lua script)
- Ensure the challenge is consumed exactly once
Acceptance Criteria
Technical Context
- File:
src/modules/auth/auth.service.ts, lines 81-150
- Current flow:
redis.exists() → verify → redis.del()
- Fix: Use
redis.getdel() or a Lua script for atomic check-and-delete
What
The
verifyChallengemethod checksredis.exists()at line 81 and only deletes the challenge at line 150, after a long processing window (XDR decoding, signature verification, database queries). Two concurrent verify requests for the same address can both pass theexistscheck before either deletes the key. Both then successfully verify and both get JWTs issued.Why
An attacker can obtain multiple JWTs from a single SEP-10 challenge, enabling token multiplication.
Scope
GETDELor a Lua script)Acceptance Criteria
Technical Context
src/modules/auth/auth.service.ts, lines 81-150redis.exists()→ verify →redis.del()redis.getdel()or a Lua script for atomic check-and-delete