Mean 14 Authentication Jwt
title: "JWT Authentication — Securing the MEAN Stack Application" description: "Implement JWT authentication in the MEAN Stack with Express backend token generation, login/signup endpoints, and Angular frontend token management." weight: 24 date: 2026-06-28 lastmod: 2026-06-28 tags: [frameworks, mean]
JWT (JSON Web Token) authentication provides a stateless authentication mechanism for the MEAN stack, where the server issues a signed token that the client sends with each request.
What You'll Learn
You will implement JWT authentication with user registration, login, token generation, protected routes, and Angular token storage with HTTP interceptors.
Why It Matters
Authentication is required for most applications. JWT provides a scalable, stateless authentication mechanism that works well with REST APIs and Single Page Applications.
Real-World Use
DodaZIP uses JWT authentication with access and refresh tokens. Access tokens expire after 15 minutes. Refresh tokens are stored in httpOnly cookies for secure renewal.
flowchart LR
A[User Login] --> B[Express Server]
B --> C[Verify Credentials]
C --> D[Generate JWT Token]
D --> E[Return Token]
E --> F[Angular Stores Token]
F --> G[Subsequent Requests]
G --> H[Include Token in Header]
H --> I[Express Verifies Token]
I --> J[Grant Access]
style B fill:#4a90d9,color:#fff
style I fill:#4a90d9,color:#fff
Backend JWT Setup
Install and configure JWT on the Express server.
npm install jsonwebtoken bcryptjs
// backend/config/auth.js
const jwt = require('jsonwebtoken');
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
const JWT_EXPIRES_IN = '7d';
function generateToken(userId) {
return jwt.sign({ userId }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
}
function verifyToken(token) {
try {
return jwt.verify(token, JWT_SECRET);
} catch (error) {
return null;
}
}
module.exports = { generateToken, verifyToken, JWT_SECRET };
Expected output: JWT utility functions. generateToken creates a signed token. verifyToken validates and decodes the token, returning null if invalid.
User Registration Endpoint
Create a registration endpoint with password hashing.
// backend/routes/authRoutes.js
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const User = require('../models/User');
const { generateToken } = require('../config/auth');
router.post('/register', async (req, res) => {
try {
const { name, email, password } = req.body;
// Check existing user
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({ error: 'Email already registered' });
}
// Hash password
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
// Create user
const user = await User.create({
name,
email,
password: hashedPassword
});
// Generate token
const token = generateToken(user._id);
res.status(201).json({
token,
user: { id: user._id, name: user.name, email: user.email, role: user.role }
});
} catch (error) {
res.status(400).json({ error: error.message });
}
});
Expected output: POST /api/auth/register creates a new user, hashes the password, generates a JWT, and returns the token and user data.
Login Endpoint
Create a login endpoint that verifies credentials and returns a token.
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
// Find user
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Verify password
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Generate token
const token = generateToken(user._id);
res.json({
token,
user: { id: user._id, name: user.name, email: user.email, role: user.role }
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
Expected output: POST /api/auth/login verifies email and password, returns a JWT token and user data on success, or 401 on failure.
Auth Middleware
Create middleware that protects routes by verifying the JWT token.
// backend/middleware/auth.js
const { verifyToken } = require('../config/auth');
const User = require('../models/User');
async function authMiddleware(req, res, next) {
try {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.split(' ')[1];
const decoded = verifyToken(token);
if (!decoded) {
return res.status(401).json({ error: 'Invalid or expired token' });
}
const user = await User.findById(decoded.userId).select('-password');
if (!user) {
return res.status(401).json({ error: 'User not found' });
}
req.user = user;
next();
} catch (error) {
res.status(500).json({ error: error.message });
}
}
module.exports = { authMiddleware };
Expected output: The middleware extracts the JWT from the Authorization header, verifies it, loads the user, and attaches user to the request object. Protected routes use this middleware.
Angular Auth Service and HTTP Interceptor
Handle token storage and automatic token attachment on the frontend.
// src/app/services/auth.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable, tap } from 'rxjs';
import { environment } from '../../environments/environment';
interface AuthResponse {
token: string;
user: { id: string; name: string; email: string; role: string };
}
@Injectable({ providedIn: 'root' })
export class AuthService {
private apiUrl = `${environment.apiUrl}/auth`;
private tokenKey = 'auth_token';
private userSubject = new BehaviorSubject<any>(null);
user$ = this.userSubject.asObservable();
constructor(private http: HttpClient) {
const savedUser = localStorage.getItem('user');
if (savedUser) {
this.userSubject.next(JSON.parse(savedUser));
}
}
register(data: { name: string; email: string; password: string }): Observable<AuthResponse> {
return this.http.post<AuthResponse>(`${this.apiUrl}/register`, data).pipe(
tap(response => this.handleAuth(response))
);
}
login(credentials: { email: string; password: string }): Observable<AuthResponse> {
return this.http.post<AuthResponse>(`${this.apiUrl}/login`, credentials).pipe(
tap(response => this.handleAuth(response))
);
}
logout() {
localStorage.removeItem(this.tokenKey);
localStorage.removeItem('user');
this.userSubject.next(null);
}
getToken(): string | null {
return localStorage.getItem(this.tokenKey);
}
isAuthenticated(): boolean {
return !!this.getToken();
}
private handleAuth(response: AuthResponse) {
localStorage.setItem(this.tokenKey, response.token);
localStorage.setItem('user', JSON.stringify(response.user));
this.userSubject.next(response.user);
}
}
HTTP Interceptor:
// src/app/interceptors/auth.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from '../services/auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const authService = inject(AuthService);
const token = authService.getToken();
if (token) {
req = req.clone({
setHeaders: { Authorization: `Bearer ${token}` }
});
}
return next(req);
};
// Provide in app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(withInterceptors([authInterceptor])),
]
};
Expected output: AuthService manages token storage in localStorage. The HTTP interceptor automatically adds the Bearer token to all outgoing requests.
Common Mistakes
Storing JWT in localStorage without security considerations: localStorage is accessible to JavaScript. Use httpOnly cookies for sensitive applications.
Not hashing passwords: Never store plain text passwords. Always hash with bcrypt before saving to the database.
Not validating token expiration on the client: Check token expiration before making requests. Redirect to login if the token is expired.
Returning the password hash in API responses: Always exclude the password field from responses using .select('-password').
Not handling token refresh: Access tokens expire. Implement refresh token flow to avoid requiring frequent logins.
Practice Questions
- What does JWT stand for and how does it work?
JSON Web Token. The server signs a token containing user claims. The client sends it with each request. The server verifies the signature.
- How do you hash passwords in Node.js?
Using bcryptjs. Generate a salt with genSalt(), then hash with bcrypt.hash(password, salt).
- How does the Angular auth interceptor work?
It intercepts all HTTP requests, gets the token from AuthService, and adds it as an Authorization header.
- What is the purpose of the auth middleware in Express?
It verifies the JWT token, loads the user, and attaches the user object to the request for route handlers.
- How do you protect routes in Express with auth middleware?
Add the authMiddleware as the second argument to the route: router.get('/profile', authMiddleware, handler).
Challenge
Implement a complete JWT authentication system with: register endpoint (with validation), login endpoint (with error handling), protected profile endpoint, Angular auth service (with token management), auth interceptor, and login/register components.
Frequently Asked Questions
Mini Project
Build a complete authentication UI with login form, registration form, protected dashboard (redirects to login if not authenticated), user profile display, and logout functionality.
What's Next
Implement Authorization for role-based access control in the MEAN application.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro