Enhancements We'll Add
-
Refresh Tokens (JWT + Refresh token system)
-
Two-Factor Authentication (2FA) via OTP
-
Role-Based Access Control (RBAC)
-
Swagger API Documentation
-
Unit Testing with Jest
Let’s break this down step-by-step. I’ll guide you through each feature and include code examples where possible.
1. Refresh Tokens
Why?
Access tokens expire quickly. Refresh tokens allow re-authentication without logging in again.
Step 1: Update User Schema
js
refreshToken: { type: String }
Step 2: Generate Refresh Token
js
const generateRefreshToken = (userId) => {
return jwt.sign({ id: userId }, process.env.JWT_REFRESH_SECRET, { expiresIn: '7d' });
};
Step 3: Login Endpoint (Update)
js
const accessToken = generateToken(user._id);
const refreshToken = generateRefreshToken(user._id);
user.refreshToken = refreshToken;
await user.save();
res.json({ accessToken, refreshToken });
Step 4: Refresh Endpoint
js
router.post('/refresh', async (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) return res.status(401).json({ message: "No token" });
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
const user = await User.findById(decoded.id);
if (user.refreshToken !== refreshToken) throw new Error();
const newAccessToken = generateToken(user._id);
res.json({ accessToken: newAccessToken });
} catch {
res.status(401).json({ message: "Invalid refresh token" });
}
});
2. Two-Factor Authentication (2FA with OTP)
Use speakeasy
for OTP generation and qrcode
for QR display.
bashnpm install speakeasy qrcode
Step 1: Enable 2FA in User Model
js
twoFactorEnabled: { type: Boolean, default: false },
twoFactorSecret: String,
Step 2: Setup 2FA Endpoint
js
const speakeasy = require('speakeasy');
const qrcode = require('qrcode');
router.get('/2fa/setup', protect, async (req, res) => {
const secret = speakeasy.generateSecret({ name: 'SecureAPI' });
const user = await User.findById(req.user);
user.twoFactorSecret = secret.base32;
await user.save();
const qr = await qrcode.toDataURL(secret.otpauth_url);
res.json({ qr });
});
Step 3: Verify OTP on Login
js
if (user.twoFactorEnabled) {
if (!otp) return res.status(400).json({ message: "OTP required" });
const verified = speakeasy.totp.verify({
secret: user.twoFactorSecret,
encoding: 'base32',
token: otp
});
if (!verified) return res.status(400).json({ message: "Invalid OTP" });
}
3. Role-Based Access Control (RBAC)
Step 1: Add Role to User
js
role: { type: String, enum: ['user', 'admin'], default: 'user' }
Step 2: Role Middleware
js
const checkRole = (...roles) => (req, res, next) => {
if (!roles.includes(req.user.role)) return res.status(403).json({ message: 'Forbidden' });
next();
};
Usage:
js
router.get('/admin', protect, checkRole('admin'), adminController);
4. Swagger Documentation
bashnpm install swagger-ui-express swagger-jsdoc
app.js
js
const swaggerUi = require('swagger-ui-express');
const swaggerJsDoc = require('swagger-jsdoc');
const swaggerSpec = swaggerJsDoc({
swaggerDefinition: {
openapi: '3.0.0',
info: {
title: 'Secure API',
version: '1.0.0',
},
servers: [{ url: 'http://localhost:5000/api' }]
},
apis: ['./routes/*.js']
});
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
Sample Swagger Comment in Route
js
/**
* @swagger
* /auth/login:
* post:
* summary: Login user
* tags: [Auth]
* requestBody:
* required: true
* responses:
* 200:
* description: Success
*/
5. Unit Testing with Jest
bashnpm install --save-dev jest supertest
Basic Test Example: /tests/auth.test
const request = require('supertest');
const app = require('../app');
describe('Auth routes', () => {
it('should register a user', async () => {
const res = await request(app).post('/api/auth/register')
.send({ email: 'test@example.com', password: 'password' });
expect(res.statusCode).toEqual(201);
expect(res.body).toHaveProperty('token');
});
});
Update package.json
for testing
json
"scripts": {
"test": "jest"
}
No comments:
Post a Comment