From d943c3e0b0b6af1df544a6d26e09dbc8243f4d90 Mon Sep 17 00:00:00 2001 From: Damien Arrachequesne Date: Fri, 12 Jan 2024 17:08:19 +0100 Subject: [PATCH] docs: update the Passport.js example --- examples/passport-example/README.md | 2 + examples/passport-example/cjs/index.html | 55 ++++++++ examples/passport-example/cjs/index.js | 109 ++++++++++++++++ .../passport-example/{ => cjs}/login.html | 10 +- examples/passport-example/cjs/package.json | 20 +++ examples/passport-example/esm/index.html | 55 ++++++++ examples/passport-example/esm/index.js | 112 ++++++++++++++++ examples/passport-example/esm/login.html | 24 ++++ examples/passport-example/esm/package.json | 20 +++ examples/passport-example/index.html | 31 ----- examples/passport-example/index.js | 104 --------------- examples/passport-example/package.json | 16 --- examples/passport-example/ts/index.html | 55 ++++++++ examples/passport-example/ts/index.ts | 122 ++++++++++++++++++ examples/passport-example/ts/login.html | 24 ++++ examples/passport-example/ts/package.json | 27 ++++ examples/passport-example/ts/tsconfig.json | 11 ++ 17 files changed, 641 insertions(+), 156 deletions(-) create mode 100644 examples/passport-example/cjs/index.html create mode 100644 examples/passport-example/cjs/index.js rename examples/passport-example/{ => cjs}/login.html (62%) create mode 100644 examples/passport-example/cjs/package.json create mode 100644 examples/passport-example/esm/index.html create mode 100644 examples/passport-example/esm/index.js create mode 100644 examples/passport-example/esm/login.html create mode 100644 examples/passport-example/esm/package.json delete mode 100644 examples/passport-example/index.html delete mode 100644 examples/passport-example/index.js delete mode 100644 examples/passport-example/package.json create mode 100644 examples/passport-example/ts/index.html create mode 100644 examples/passport-example/ts/index.ts create mode 100644 examples/passport-example/ts/login.html create mode 100644 examples/passport-example/ts/package.json create mode 100644 examples/passport-example/ts/tsconfig.json diff --git a/examples/passport-example/README.md b/examples/passport-example/README.md index 9427a67525..96d641c92d 100644 --- a/examples/passport-example/README.md +++ b/examples/passport-example/README.md @@ -5,6 +5,8 @@ This example shows how to retrieve the authentication context from a basic [Expr ![Passport example](assets/passport_example.gif) +Please read the related guide: https://socket.io/how-to/use-with-passport + ## How to use ``` diff --git a/examples/passport-example/cjs/index.html b/examples/passport-example/cjs/index.html new file mode 100644 index 0000000000..0ca38b7799 --- /dev/null +++ b/examples/passport-example/cjs/index.html @@ -0,0 +1,55 @@ + + + + + Passport example + + +

Authenticated!

+ + + + + + + + + + + + + + + + +
StatusDisconnected
Socket ID
Username
+ +
+
+ +
+
+ + + + + diff --git a/examples/passport-example/cjs/index.js b/examples/passport-example/cjs/index.js new file mode 100644 index 0000000000..2f21a58ffa --- /dev/null +++ b/examples/passport-example/cjs/index.js @@ -0,0 +1,109 @@ +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"); +const LocalStrategy = 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.initialize()); +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: "/", + }), +); + +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( + new LocalStrategy((username, password, done) => { + if (username === "john" && password === "changeit") { + console.log("authentication OK"); + return done(null, { id: 1, username }); + } else { + console.log("wrong credentials"); + return done(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 = new Server(httpServer); + +io.engine.use(sessionMiddleware); +io.engine.use(passport.initialize()); +io.engine.use(passport.session()); + +io.engine.use( + (req, res, next) => { + if (req.user) { + next(); + } else { + res.writeHead(401); + res.end(); + } + }, +); + +io.on("connection", (socket) => { + const req = socket.request; + + socket.join(`session:${req.session.id}`); + socket.join(`user:${req.user.id}`); + + socket.on("whoami", (cb) => { + cb(req.user.username); + }); +}); + +httpServer.listen(port, () => { + console.log(`application is running at: http://localhost:${port}`); +}); diff --git a/examples/passport-example/login.html b/examples/passport-example/cjs/login.html similarity index 62% rename from examples/passport-example/login.html rename to examples/passport-example/cjs/login.html index 9a394519e3..fe8775dc3e 100644 --- a/examples/passport-example/login.html +++ b/examples/passport-example/cjs/login.html @@ -8,17 +8,17 @@

Not authenticated

- - + +
- - + +
- \ No newline at end of file + diff --git a/examples/passport-example/cjs/package.json b/examples/passport-example/cjs/package.json new file mode 100644 index 0000000000..681a17c710 --- /dev/null +++ b/examples/passport-example/cjs/package.json @@ -0,0 +1,20 @@ +{ + "name": "passport-example", + "version": "0.0.1", + "private": true, + "type": "commonjs", + "description": "Example with passport (https://www.passportjs.org)", + "scripts": { + "start": "node index.js" + }, + "dependencies": { + "express": "~4.17.3", + "express-session": "~1.17.2", + "passport": "^0.7.0", + "passport-local": "^1.0.0", + "socket.io": "^4.7.2" + }, + "devDependencies": { + "prettier": "^3.1.1" + } +} diff --git a/examples/passport-example/esm/index.html b/examples/passport-example/esm/index.html new file mode 100644 index 0000000000..0ca38b7799 --- /dev/null +++ b/examples/passport-example/esm/index.html @@ -0,0 +1,55 @@ + + + + + Passport example + + +

Authenticated!

+ + + + + + + + + + + + + + + + +
StatusDisconnected
Socket ID
Username
+ +
+
+ +
+
+ + + + + diff --git a/examples/passport-example/esm/index.js b/examples/passport-example/esm/index.js new file mode 100644 index 0000000000..71752ec9dc --- /dev/null +++ b/examples/passport-example/esm/index.js @@ -0,0 +1,112 @@ +import express from "express"; +import { createServer } from "http"; +import { Server } from "socket.io"; +import session from "express-session"; +import bodyParser from "body-parser"; +import passport from "passport"; +import { Strategy as LocalStrategy } 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.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( + new LocalStrategy((username, password, done) => { + if (username === "john" && password === "changeit") { + console.log("authentication OK"); + return done(null, { id: 1, username }); + } else { + console.log("wrong credentials"); + return done(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 = new Server(httpServer); + +io.engine.use(sessionMiddleware); +io.engine.use(passport.initialize()); +io.engine.use(passport.session()); + +io.engine.use( + (req, res, next) => { + if (req.user) { + next(); + } else { + res.writeHead(401); + res.end(); + } + }, +); + +io.on("connection", (socket) => { + const req = socket.request; + + socket.join(`session:${req.session.id}`); + socket.join(`user:${req.user.id}`); + + socket.on("whoami", (cb) => { + cb(req.user.username); + }); +}); + +httpServer.listen(port, () => { + console.log(`application is running at: http://localhost:${port}`); +}); diff --git a/examples/passport-example/esm/login.html b/examples/passport-example/esm/login.html new file mode 100644 index 0000000000..fe8775dc3e --- /dev/null +++ b/examples/passport-example/esm/login.html @@ -0,0 +1,24 @@ + + + + + Passport example + + +

Not authenticated

+
+
+ + +
+
+
+ + +
+
+ +
+
+ + diff --git a/examples/passport-example/esm/package.json b/examples/passport-example/esm/package.json new file mode 100644 index 0000000000..920dcec4c3 --- /dev/null +++ b/examples/passport-example/esm/package.json @@ -0,0 +1,20 @@ +{ + "name": "passport-example", + "version": "0.0.1", + "private": true, + "type": "module", + "description": "Example with passport (https://www.passportjs.org)", + "scripts": { + "start": "node index.js" + }, + "dependencies": { + "express": "~4.17.3", + "express-session": "~1.17.2", + "passport": "^0.7.0", + "passport-local": "^1.0.0", + "socket.io": "^4.7.2" + }, + "devDependencies": { + "prettier": "^3.1.1" + } +} diff --git a/examples/passport-example/index.html b/examples/passport-example/index.html deleted file mode 100644 index aa24d1b8fd..0000000000 --- a/examples/passport-example/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Passport example - - -

Authenticated!

-

Socket ID:

-

Username:

-
-
- -
-
- - - - diff --git a/examples/passport-example/index.js b/examples/passport-example/index.js deleted file mode 100644 index 0c054d58d8..0000000000 --- a/examples/passport-example/index.js +++ /dev/null @@ -1,104 +0,0 @@ -const app = require("express")(); -const server = require("http").createServer(app); -const port = process.env.PORT || 3000; - -const session = require("express-session"); -const bodyParser = require("body-parser"); -const passport = require("passport"); -const LocalStrategy = require("passport-local").Strategy; - -const sessionMiddleware = session({ secret: "changeit", resave: false, saveUninitialized: false }); -app.use(sessionMiddleware); -app.use(bodyParser.urlencoded({ extended: false })); -app.use(passport.initialize()); -app.use(passport.session()); - -const DUMMY_USER = { - id: 1, - username: "john", -}; - -passport.use( - new LocalStrategy((username, password, done) => { - if (username === "john" && password === "doe") { - console.log("authentication OK"); - return done(null, DUMMY_USER); - } else { - console.log("wrong credentials"); - return done(null, false); - } - }) -); - -app.get("/", (req, res) => { - const isAuthenticated = !!req.user; - if (isAuthenticated) { - console.log(`user is authenticated, session is ${req.session.id}`); - } else { - console.log("unknown user"); - } - res.sendFile(isAuthenticated ? "index.html" : "login.html", { root: __dirname }); -}); - -app.post( - "/login", - passport.authenticate("local", { - successRedirect: "/", - failureRedirect: "/", - }) -); - -app.post("/logout", (req, res) => { - console.log(`logout ${req.session.id}`); - const socketId = req.session.socketId; - if (socketId && io.of("/").sockets.get(socketId)) { - console.log(`forcefully closing socket ${socketId}`); - io.of("/").sockets.get(socketId).disconnect(true); - } - req.logout(); - res.cookie("connect.sid", "", { expires: new Date() }); - res.redirect("/"); -}); - -passport.serializeUser((user, cb) => { - console.log(`serializeUser ${user.id}`); - cb(null, user.id); -}); - -passport.deserializeUser((id, cb) => { - console.log(`deserializeUser ${id}`); - cb(null, DUMMY_USER); -}); - -const io = require('socket.io')(server); - -// convert a connect middleware to a Socket.IO middleware -const wrap = middleware => (socket, next) => middleware(socket.request, {}, next); - -io.use(wrap(sessionMiddleware)); -io.use(wrap(passport.initialize())); -io.use(wrap(passport.session())); - -io.use((socket, next) => { - if (socket.request.user) { - next(); - } else { - next(new Error('unauthorized')) - } -}); - -io.on('connect', (socket) => { - console.log(`new connection ${socket.id}`); - socket.on('whoami', (cb) => { - cb(socket.request.user ? socket.request.user.username : ''); - }); - - const session = socket.request.session; - console.log(`saving sid ${socket.id} in session ${session.id}`); - session.socketId = socket.id; - session.save(); -}); - -server.listen(port, () => { - console.log(`application is running at: http://localhost:${port}`); -}); diff --git a/examples/passport-example/package.json b/examples/passport-example/package.json deleted file mode 100644 index 58ad8f7130..0000000000 --- a/examples/passport-example/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "passport-example", - "version": "0.0.1", - "description": "Example with Passport (http://www.passportjs.org/)", - "dependencies": { - "body-parser": "~1.19.0", - "express": "~4.17.1", - "express-session": "~1.17.1", - "passport": "~0.4.1", - "passport-local": "~1.0.0", - "socket.io": "^4.0.0" - }, - "scripts": { - "start": "node index.js" - } -} diff --git a/examples/passport-example/ts/index.html b/examples/passport-example/ts/index.html new file mode 100644 index 0000000000..0ca38b7799 --- /dev/null +++ b/examples/passport-example/ts/index.html @@ -0,0 +1,55 @@ + + + + + Passport example + + +

Authenticated!

+ + + + + + + + + + + + + + + + +
StatusDisconnected
Socket ID
Username
+ +
+
+ +
+
+ + + + + diff --git a/examples/passport-example/ts/index.ts b/examples/passport-example/ts/index.ts new file mode 100644 index 0000000000..d929068998 --- /dev/null +++ b/examples/passport-example/ts/index.ts @@ -0,0 +1,122 @@ +import express = require("express"); +import { createServer, ServerResponse } from "http"; +import { Server } from "socket.io"; +import session from "express-session"; +import { type Request } 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 { + interface User { + 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( + new LocalStrategy((username, password, done) => { + if (username === "john" && password === "changeit") { + console.log("authentication OK"); + return done(null, { id: 1, username }); + } else { + console.log("wrong credentials"); + return done(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 = new Server(httpServer); + +io.engine.use(sessionMiddleware); +io.engine.use(passport.initialize()); +io.engine.use(passport.session()); + +io.engine.use( + (req: { user: Express.User }, res: ServerResponse, next: Function) => { + if (req.user) { + next(); + } else { + res.writeHead(401); + res.end(); + } + }, +); + +io.on("connection", (socket) => { + const req = socket.request as Request & { user: Express.User }; + + socket.join(`session:${req.session.id}`); + socket.join(`user:${req.user.id}`); + + socket.on("whoami", (cb) => { + cb(req.user.username); + }); +}); + +httpServer.listen(port, () => { + console.log(`application is running at: http://localhost:${port}`); +}); diff --git a/examples/passport-example/ts/login.html b/examples/passport-example/ts/login.html new file mode 100644 index 0000000000..fe8775dc3e --- /dev/null +++ b/examples/passport-example/ts/login.html @@ -0,0 +1,24 @@ + + + + + Passport example + + +

Not authenticated

+
+
+ + +
+
+
+ + +
+
+ +
+
+ + diff --git a/examples/passport-example/ts/package.json b/examples/passport-example/ts/package.json new file mode 100644 index 0000000000..ddb4522b90 --- /dev/null +++ b/examples/passport-example/ts/package.json @@ -0,0 +1,27 @@ +{ + "name": "passport-example", + "version": "0.0.1", + "private": true, + "type": "module", + "description": "Example with passport (https://www.passportjs.org)", + "scripts": { + "start": "ts-node index.ts" + }, + "dependencies": { + "@types/express": "^4.17.17", + "@types/express-session": "^1.17.7", + "@types/node": "^20.6.0", + "@types/passport": "^1.0.16", + "express": "~4.17.3", + "express-session": "~1.17.2", + "passport": "^0.7.0", + "passport-local": "^1.0.0", + "socket.io": "^4.7.2", + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + }, + "devDependencies": { + "@types/passport-local": "^1.0.38", + "prettier": "^3.1.1" + } +} diff --git a/examples/passport-example/ts/tsconfig.json b/examples/passport-example/ts/tsconfig.json new file mode 100644 index 0000000000..fe03ed87a1 --- /dev/null +++ b/examples/passport-example/ts/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", + "target": "ES2022", + "strict": true + }, + "ts-node": { + "esm": true + } +}