Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Windows Tipps & SecurityNighthawk M7 Pro im Test: Flexibler, aber teurer 5G-Router(21.09.2026 um 10:30 Uhr)
Sichere ProgrammierungNeue Gmail-Funktion: So sparst du jetzt Zeit bei Einmalcodes(21.09.2026 um 10:00 Uhr)
Sichere ProgrammierungYour GIF exporter is fine — the container is the problem(21.09.2026 um 10:01 Uhr)
Sichere ProgrammierungCSS, Motion, or GSAP? I Choose by Who Owns the Animation(21.09.2026 um 10:12 Uhr)
Windows Tipps & SecurityNighthawk M7 Pro im Test: Flexibler, aber teurer 5G-Router(21.09.2026 um 10:30 Uhr)
Sichere ProgrammierungNeue Gmail-Funktion: So sparst du jetzt Zeit bei Einmalcodes(21.09.2026 um 10:00 Uhr)
Sichere ProgrammierungYour GIF exporter is fine — the container is the problem(21.09.2026 um 10:01 Uhr)
Sichere ProgrammierungCSS, Motion, or GSAP? I Choose by Who Owns the Animation(21.09.2026 um 10:12 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

My app's anti-theft feature locked every user out

A real incident from the finance app I build solo. The security design was textbook. That's exactly why it bit me. The textbook setup Short-lived access token, long-lived rotating refresh token. Every time the client…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

A real incident from the finance app I build solo. The security design

was textbook. That's exactly why it bit me.





The textbook setup



Short-lived access token, long-lived rotating refresh token. Every

time the client refreshes:




  1. It presents its refresh token.

  2. The server verifies it, revokes it, and issues a brand-new
    access + refresh pair.

  3. The old refresh token is now dead — single use.



And the part everyone recommends (OWASP calls it refresh-token reuse

detection): if a client ever presents a refresh token that's already

been revoked
, treat it as theft. An attacker who stole a token and

replayed it would trip this. The safe response is scorched earth —

revoke every session for that user so both the attacker and the

victim get kicked and must sign in fresh.



This is correct. It catches a real attack. Ship it.





The incident



One evening my own app showed me a auth error over a €0.00 balance and

an "add your first account" screen — as if I'd never signed in. The

data was all there on the backend. Only the login was dead.



The audit log told the story. All day: healthy refreshes every ~15

minutes. Then, one refresh, the server saw an already-revoked token,

declared theft, and revoked all sessions. The "attacker"?



Every session in the chain carried the same device id. It was my

phone. My phone was the thief.





How a device robs itself



A same-device replay of its own just-rotated token has completely

benign causes, and they're all common:





  • Lost response. The server rotated the token and committed, but the
    HTTP response never made it back (dropped connection, backgrounded
    app, dead tunnel). The client still holds the old token and retries
    it. To the server: a revoked token, replayed.


  • A failed local write. The new token pair came back, but writing it
    to the keychain failed (or raced with app suspension). Next launch,
    the client presents the previous token.


  • Two processes racing. My app and its widget extension both hit a
    401 and both refresh with the same token. One wins and rotates it; the
    other arrives a beat later with what is now a revoked token.



None of these are theft. But "revoked token replayed" looked identical

to theft, so the nuke fired and I logged myself out everywhere.





The insight



Here's the thing I missed: for a same-device replay, the revoke-all

adds almost no security.



Reuse-detection defends against an attacker who exfiltrated a refresh

token and replays it from somewhere else. That "somewhere else" is

the signal. An attacker replaying from the same device, with the same

bound device id, already had full access to the device's storage — the

nuke doesn't save you there; they could just present the live token.



So the theft signal isn't "a revoked token was replayed." It's "a

revoked token was replayed from a different device than the one the

session is bound to
."





The fix



Bind sessions to a device id, then scope the scorched-earth response to

the cases that are actually suspicious:




if (session.revokedAt) {
const sameDeviceBenignReplay =
session.deviceId !== UNBOUND &&
session.deviceId === presentedDeviceId &&
session.revokedReason === "rotated";

if (!sameDeviceBenignReplay) {
await revokeAllSessions(session.userId, "theft_detected");
}
throw new RefreshError("token_replayed"); // 401 either way
}








  • Same device, replaying its own rotated token → plain 401. The
    client just re-authenticates its refresh normally or picks up the
    winner's new token. No nuke.


  • Different device, or a non-rotation revocation (sign-out), or an
    unbound session we can't attribute → keep the full revoke-all.
    Real
    theft signals still get scorched earth.



Three supporting fixes made it robust:





  1. A cross-process refresh coordinator. The app and widget now go
    through a single-flight mutex (a file lock in a shared container +
    a notification), so only one refresh is ever in flight per device.
    The loser waits for the winner's token instead of racing it.


  2. Harden the token write. Check the keychain write status, retry
    once, and log loudly on failure — a silently failed write was one
    of the replay causes.


  3. Never turn a transient failure into a logout. A dropped network
    or a 5xx on the refresh endpoint must be retryable, not "your
    session is dead." Only a genuine 401/403 from the auth server means
    sign out.






Takeaways




  • Rotating refresh tokens + reuse detection is still the right design.
    Keep it.

  • But "reused token" is not the same as "stolen token." The stolen
    signal is reuse from an unexpected device. Bind sessions to a
    device and let that distinction gate your most destructive response.

  • Make client refresh single-flight across every process that shares
    the credentials, or your own app becomes the attacker.

  • Distinguish transient failures from auth rejections everywhere in the
    refresh path. Logging users out on a network blip is its own outage.



The uncomfortable lesson: a security control that's too eager is

indistinguishable from a bug to the person it locks out. Scope the

blast radius to the actual threat.






I'm building EveryPenny, a private,

multi-currency money tracker for iPhone. This one shipped a fix the

same day — solo means the postmortem and the patch are the same

afternoon. Ask me anything about the auth model in the comments.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten My app's anti-theft feature locked every user out

Thematisch verwandte Begriffe: apps, antitheft, feature, locked · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-94030 | A security vulnerability has been detected in SerenityOS up to 3d83e4509…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick