TrendVidStream moved its entire authentication stack to a rotating-refresh-token system that spots a stolen token the moment it’s reused. The change turned a 30-day JWT that once let an attacker roam freely into a short-lived credential that instantly triggers a lock-out.
A single breach forced the switch: a partner SDK cached a 30-day JWT in plain text, an attacker extracted it and replayed it from another country, and the only remedy was to rotate the signing key—an operation that logged every user out. The incident reshaped the company’s token security and now powers the video-streaming service.
Why the old model failed
JWTs (JSON Web Tokens) are self-contained, signed blobs that let a server verify a request without a database lookup. The convenience hides a cost: if a token lasts weeks, stealing it gives an adversary weeks of access. In the breach, the stolen token remained valid until its 30-day expiry because there was no way to invalidate it early.
Rotating the signing key is the only global way to invalidate all tokens, but it forces every user to log in again, disrupting service and eroding trust. The flaw lay not in the JWT itself but in relying on a single, long-lived credential.
The new design in a nutshell
TrendVidStream now issues two kinds of tokens:
- Access tokens – valid for 15 minutes; they carry the permissions needed for each API call.
- Refresh tokens – single-use tokens that exchange a short-lived access token for a new pair.
When a client presents a refresh token, the server:
- Verifies the token’s signature and claims.
- Checks whether the token has already been used.
- If the check passes, issues a brand-new refresh token and a fresh 15-minute access token.
- Marks the old refresh token as used.
If step 2 fails—meaning the same token appears a second time—the server treats it as a theft signal and revokes the entire “family” of tokens linked to that login session. Both the victim and the attacker are forced to log in again, cutting the breach short.
Tracking token families
Instead of treating each token as an isolated record, the system groups them into families that start when a user logs in. Every rotation creates a new member of that family. The SQLite schema used in production stores:
- family_id – a stable identifier for the whole session.
- token_id – a unique identifier for each refresh token.
- generation – a counter useful for debugging.
- used_at – timestamp of when the token was redeemed.
- revoked – a flag that, when set, disables the entire family.
When a reuse event is detected, the revoked flag for that family_id is set, instantly invalidating every token belonging to the compromised session. This turns a silent hijack into a high-signal alert that appears in the logs.
Three implementation rules that keep the system reliable
Validate the signature first An attacker could guess token IDs and trigger mass revocations if the server checks “used-before?” before verifying the signature. Confirming authenticity first prevents unnecessary denial-of-service attacks.
Allow a grace window Mobile apps often fire two refresh requests in quick succession when a token expires. If the server is too strict, the second request would be flagged as reuse, logging out a legitimate user. A few seconds of buffer lets a “used” token still return a fresh pair, smoothing out race conditions.
Wrap the whole flow in a transaction with row locking Without atomicity, two concurrent requests could both think they are the first to use a token, issuing duplicate refresh tokens and breaking the single-use guarantee. A database transaction that locks the token row guarantees that only one request succeeds.
Takeaway: By making refresh tokens single-use and watching for reuse, a system can turn every stolen credential into an alarm, protecting users without forcing a platform-wide logout.
