Skip to main content

On This Page

Implementing Production-Grade JWT Authentication with Express and TypeScript

2 min read
Share

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