Authentication is a crucial component of any web application. It ensures that users are who they claim to be and allows them to securely access resources and data. In the world of Node.js and Express.js, Passport.js is a powerful and flexible tool for implementing authentication. This chapter will guide you through everything you need to know about Passport.js, from basic concepts to advanced usage, with practical examples and clear explanations.
Authentication is the process of verifying the identity of a user or a system. In web applications, it typically involves validating a user’s credentials, such as a username and password, against a stored record in a database. Once authenticated, the user can access protected resources, such as dashboards, profiles, or other private data.
Passport.js is a popular authentication middleware for Node.js. It provides a simple and flexible way to authenticate users using different strategies, such as username and password, OAuth, OpenID, and more. Passport.js is unopinionated, meaning it doesn’t impose specific methods for handling user data, making it highly adaptable to various types of applications.
In this section, we’ll set up a basic Express application and integrate Passport.js to handle local authentication using a username and password.
Let’s start by setting up a new Node.js project.
Create a new directory for your project and initialize it with npm:
mkdir passport-auth-example
cd passport-auth-example
npm init -y
Install Express, Passport, and other necessary middleware:
npm install express passport passport-local express-session body-parser
Next, we will create a basic Express.js application and configure Passport.js for local authentication.
app.js
const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const app = express();
const PORT = 3000;
// Middleware setup
app.use(bodyParser.urlencoded({ extended: false }));
// Session setup
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true
}));
// Initialize Passport and session handling
app.use(passport.initialize());
app.use(passport.session());
// Simulated user data
const users = [
{ id: 1, username: 'john', password: 'password123' },
{ id: 2, username: 'jane', password: 'password456' }
];
// Passport local strategy
passport.use(new LocalStrategy(
(username, password, done) => {
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
return done(null, false, { message: 'Incorrect username or password.' });
}
return done(null, user);
}
));
// Serialize user to store in session
passport.serializeUser((user, done) => {
done(null, user.id);
});
// Deserialize user from session
passport.deserializeUser((id, done) => {
const user = users.find(u => u.id === id);
done(null, user);
});
// Login route
app.post('/login',
passport.authenticate('local', {
successRedirect: '/dashboard',
failureRedirect: '/login',
failureFlash: false
})
);
// Dashboard route - protected
app.get('/dashboard', (req, res) => {
if (req.isAuthenticated()) {
res.send(`Welcome to your dashboard, ${req.user.username}!`);
} else {
res.redirect('/login');
}
});
// Login page route
app.get('/login', (req, res) => {
res.send(`
`);
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
express
, passport
, and passport-local
for handling the authentication process.express-session
is used for managing user sessions, ensuring that users remain logged in as they navigate between pages.LocalStrategy
checks the username and password against a simulated user database.passport.serializeUser
and passport.deserializeUser
handle how user data is stored in the session. The user ID is serialized into the session, and when a request is made, it is deserialized to retrieve the full user object./login
route uses Passport’s authenticate
method to check the credentials. On success, the user is redirected to the /dashboard
route./dashboard
route is protected by checking if the user is authenticated using req.isAuthenticated()
. If not, they are redirected to the login page.Start the Server: Run the application with:
node app.js
You should see the output:
Server running on http://localhost:3000
http://localhost:3000/login
.username: john
and password: password123
.One of the most powerful features of Passport.js is its ability to authenticate users through third-party providers like Google, Facebook, GitHub, and more. In this section, we’ll demonstrate how to authenticate using Google.
npm install passport-google-oauth20
To use Google authentication, you need to register your application with Google and obtain CLIENT_ID
and CLIENT_SECRET
.
app.js
(continued)
const GoogleStrategy = require('passport-google-oauth20').Strategy;
// Configure the Google strategy for Passport
passport.use(new GoogleStrategy({
clientID: 'YOUR_GOOGLE_CLIENT_ID',
clientSecret: 'YOUR_GOOGLE_CLIENT_SECRET',
callbackURL: 'http://localhost:3000/auth/google/callback'
},
(accessToken, refreshToken, profile, done) => {
// Simulate user retrieval from database
let user = users.find(u => u.username === profile.id);
if (!user) {
user = { id: users.length + 1, username: profile.id, name: profile.displayName };
users.push(user);
}
return done(null, user);
}
));
// Google auth route
app.get('/auth/google',
passport.authenticate('google', { scope: ['profile'] })
);
// Google auth callback
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
(req, res) => {
// Successful authentication, redirect to dashboard.
res.redirect('/dashboard');
}
);
GoogleStrategy
is configured with the clientID
, clientSecret
, and callbackURL
./auth/google
route initiates the Google OAuth flow, redirecting the user to Google’s login page./auth/google/callback
route is the callback URL that Google redirects to after the user has logged in. This route uses Passport to complete the authentication process and redirect the user to the dashboard.node app.js
.http://localhost:3000/auth/google
to start the Google OAuth process.Sometimes, the built-in strategies in Passport.js might not meet your needs. In such cases, you can implement a custom strategy.
Example: Let’s create a simple custom strategy that authenticates users based on a predefined API key.
app.js
(continued)
const { Strategy: CustomStrategy } = require('passport-custom');
passport.use('api-key', new CustomStrategy(
(req, done) => {
const apiKey = req.headers['api-key'];
const user = users.find(u => u.apiKey === apiKey);
if (!user) {
return done(null, false, { message: 'Invalid API key.' });
}
return done(null, user);
}
));
// API key route
app.get('/api/protected',
passport.authenticate('api-key', { session: false }),
(req, res) => {
res.send(`Access granted to user: ${req.user.username}`);
}
);
CustomStrategy
checks for an API key in the request headers and matches it with a user’s API key in the database./api/protected
route uses the custom API key strategy to authenticate requests.http://localhost:3000/api/protected
with the api-key
header set.Passport.js is a versatile tool that can be tailored to fit any authentication requirement, making it a must-know for any Express.js developer. By following the examples and guidelines provided, you should be well-equipped to implement secure and flexible authentication in your own projects. Happy coding !❤️