API Key Authentication and Role-Based Access Control
API keys, API key generation, API key middleware, RBAC, role-based access control, permissions, admin middleware, resource ownership
API Key Authentication and Role-Based Access Control
JWT is ideal for user-facing authentication, but machine-to-machine API communication often uses API keys — long random tokens that identify and authenticate a service or client application. Role-Based Access Control (RBAC) then determines what authenticated users or services are allowed to do.
API Key Design
A good API key is a cryptographically random string of sufficient length (at least 32 bytes). Prefix it with a service identifier for easier identification: sk_live_a1b2c3d4e5f6.... Never send API keys as URL query parameters (they end up in server logs). Send them in the Authorization header or a custom X-API-Key header.
const crypto = require('crypto');
const generateApiKey = () => {
return 'sk_live_' + crypto.randomBytes(32).toString('hex');
};
API Key Middleware
const apiKeyAuth = async (req, res, next) => {
const key = req.headers['x-api-key'];
if (!key) return res.status(401).json({ error: { code: 'API_KEY_MISSING' } });
const hashedKey = crypto.createHash('sha256').update(key).digest('hex');
const client = await ApiClient.findOne({ keyHash: hashedKey, isActive: true });
if (!client) return res.status(401).json({ error: { code: 'API_KEY_INVALID' } });
req.client = client;
next();
};Store only the hash of the API key in the database — never the plaintext. This way, a database breach does not expose active keys.
Role-Based Access Control
const authorize = (...roles) => (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({
success: false,
error: { code: 'FORBIDDEN', message: 'Insufficient permissions' }
});
}
next();
};
router.delete('/:id', protect, authorize('admin'), deleteUser);