Reusable Node Code Modules, Part 3 - Authentication and Authorization
Securing Your API: Implementing Reusable Authentication and Authorization Modules
Authentication and authorization are critical for securing APIs, ensuring only authorized users can access specific resources. This guide covers how to structure reusable authentication and authorization modules in Node.js using Express.
Common Libraries and Tools
1. Passport
Passport is an authentication middleware for Node.js, providing a comprehensive and flexible approach to authentication
Key Features
Strategy-Based: Supports a wide range of authentication strategies (OAuth, JWT, local, etc.)
Middleware Integration: Seamlessly integrates with Express middleware
Extensibility: Allows for custom strategies and extensions
Community Support: Extensive community support and documentation
2. JWT (jsonwebtoken)
jsonwebtoken is a library to sign, verify, and decode JSON Web Tokens (JWT), often used for stateless authentication
Key Features
Stateless Authentication: Uses JWTs to manage user sessions without server-side storage
Security: Supports HMAC and RSA algorithms for token signing
Integration: Easily integrates with Express and other middleware
Flexibility: Allows custom payloads and claims
3. Auth0
Auth0 is a flexible, drop-in solution to add authentication and authorization services to applications.
Key Features
OAuth 2.0 and OpenID Connect: Supports industry-standard protocols
Customizable: Allows for custom rules, connections, and hooks
Management Dashboard: Provides a comprehensive dashboard for user management and monitoring
Extensive Integrations: Integrates with numerous third-party services and frameworks
4. Okta
Okta provides a platform for identity management and authentication services, supporting a wide range of authentication methods.
Key Features
Identity Management: Comprehensive identity management features
Security: Supports multi-factor authentication and adaptive security policies
API Access Management: Manages API access and scopes
Integration: Integrates with various applications and services
Comparison
Passport: Best for flexibility and strategy-based authentication with a wide range of supported methods
JWT (jsonwebtoken): Ideal for stateless authentication with secure token management
Auth0: Suitable for applications needing a robust, hosted authentication solution with extensive integrations
Okta: Best for comprehensive identity management and advanced security features
Examples
Example 1: Passport
Setup:
$ npm install passport passport-local express-session
Configuration:
import express from 'express';
import passport from 'passport';
import { Strategy as LocalStrategy } from 'passport-local';
import session from 'express-session';
const app = express();
// Dummy user for illustration
const user = { id: 1, username: 'user', password: 'pass' };
// Passport configuration
passport.use(new LocalStrategy(
(username, password, done) => {
if (username === user.username && password === user.password) {
return done(null, user);
} else {
return done(null, false, { message: 'Incorrect username or password.' });
}
}
));
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
done(null, user);
});
app.use(session({ secret: 'secret', resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());
Usage:
app.post('/login', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login'
}));
app.get('/logout', (req, res) => {
req.logout();
res.redirect('/');
});
app.get('/protected', (req, res) => {
if (req.isAuthenticated()) {
res.send('Access granted');
} else {
res.redirect('/login');
}
});
app.listen(3000, () => {
console.log('Server started on http://localhost:3000');
});
Example 2: JWT (jsonwebtoken)
Setup:
$ npm install jsonwebtoken express
Configuration:
import express from 'express';
import jwt from 'jsonwebtoken';
const app = express();
const secretKey = 'your_secret_key';
app.use(express.json());
Usage:
// Generate JWT
app.post('/login', (req, res) => {
const { username, password } = req.body;
if (username === 'user' && password === 'pass') {
const token = jwt.sign({ username }, secretKey, { expiresIn: '1h' });
res.json({ token });
} else {
res.sendStatus(401);
}
});
// Middleware to verify JWT
const authenticateJWT = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (token) {
jwt.verify(token, secretKey, (err, user) => {
if (err) {
return res.sendStatus(403);
}
req.user = user;
next();
});
} else {
res.sendStatus(401);
}
};
// Protected route
app.get('/protected', authenticateJWT, (req, res) => {
res.send('Access granted');
});
app.listen(3000, () => {
console.log('Server started on http://localhost:3000');
});
Example 3: Auth0
Setup:
$ npm install express express-jwt jwks-rsa
Configuration:
import express from 'express';
import jwt from 'express-jwt';
import jwksRsa from 'jwks-rsa';
const app = express();
const authConfig = {
domain: 'your-auth0-domain',
audience: 'your-auth0-audience'
};
// JWT middleware
const checkJwt = jwt({
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${authConfig.domain}/.well-known/jwks.json`
}),
audience: authConfig.audience,
issuer: `https://${authConfig.domain}/`,
algorithms: ['RS256']
});
Usage:
app.get('/protected', checkJwt, (req, res) => {
res.send('Access granted');
});
app.listen(3000, () => {
console.log('Server started on http://localhost:3000');
});
Example 4: Okta
Setup:
$ npm install express @okta/okta-sdk-nodejs @okta/oidc-middleware
Configuration:
import express from 'express';
import session from 'express-session';
import OktaJwtVerifier from '@okta/jwt-verifier';
import { ExpressOIDC } from '@okta/oidc-middleware';
const app = express();
const oktaJwtVerifier = new OktaJwtVerifier({
issuer: 'https://{yourOktaDomain}/oauth2/default',
clientId: '{clientId}',
assertClaims: {
aud: 'api://default',
},
});
const oidc = new ExpressOIDC({
issuer: 'https://{yourOktaDomain}/oauth2/default',
client_id: '{clientId}',
client_secret: '{clientSecret}',
redirect_uri: 'http://localhost:3000/authorization-code/callback',
scope: 'openid profile',
});
app.use(session({
secret: 'your_session_secret',
resave: true,
saveUninitialized: false,
}));
app.use(oidc.router);
Usage:
app.get('/protected', oidc.ensureAuthenticated(), (req, res) => {
res.send('Access granted');
});
oidc.on('ready', () => {
app.listen(3000, () => {
console.log('Server started on http://localhost:3000');
});
});
oidc.on('error', err => {
console.error('Unable to configure ExpressOIDC', err);
});