| name | authentication-implementation |
| description | Guide for implementing secure authentication with JWT and session management. Use when adding authentication to an application. |
Authentication Implementation
Follow this process to implement secure authentication:
1. Password Hashing
Use bcrypt for password hashing:
import bcrypt from 'bcrypt';
const hashedPassword = await bcrypt.hash(password, 10);
await db.user.create({
data: { email, password: hashedPassword },
});
const user = await db.user.findUnique({ where: { email } });
const isValid = await bcrypt.compare(password, user.password);
2. JWT Token Generation
import jwt from 'jsonwebtoken';
function generateTokens(userId: string) {
const accessToken = jwt.sign(
{ userId },
process.env.JWT_SECRET!,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId },
process.env.JWT_REFRESH_SECRET!,
{ expiresIn: '7d' }
);
return { accessToken, refreshToken };
}
3. Authentication Middleware
import { Request, Response, NextFunction } from 'express';
interface AuthRequest extends Request {
userId?: string;
}
export async function authenticate(
req: AuthRequest,
res: Response,
next: NextFunction
) {
try {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Authentication required' });
}
const payload = jwt.verify(token, process.env.JWT_SECRET!) as { userId: string };
req.userId = payload.userId;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid or expired token' });
}
}
4. Login Endpoint
router.post('/api/auth/login', async (req, res) => {
const { email, password } = req.body;
const user = await db.user.findUnique({ where: { email } });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const { accessToken, refreshToken } = generateTokens(user.id);
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
res.json({ accessToken, user: { id: user.id, email: user.email } });
});
5. Logout Endpoint
router.post('/api/auth/logout', (req, res) => {
res.clearCookie('refreshToken');
res.json({ message: 'Logged out successfully' });
});
6. Token Refresh
router.post('/api/auth/refresh', async (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) {
return res.status(401).json({ error: 'Refresh token required' });
}
try {
const payload = jwt.verify(
refreshToken,
process.env.JWT_REFRESH_SECRET!
) as { userId: string };
const { accessToken } = generateTokens(payload.userId);
res.json({ accessToken });
} catch (error) {
res.status(401).json({ error: 'Invalid refresh token' });
}
});
7. Frontend Integration (React)
const AuthContext = createContext<AuthContextType | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [token, setToken] = useState<string | null>(null);
const login = async (email: string, password: string) => {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const data = await response.json();
setToken(data.accessToken);
setUser(data.user);
};
return (
<AuthContext.Provider value={{ user, token, login }}>
{children}
</AuthContext.Provider>
);
}
8. Security Checklist
- ✅ Hash passwords with bcrypt (salt rounds >= 10)
- ✅ Use HTTPS in production
- ✅ Set HTTP-only cookies for refresh tokens
- ✅ Short expiry for access tokens (15min)
- ✅ Implement rate limiting on auth endpoints
- ✅ Validate and sanitize all inputs
- ✅ Use CSRF protection
- ✅ Implement account lockout after failed attempts
- ❌ Never log passwords or tokens
- ❌ Never store tokens in localStorage (XSS risk)
- ❌ Don't expose user existence in error messages