Build1 distinct publisher3 min readUpdated
A dev.to walkthrough starts with a friend whose attacker survived a password reset. The tutorial path to JWT auth ships with no per-user revocation at all.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
A dev.to walkthrough titled "JWT Authentication in Express That You Can Actually Revoke" opens with a message its author got from a friend: someone else was logged into his account, he had changed his password, and they were still in [1][2]. He had followed the tutorials to the letter, signing a JWT at login, handing it to the frontend, keeping it in localStorage and attaching it to every request [3]. The bug was not in his code. It was in the design he copied.
A signed token stays valid until it expires, and his were set to expire in 30 days [4]. The password reset accomplished nothing, because nothing in the token depended on the password [5]. There was no list of active sessions to delete from and nothing to revoke [6]. His remaining move was rotating the signing secret, which logs out every user on the platform at once [7]. That is the whole incident response budget of a tutorial auth system: one switch, and it hits everybody.
The post names four things standing between the demo and production, and they compound [8]. localStorage is readable by any JavaScript on the page, which includes your analytics snippet, an npm package compromised upstream, and any XSS hole of your own; one getItem call and the attacker has a credential they can replay from their own machine, undetected [9]. Stateless verification, the reason people pick JWTs, is exactly what removes revocation: ban a user and they stay logged in, reset a password and the thief stays logged in [10]. Long TTLs get chosen because nobody wants to re-authenticate every fifteen minutes, which is also what makes a stolen token worth stealing [11]. And the payload is encoded, not encrypted; the author says he has read internal role hierarchies, email addresses, and on one occasion a live database connection string out of token payloads [12].
The fix in the piece is two tokens with two jobs [13]. The access token stays a JWT, 15 minutes, held in JS memory, verified by signature with no database call [13][15]. The refresh token is deliberately not a JWT: 64 random bytes in an httpOnly cookie, sent only to /auth/*, stored hashed so a database leak does not hand over live sessions, and replaced every time it is used [14]. The arithmetic on the exposure window is the part worth writing on a whiteboard. Going from a 30-day token to a 15-minute one cuts the useful life of a stolen access credential by a factor of about 2,880 [18], and the server-side refresh record is what finally gives you a row to delete.
Rotation is also what makes theft detectable, and the author's assessment is that it is the piece almost nobody implements [16]. Everything downstream of that claim is unglamorous plumbing: the dependency list is express, jsonwebtoken, bcrypt, cookie-parser, pg and dotenv, plus helmet, express-rate-limit and cors [19], and the thirty-second step people skip is generating two distinct 256-bit-plus secrets rather than the literal string "secret" [17].
Worth checking this week, before anyone writes code: can you log out one named user right now, without touching anyone else's session? If the answer is secret rotation, you do not have revocation [6][7]. If the answer is a sessions table you can delete a row from, check whether refresh tokens in it are hashed [14], and whether reuse of an already-rotated token raises anything at all [16].
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
A dev.to article by mmushood is titled "JWT Authentication in Express That You Can Actually Revoke", subtitled: access tokens, refresh token rotation, and theft detection, the parts most Node.js tutorials leave out.
Nearly every "JWT authentication in Node.js" post ends in the same place, signing a token, putting it in localStorage and sending a Bearer header, which gets you a demo; the author names four things standing between that and production.
The JWT payload is not encrypted; base64url is encoding, and any JWT can be pasted into jwt.io and read. The author says he has seen internal role hierarchies, email addresses, and on one occasion a live database connection string in a token payload.
The recommended architecture is a very short-lived access token held in JS memory (a JWT, 15 minutes, sent as an Authorization Bearer header) plus a long-lived refresh token (opaque, 30 days) in an httpOnly cookie tracked in the database.
The access token is verified purely by signature with no database call, which is what keeps the API fast; if it leaks, exposure is fifteen minutes.
The friend's only remaining move was rotating the signing secret, which logged out every user on the platform at once; that was his entire kill switch.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
One self-contained tutorial; mechanics checkable, prevalence claims uncited
All evidence comes from a single dev.to walkthrough. Its core technical assertions are self-evidencing and independently checkable in principle (localStorage is readable by page JavaScript, base64url is encoding not encryption, signature-only verification means no revocation, the described token parameters appear in the shown config code). What is missing is any external corroboration: the breach anecdote is second-hand and unnamed, the prevalence statements are unquantified, and the supplied text ends before the rotation/theft-detection implementation that the article calls its centrepiece.
No adoption signal in the cluster
The cluster contains no release, deployment, benchmark, usage disclosure, pricing or licensing event — only a tutorial describing a pattern. Nothing in the supplied material indicates how many teams run this architecture, and inferring adoption from the author's claims about what "almost nobody" implements would be guessing.
Sound mechanics, overstated prevalence framing
Slightly overstated overall. The security substance is accurate and unglamorous, and the exposure-window arithmetic is honest, so the gap is small. It is positive rather than zero because the article leans on universal-sounding framing it does not evidence — that nearly every Node.js JWT post ends in an insecure place and that almost nobody implements rotation — and because the headline promise of revocation depends on the rotation and theft-detection code that the supplied text never reaches. The 2,880x window reduction also applies only to the access token; the refresh credential still lives 30 days.
Developer-audience content with self-cross-promotion, no vendor stake
Modest and visible incentives. This is individually authored platform content whose payoff is reach and reputation, including an explicit pointer back to the author's own earlier post on environment variables in Vite. There is no product, sponsor, paid tier or vendor being sold — the stack recommended is entirely commodity open-source npm packages — so the pull toward exaggeration is limited to engagement-friendly framing rather than commercial interest.
Confident on the pattern, thin on the world beyond it
Confidence is moderate. What the article recommends and why is unambiguous and internally consistent, and the technical premises are the kind that can be checked without trusting the author, so claims about the design itself are reliable. Confidence is held down by single-publisher sourcing, an unverifiable motivating incident, no adoption dimension at all, and a body excerpt that terminates mid-snippet before the rotation logic.
build
A Timed-Out Reset SMS Is Not A Failed One, And Your Retry Code Probably Disagrees1 distinct publisher
build
A GAN beauty filter is a device budget allocation, not a feature toggle1 distinct publisher
build
Every viewer hits your HLS key endpoint in the same second, and almost nobody tests it1 distinct publisher
build
The optional EntityManager is the bug: moving the transaction boundary into AsyncLocalStorage1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 15, 2026