Implementing Production-Grade JWT Authentication with Express and TypeScript
These articles are AI-generated summaries. Please check the original sources for full details.
JWT Auth in Express with TS
The NHero authentication system implements a dual-token architecture using JSON Web Tokens (JWT). It utilizes short-lived access tokens paired with database-stored refresh tokens to balance security and user experience.
Why This Matters
Many beginner implementations rely on a single JWT stored in LocalStorage, leaving applications vulnerable to XSS attacks and making session invalidation impossible. A production-ready model requires stateful refresh token management and HTTP-only cookies to ensure that sensitive credentials cannot be accessed by client-side scripts.
Key Insights
- Token Rotation: Using Access Tokens for short-term auth and Refresh Tokens for new access token generation prevents long-term session hijacking.
- Secure Storage: Implementing ‘httpOnly: true’ cookies protects tokens from XSS attacks compared to LocalStorage.
- Schema Integration: Attaching methods like ‘isPasswordCorrect’ directly to Mongoose schemas ensures logic stays close to the model for better reusability.
- Stateful Revocation: Storing refresh tokens in the database allows for proper logout functionality and detection of token reuse attacks.
Working Examples
Mongoose pre-save middleware for automatic password hashing.
userSchema.pre("save", async function (): Promise<void> {
if (!this.isModified("password")) return;
this.password = await bcrypt.hash(this.password, 10);
});
Middleware to verify JWTs and validate user existence in the database.
export const verifyJWT = async (req: Request, _: Response, next: NextFunction) => {
try {
const accessToken = req.cookies?.accessToken || req.header("Authorization")?.replace("Bearer ", "");
if (!accessToken) {
throw new ApiError(401, "Access token is missing");
}
const decodedToken = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET!!) as JwtPayload;
const user = await User.findById(decodedToken._id).select("-password -refreshToken");
if (!user) {
throw new ApiError(401, "Invalid Access Token");
}
req.user = user;
next();
} catch (error: any) {
throw new ApiError(401, error?.message || "Invalid Access Token");
}
};
Practical Applications
- …Use Case: Session Management; behavior involves storing refresh tokens in MongoDB to allow remote session invalidation during logout.
- …Pitfall: Sending sensitive fields; returning hashed passwords or refresh tokens in API responses exposes the system to unnecessary risk.
References:
Continue reading
Next article
Turborepo vs Nx vs Bazel: Choosing the Right Monorepo Strategy for 2026
Related Content
Automating Dependency Management with Renovate for Small Engineering Teams
Eliminate manual dependency updates and CVE risks by implementing an end-to-end automation system using Renovate.
Implementing OAuth 2.0 Device Flow for Input-Constrained Environments
Streamline authentication for CLIs and IoT devices using the OAuth 2.0 device authorization grant to eliminate complex password entry on limited interfaces.
Securing Remote Access: A Technical Guide to ssh-keygen
Learn how to use ssh-keygen to implement public-key authentication and secure server access using RSA, ECDSA, and Ed25519 algorithms.