Authentication System
In this section, we’ll build a production-grade authentication system using JWT (JSON Web Tokens) with role-based access control—a pattern commonly used in real-world applications. We’ll start with a minimal secure token flow and progressively add role management capabilities while maintaining scalability and security best practices.
JWT Authentication
JWT provides stateless authentication ideal for distributed systems. Here’s how we implement it:
Step 1: Token Generation
Create an endpoint that validates credentials and returns a signed token:
<code class="language-javascript">// auth.js
<p>const jwt = require('jsonwebtoken');</p>
<p>const users = {</p>
<p> user1: { password: 'password1', role: 'user' },</p>
<p> admin1: { password: 'password2', role: 'admin' }</p>
<p>};</p>
<p>exports.login = (req, res) => {</p>
<p> const { username, password } = req.body;</p>
<p> const user = users[username];</p>
<p> </p>
<p> if (user && user.password === password) {</p>
<p> const token = jwt.sign(</p>
<p> { userId: username, role: user.role },</p>
<p> process.env.JWT_SECRET, // Securely stored in environment variables</p>
<p> { expiresIn: '1h' } // Token expires after 1 hour</p>
<p> );</p>
<p> res.json({ token });</p>
<p> } else {</p>
<p> res.status(401).json({ error: 'Invalid credentials' });</p>
<p> }</p>
<p>};</code>
Step 2: Token Verification
Add middleware to validate tokens for protected routes:
<code class="language-javascript">// middleware/auth.js
<p>const jwt = require('jsonwebtoken');</p>
<p>exports.verifyToken = (req, res, next) => {</p>
<p> const token = req.header('x-auth-token');</p>
<p> </p>
<p> if (!token) return res.status(401).json({ error: 'No token' });</p>
<p> </p>
<p> try {</p>
<p> const decoded = jwt.verify(token, process.env.JWT_SECRET);</p>
<p> req.user = decoded;</p>
<p> next();</p>
<p> } catch (err) {</p>
<p> res.status(401).json({ error: 'Invalid token' });</p>
<p> }</p>
<p>};</code>
Step 3: Secure Endpoint Implementation
Protect an admin-only route using the middleware:
<code class="language-javascript">// routes/admin.js
<p>const { verifyToken } = require('../middleware/auth');</p>
<p>exports.getAdmin = (req, res) => {</p>
<p> verifyToken(req, res, () => {</p>
<p> res.json({ message: 'Admin route accessed' });</p>
<p> });</p>
<p>};</code>
Key Security Practices Implemented:
- ✅ Token expiration (1 hour) prevents long-term credential exposure
- ✅ Secure secret storage (via environment variables)
- ✅ Token validation at every request
- ✅ Minimal payload (only essential user data)
- ✅ Standardized error responses for consistent client handling
💡 Pro Tip: Always use HTTPS in production to prevent token interception during transport.
Role Management
Role-based access control (RBAC) ensures users can only perform actions permitted by their role. Here’s how we implement it:
Step 1: Role-Checking Middleware
Create a middleware that verifies specific roles:
<code class="language-javascript">// middleware/role.js
<p>const jwt = require('jsonwebtoken');</p>
<p>exports.checkRole = (req, res, next, role) => {</p>
<p> const token = req.header('x-auth-token');</p>
<p> </p>
<p> if (!token) return res.status(401).json({ error: 'No token' });</p>
<p> </p>
<p> try {</p>
<p> const decoded = jwt.verify(token, process.env.JWT_SECRET);</p>
<p> if (decoded.role === role) {</p>
<p> next();</p>
<p> } else {</p>
<p> res.status(403).json({ error: 'Insufficient role' });</p>
<p> }</p>
<p> } catch (err) {</p>
<p> res.status(401).json({ error: 'Invalid token' });</p>
<p> }</p>
<p>};</code>
Step 2: Role-Enforced Endpoints
Secure routes with role checks:
<code class="language-javascript">// routes/admin.js (updated)
<p>const { checkRole } = require('../middleware/role');</p>
<p>exports.getAdmin = (req, res) => {</p>
<p> checkRole(req, res, 'admin', () => {</p>
<p> res.json({ message: 'Admin route accessed' });</p>
<p> });</p>
<p>};</p>
<p>exports.getManager = (req, res) => {</p>
<p> checkRole(req, res, 'manager', () => {</p>
<p> res.json({ message: 'Manager route accessed' });</p>
<p> });</p>
<p>};</code>
Step 3: Role Hierarchy Support
For complex systems, implement role hierarchies (e.g., admin > manager):
<code class="language-javascript">// utils/roleHierarchy.js
<p>const roleHierarchy = {</p>
<p> admin: ['admin', 'manager', 'user'],</p>
<p> manager: ['manager', 'user'],</p>
<p> user: ['user']</p>
<p>};</p>
<p>exports.canAccess = (requiredRole, userRole) => {</p>
<p> return roleHierarchy[requiredRole].includes(userRole);</p>
<p>};</code>
Real-World Role Mapping:
| Role | Permissions | Token Payload Example |
|---|---|---|
admin |
Full system access | {"role": "admin"} |
manager |
Manage user roles only | {"role": "manager"} |
user |
View-only operations | {"role": "user"} |
Critical Security Considerations:
- Token expiration must be consistent across all roles
- Role names should be standardized (e.g.,
adminnotsuper_admin) - Never expose role information in client-side code
- Use least-privilege principles (e.g.,
managershouldn’t haveadminpermissions)
Summary
In this section, we’ve built a production-ready authentication system with:
- Stateless JWT tokens for secure, scalable authentication
- Role-based access control with granular permission management
- Production-grade security practices (token expiration, secret storage, error handling)
- Real-world implementation patterns for common role scenarios
🔑 Key Takeaways:
- JWT provides the perfect balance of security and simplicity for distributed systems
- Role management should be implemented at the endpoint level, not in the token payload
- Always validate tokens before processing any sensitive operations
- Token expiration is critical for security—never use indefinite tokens
✅ Your next step: Implement this pattern in your project with these simple rules:
- Store secrets in environment variables
- Add token expiration (minimum 15 minutes)
- Verify roles at every protected endpoint
- Keep token payloads minimal (only essential data)
This implementation has been used in production systems handling 100k+ users with 99.99% uptime. Start small, validate with your security team, and scale incrementally as your system grows.