Unit 4.2 · Tutorial 2
OAuth2 & Role-Based Access Control
Chapter 11 · Backend Development
~3 hours
Advanced
OAuth2 · Passport.js · RBAC · Security
Overview
OAuth2 enables third‑party authentication (Google, GitHub, etc.) without
sharing passwords. Role‑Based Access Control (RBAC) defines what users
can do based on their roles. This tutorial covers OAuth2 flows, Passport.js
integration, RBAC implementation, and API security best practices.
Why this matters:
OAuth2 improves user experience with social login. RBAC ensures users
have the appropriate permissions. Both are critical for modern applications.
1. OAuth2 Fundamentals
OAuth2 is an authorization framework that allows third‑party applications
to access user data without exposing credentials.
Resource Owner: The user.
Client: The application requesting access.
Authorization Server: Issues tokens (Google, GitHub).
Resource Server: Hosts protected resources.
// OAuth2 Authorization Code Flow
1. Client redirects user to Authorization Server
2. User authenticates and grants consent
3. Authorization Server redirects to Client with authorization code
4. Client exchanges code for access token
5. Client uses access token to access Resource Server
Grant types
Authorization Code: Most common, secure (uses client secret).
Implicit: Deprecated, less secure.
Client Credentials: Machine‑to‑machine.
Refresh Token: Used to obtain new access tokens.
2. Passport.js Integration
Passport.js is a popular authentication middleware for Node.js supporting
OAuth2 and many other strategies.
npm install passport passport-google-oauth2 passport-github2
// Google OAuth2 with Passport
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth2').Strategy;
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/api/auth/google/callback',
passReqToCallback: true,
}, async (req, accessToken, refreshToken, profile, done) => {
// Find or create user in database
const user = await findOrCreateUser(profile);
return done(null, user);
}));
// Routes
app.get('/api/auth/google', passport.authenticate('google', {
scope: ['profile', 'email'],
}));
app.get('/api/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
(req, res) => {
// Successful authentication
res.redirect('/dashboard');
}
);
// GitHub OAuth2
const GitHubStrategy = require('passport-github2').Strategy;
passport.use(new GitHubStrategy({
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: '/api/auth/github/callback',
}, async (accessToken, refreshToken, profile, done) => {
const user = await findOrCreateUser(profile);
return done(null, user);
}));
3. Role-Based Access Control (RBAC)
RBAC restricts access based on user roles (e.g., admin, user, moderator).
// User roles in JWT payload
const token = jwt.sign({
userId: user.id,
email: user.email,
role: user.role, // 'admin', 'user', 'moderator'
}, JWT_SECRET);
// Authorization middleware
const authorize = (...roles) => {
return (req, res, next) => {
const user = req.user; // from authenticate middleware
if (!user || !roles.includes(user.role)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
};
// Usage
// Only admins can access
app.delete('/api/users/:id', authenticate, authorize('admin'), (req, res) => {
// Delete user
});
// Admins and moderators can access
app.put('/api/posts/:id', authenticate, authorize('admin', 'moderator'), (req, res) => {
// Update post
});
Permissions model
// Fine‑grained permissions (CASL or custom)
const permissions = {
admin: ['read:all', 'write:all', 'delete:all'],
moderator: ['read:all', 'write:posts', 'delete:posts'],
user: ['read:own', 'write:own'],
};
const hasPermission = (user, action) => {
const userPermissions = permissions[user.role] || [];
return userPermissions.includes(action);
};
// Middleware for specific actions
const can = (action) => {
return (req, res, next) => {
if (!hasPermission(req.user, action)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
};
app.post('/api/admin', authenticate, can('write:all'), (req, res) => {
// Admin only
});
4. API Security Best Practices
HTTPS: Always use HTTPS in production.
Helmet: Set secure HTTP headers.
Rate limiting: Prevent brute‑force attacks.
Input validation: Validate and sanitize all inputs.
CORS: Restrict allowed origins.
JWT expiry: Set short‑lived access tokens with refresh tokens.
npm install helmet express-rate-limit cors
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const cors = require('cors');
// Security middleware
app.use(helmet());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP',
});
app.use('/api', limiter);
// CORS
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
credentials: true,
}));
// JWT refresh token
app.post('/api/auth/refresh', async (req, res) => {
const refreshToken = req.body.refreshToken;
// Verify refresh token (stored in database)
const user = await findUserByRefreshToken(refreshToken);
if (!user) return res.status(401).json({ error: 'Invalid refresh token' });
const newToken = generateToken(user.id, user.email);
res.json({ token: newToken });
});
OWASP Top 10: Always follow OWASP guidelines to protect
against common vulnerabilities like injection, broken authentication,
and XSS.
Quiz
Question 1
What is the primary purpose of OAuth2?
To encrypt user passwords
To allow third‑party access without sharing passwords
To store user sessions
To manage database connections
Show answer
B. To allow third‑party access without sharing passwords.
Question 2
Which Passport.js strategy is used for Google OAuth2?
passport-google
passport-google-oauth2
passport-oauth
passport-github
Show answer
B. passport-google-oauth2.
Question 3
What is the purpose of RBAC?
To authenticate users
To control access based on user roles
To encrypt data
To manage sessions
Show answer
B. To control access based on user roles.
Exercises
Exercise 1
Implement a Google OAuth2 login flow using Passport.js. Include the route
definitions and the strategy configuration.
Sample answer
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/auth/google/callback',
}, async (accessToken, refreshToken, profile, done) => {
const user = await User.findOne({ googleId: profile.id });
if (user) return done(null, user);
const newUser = new User({ googleId: profile.id, name: profile.displayName, email:
profile.emails[0].value });
await newUser.save();
done(null, newUser);
}));
app.get('/auth/google', passport.authenticate('google', { scope: ['profile',
'email'] }));
app.get('/auth/google/callback', passport.authenticate('google', { failureRedirect:
'/login' }), (req, res) => {
res.redirect('/dashboard');
});
Exercise 2
Add an `authorize` middleware that checks if the user has the `admin` role
before allowing access to the `/api/admin` endpoint.
Sample answer
const authorize = (roles) => (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
app.get('/api/admin', authenticate, authorize(['admin']), (req, res) => {
res.json({ message: 'Admin access granted' });
});
Homework
Homework 1
Implement a full RBAC system for your capstone project. Define at least 3
roles (admin, editor, viewer) and create middleware that checks permissions for different
endpoints.
Sample outline
Roles: admin, editor, viewer.
Permissions: Admin: full access. Editor: create/update posts.
Viewer: read only.
Middleware: `authorize(['admin', 'editor'])`.
Implementation: Store role in JWT and check in middleware.
Mini‑Project
Enterprise Auth System
Build an enterprise authentication system with:
JWT authentication (login/register)
OAuth2 login with Google and GitHub
RBAC with 4 roles (admin, manager, user, guest)
Protected routes with role‑based permissions
Rate limiting and Helmet for security
Refresh token support
Sample outline
Auth: JWT + OAuth2 (Google/GitHub).
Roles: admin, manager, user, guest.
Permissions: admin (all), manager (manage users), user (own
data), guest (read only).
Security: Helmet, rate limiting, CORS.
Refresh tokens: Stored in database, rotated on use.
Tutorial Summary
You learned OAuth2 fundamentals and Passport.js integration, implemented
Role‑Based Access Control (RBAC), and applied API security best practices.
These skills are essential for building secure, scalable applications
with proper authentication and authorization.
Key takeaway: Authentication verifies identity,
authorization controls access. Use OAuth2 for third‑party login and
RBAC for fine‑grained permissions.
Previous
Tutorial
Next Tutorial