Authentication in Express applications is often underestimated.
Most implementations stop at “generate a JWT and verify it,” but real-world systems require much more:
• Refresh token rotation
• Token invalidation
• Concurrency safety
• Role-based access control (RBAC)
• Stateful tracking (usually via Redis)
After building this stack multiple times, I decided to formalise it into a reusable system and eventually a CLI that scaffolds it in seconds.
This post breaks down the architecture behind it and how you can use it.
The Problem with Typical JWT Auth
A basic JWT setup usually looks like this:
TypeScript
const token = jwt.sign(user, secret, { expiresIn: "15m" });
This works for simple cases, but breaks down quickly:
- No Token Revocation
Once issued, a JWT is valid until it expires.
You can’t easily invalidate it.
- Stateless Refresh = Security Risk
If refresh tokens are not tracked, they can be reused indefinitely.
- Concurrency Issues
If two refresh requests happen at the same time:
both can succeed
multiple valid tokens are created
This leads to token duplication and potential session abuse.
The Architecture I Settled On
To solve these issues
, the system uses:
Access Tokens (JWT)
• Short-lived (e.g. 15 minutes)
• Used for API authentication
• Stateless verification
Refresh Tokens (Stateful)
• Stored in Redis
• Rotated on every use
• Old tokens are invalidated
This gives you control over sessions.
Redis as a Token Store
Redis acts as the source of truth for refresh tokens:
• Track active sessions
• Invalidate tokens instantly
• Enforce single-use refresh tokens
Refresh Token Rotation
Every refresh request:
• Verifies the token
• Checks Redis for validity
• Deletes the old token
• Issues a new refresh token
This ensures:
A refresh token can only be used once.
Concurrency Safety
A key problem is handling simultaneous refresh requests.
Solution:
• Atomic operations in Redis
• Only one request can invalidate + rotate
• The second request fails with 401
This prevents replay attacks and token duplication.
RBAC (Role-Based Access Control)
Authentication is not enough , you also need authorization.
Example middleware:
TypeScript
app.get("/admin", auth.requireAdmin, (req, res) => {
res.json({ message: "Admin only" });
});
This ensures only users with the correct role can access protected routes.
Putting It Together
The system exposes a simple interface:
TypeScript:
const auth = await createAuthenik8({
jwtSecret: process.env.JWT_SECRET!,
refreshSecret: process.env.REFRESH_SECRET!,
});
From there:
signToken() → access tokens
generateRefreshToken() → refresh tokens
Refresh token() → rotation logic
requireAdmin → RBAC middleware
Automating the Setup
After building this repeatedly, I created a CLI to remove the setup step entirely:
Bash
npx create-authenik8-app my-app
This generates:
• Express + TypeScript project
• JWT + refresh token system
• Redis integration
•RBAC middleware
• Preconfigured routes
You can go from zero → running auth server in minutes.
Why Redis is Required
A common question is: “Why not keep everything stateless?”
Because:
• You can’t revoke tokens
• You can’t prevent reuse
• You can’t track sessions
Redis enables:
• Immediate invalidation
• Session tracking
• Single-use refresh tokens
Trade-offs
This approach introduces:
• External dependency (Redis)
• Slight complexity increase
• Network latency for token validation
But in exchange, you get:
• Proper session control
• Stronger security guarantees
• Predictable auth behavior
Final Thoughts
Authentication is one of those systems that seems simple until it isn’t.
The difference between a basic implementation and a production-ready one is:
• handling edge cases
• controlling state
• preventing abuse
This project started as a way to standardize those decisions and avoid rewriting the same logic repeatedly.
If you’ve built auth systems before, I’d be interested in how you approach:
- refresh token handling
- revocation strategies
- concurrency edge cases
Links:
Authenik8 site:https://authenik8.vercel.app/
Gitlab: https://gitlab.com/COD434/create-authenik8-app
Tiktok: https://www.tiktok.com/@thesbd8?\_r=1&\_t=ZS-9577WVfucT4
Try It
Bash
npx create-authenik8-app my-app
Closing
If you’re working with Express and need a solid starting point for authentication, this should save you a significant amount of setup time.
Feedback is welcome.
Top comments (0)