How to use with Passport.js
Let's start from a basic application:
- CommonJS
- ES modules
- TypeScript
const express =require("express");
const{ createServer }=require("node:http");
const{Server}=require("socket.io");
const session =require("express-session");
const bodyParser =require("body-parser");
const passport =require("passport");
constLocalStrategy=require("passport-local").Strategy;
const{ join }=require("node:path");
const port = process.env.PORT||3000;
const app =express();
const httpServer =createServer(app);
const sessionMiddleware =session({
secret:"changeit",
resave:true,
saveUninitialized:true,
});
app.use(sessionMiddleware);
app.use(bodyParser.urlencoded({extended:false}));
app.use(passport.session());
app.get("/",(req, res)=>{
if(!req.user){
return res.redirect("/login");
}
res.sendFile(join(__dirname,"index.html"));
});
app.get("/login",(req, res)=>{
if(req.user){
return res.redirect("/");
}
res.sendFile(join(__dirname,"login.html"));
});
app.post(
"/login",
passport.authenticate("local",{
successRedirect:"/",
failureRedirect:"/",
}),
);
passport.use(
newLocalStrategy((username, password, done)=>{
if(username ==="john"&& password ==="changeit"){
console.log("authentication OK");
returndone(null,{id:1, username });
}else{
console.log("wrong credentials");
returndone(null,false);
}
}),
);
passport.serializeUser((user, cb)=>{
console.log(`serializeUser ${user.id}`);
cb(null, user);
});
passport.deserializeUser((user, cb)=>{
console.log(`deserializeUser ${user.id}`);
cb(null, user);
});
const io =newServer(httpServer);
httpServer.listen(port,()=>{
console.log(`application is running at: http://localhost:${port}`);
});
importexpressfrom"express";
import{ createServer }from"http";
import{Server}from"socket.io";
importsessionfrom"express-session";
importbodyParserfrom"body-parser";
importpassportfrom"passport";
import{StrategyasLocalStrategy}from"passport-local";
import{ dirname, join }from"node:path";
import{ fileURLToPath }from"node:url";
const port = process.env.PORT||3000;
const app =express();
const httpServer =createServer(app);
const sessionMiddleware =session({
secret:"changeit",
resave:true,
saveUninitialized:true,
});
app.use(sessionMiddleware);
app.use(bodyParser.urlencoded({extended:false}));
app.use(passport.session());
const __dirname =dirname(fileURLToPath(import.meta.url));
app.get("/",(req, res)=>{
if(!req.user){
return res.redirect("/login");
}
res.sendFile(join(__dirname,"index.html"));
});
app.get("/login",(req, res)=>{
if(req.user){
return res.redirect("/");
}
res.sendFile(join(__dirname,"login.html"));
});
app.post(
"/login",
passport.authenticate("local",{
successRedirect:"/",
failureRedirect:"/",
}),
);
app.post("/logout",(req, res)=>{
const sessionId = req.session.id;
req.session.destroy(()=>{
// disconnect all Socket.IO connections linked to this session ID
io.to(`session:${sessionId}`).disconnectSockets();
res.status(204).end();
});
});
passport.use(
newLocalStrategy((username, password, done)=>{
if(username ==="john"&& password ==="changeit"){
console.log("authentication OK");
returndone(null,{id:1, username });
}else{
console.log("wrong credentials");
returndone(null,false);
}
}),
);
passport.serializeUser((user, cb)=>{
console.log(`serializeUser ${user.id}`);
cb(null, user);
});
passport.deserializeUser((user, cb)=>{
console.log(`deserializeUser ${user.id}`);
cb(null, user);
});
const io =newServer(httpServer);
httpServer.listen(port,()=>{
console.log(`application is running at: http://localhost:${port}`);
});
import express =require("express");
import{ createServer }from"http";
import{ Server }from"socket.io";
import session from"express-session";
import{typeRequest,typeResponse}from"express";
import bodyParser =require("body-parser");
import passport =require("passport");
import{ Strategy as LocalStrategy }from"passport-local";
import{ dirname, join }from"node:path";
import{ fileURLToPath }from"node:url";
declare global {
namespace Express {
interfaceUser{
id:number;
username:string;
}
}
}
const port = process.env.PORT||3000;
const app =express();
const httpServer =createServer(app);
const sessionMiddleware =session({
secret:"changeit",
resave:true,
saveUninitialized:true,
});
app.use(sessionMiddleware);
app.use(bodyParser.urlencoded({ extended:false}));
app.use(passport.initialize());
app.use(passport.session());
const __dirname =dirname(fileURLToPath(import.meta.url));
app.get("/",(req, res)=>{
if(!req.user){
return res.redirect("/login");
}
res.sendFile(join(__dirname,"index.html"));
});
app.get("/login",(req, res)=>{
if(req.user){
return res.redirect("/");
}
res.sendFile(join(__dirname,"login.html"));
});
app.post(
"/login",
passport.authenticate("local",{
successRedirect:"/",
failureRedirect:"/",
}),
);
app.post("/logout",(req, res)=>{
const sessionId = req.session.id;
req.session.destroy(()=>{
// disconnect all Socket.IO connections linked to this session ID
io.to(`session:${sessionId}`).disconnectSockets();
res.status(204).end();
});
});
passport.use(
newLocalStrategy((username, password, done)=>{
if(username ==="john"&& password ==="changeit"){
console.log("authentication OK");
returndone(null,{ id:1, username });
}else{
console.log("wrong credentials");
returndone(null,false);
}
}),
);
passport.serializeUser((user, cb)=>{
console.log(`serializeUser ${user.id}`);
cb(null, user);
});
passport.deserializeUser((user: Express.User, cb)=>{
console.log(`deserializeUser ${user.id}`);
cb(null, user);
});
const io =newServer(httpServer);
httpServer.listen(port,()=>{
console.log(`application is running at: http://localhost:${port}`);
});
You'll need those additional types:
npm install @types/express @types/express-session @types/passport @types/passport-local
Sharing the user context
The user context can be shared with the Socket.IO server by calling:
functiononlyForHandshake(middleware){
return(req, res, next)=>{
const isHandshake = req._query.sid===undefined;
if(isHandshake){
middleware(req, res, next);
}else{
next();
}
};
}
io.engine.use(onlyForHandshake(sessionMiddleware));
io.engine.use(onlyForHandshake(passport.session()));
io.engine.use(
onlyForHandshake((req, res, next)=>{
if(req.user){
next();
}else{
res.writeHead(401);
res.end();
}
}),
);
Here's what happens:
- the
express-sessionmiddleware retrieves the session context from the cookie - the
passportmiddleware extracts the user context from the session - and finally, the handshake is validated if the user context was found
The onlyForHandshake() method ensures that the middlewares are only applied to the first HTTP request of the session.
You'll now have access to the user object:
io.on("connection",(socket)=>{
const user = socket.request.user;
});
Using the user ID
You can use the user ID to make the link between Express and Socket.IO:
io.on("connection",(socket)=>{
const userId = socket.request.user.id;
// the user ID is used as a room
socket.join(`user:${userId}`);
});
Which allows you to easily broadcast an event to all the connections of a given user:
io.to(`user:${userId}`).emit("foo","bar");
You can also check whether a user is currently connected:
const sockets =await io.in(`user:${userId}`).fetchSockets();
const isUserConnected = sockets.length>0;
That's it for the compatibility with Passport.js. Thanks for reading!
The complete example can be found here.
- CommonJS
- ES modules
- TypeScript
You can run this example directly in your browser on:
You can run this example directly in your browser on:
You can run this example directly in your browser on: