const jwt = require('jsonwebtoken');
function authenticate(req, res, next) {
const header = req.headers.authorization || '';
const [scheme, token] = header.split(' ');
if (scheme !== 'Bearer' || !token) {
return res.status(401).json({ error: 'Missing or malformed Authorization header' });
}
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid or expired token' });
}
}
function requireRole(...allowedRoles) {
return function (req, res, next) {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
const userRoles = Array.isArray(req.user.roles)
? req.user.roles
: [req.user.role].filter(Boolean);
const permitted = userRoles.some((r) => allowedRoles.includes(r));
if (!permitted) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
module.exports = { authenticate, requireRole };
const express = require('express');
const { authenticate, requireRole } = require('./auth');
const router = express.Router();
router.use(authenticate);
router.get('/metrics', requireRole('admin', 'analyst'), (req, res) => {
res.json({ activeUsers: 1423, requestedBy: req.user.sub });
});
router.post('/users/:id/ban', requireRole('admin'), (req, res) => {
res.json({ banned: req.params.id, by: req.user.sub });
});
router.patch('/posts/:id', requireRole('admin', 'editor'), (req, res) => {
res.json({ updated: req.params.id, editor: req.user.sub });
});
module.exports = router;
const express = require('express');
const adminRoutes = require('./admin.routes');
const app = express();
app.use(express.json());
app.use('/admin', adminRoutes);
app.get('/health', (req, res) => res.json({ status: 'ok' }));
app.use((err, req, res, next) => {
console.error(err);
res.status(err.status || 500).json({ error: err.message || 'Internal Server Error' });
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`API listening on :${port}`);
});
This snippet shows how role-based access control (RBAC) is layered onto an Express API using two small, composable middlewares and a protected router. The core idea is separation of concerns: authentication answers "who is this request?" while authorization answers "is this user allowed to do this?". Keeping those in distinct middlewares means the same identity resolution runs everywhere, and each route independently declares the roles it demands.
In auth.js, authenticate extracts a bearer token from the Authorization header, verifies it with jwt.verify, and attaches the decoded claims to req.user. Any failure — a missing header, a malformed token, an expired signature — short-circuits with a 401, so downstream handlers never run against an unauthenticated request. Because it always calls next(err) or next(), it fits Express's linear middleware chain cleanly.
The heart of the pattern is requireRole, a middleware factory: it is a function that returns a middleware, closing over the allowedRoles passed at mount time. This is why the caller can write requireRole('admin', 'editor') and get back a purpose-built guard. Using a factory avoids repeating boilerplate per route and keeps the role policy declarative and visible at the routing layer. The returned function normalizes req.user.roles to an array, checks for any intersection with allowedRoles, and returns 403 when the user is authenticated but lacks permission — an important distinction from the 401 that authenticate raises.
In admin.routes.js, the two guards are composed in order: authenticate runs first to populate req.user, then requireRole(...) enforces policy. Mounting authenticate once with router.use means every route in the file is protected by default, which is safer than opt-in protection that is easy to forget.
Finally, app.js wires the router under /admin and adds a trailing error handler that maps thrown errors to JSON responses. The trade-off of JWT-based roles is that claims are only as fresh as the token; a revoked role persists until the token expires, so short lifetimes or a revocation check are worth considering for sensitive systems.
Related snips
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
module Api
module V1
class UsersController < BaseController
def show
user = User.includes(:profile).find(params[:id])
ETags for conditional requests and caching
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: String!
GraphQL API with Spring Boot
#!/usr/bin/env bash
set -euo pipefail
export VAULT_ADDR="https://vault.internal:8200"
export VAULT_TOKEN="${VAULT_TOKEN:?missing VAULT_TOKEN}"
Secrets management with environment isolation and Vault
import axios, { AxiosError } from 'axios'
import { v4 as uuidv4 } from 'uuid'
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
timeout: 15000,
Axios API client with interceptors
import { randomBytes, createHash } from "crypto";
import jwt from "jsonwebtoken";
import { RefreshTokenStore } from "./store";
const ACCESS_SECRET = process.env.ACCESS_SECRET!;
const ACCESS_TTL = "15m";
JWT access + refresh token rotation (conceptual)
Share this code
Here's the card — post it anywhere.