Skip to main content
Latest blog post (July 25, 2024): npm package provenance.

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}`);
});

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-session middleware retrieves the session context from the cookie
  • the passport middleware extracts the user context from the session
  • and finally, the handshake is validated if the user context was found
tip

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.

tip
  • CommonJS
  • ES modules
  • TypeScript

You can run this example directly in your browser on:

AltStyle によって変換されたページ (->オリジナル) /