diff --git a/examples/passport-jwt-example/README.md b/examples/passport-jwt-example/README.md
new file mode 100644
index 0000000000..cf675b0d49
--- /dev/null
+++ b/examples/passport-jwt-example/README.md
@@ -0,0 +1,41 @@
+
+# Example with [`passport-jwt`](https://www.passportjs.org/packages/passport-jwt/)
+
+This example shows how to retrieve the authentication context from a basic [Express](http://expressjs.com/) + [Passport](http://www.passportjs.org/) application.
+
+![Passport example](assets/passport_example.gif)
+
+Please read the related guide: https://socket.io/how-to/use-with-jwt
+
+## How to use
+
+```
+$ npm ci && npm start
+```
+
+And point your browser to `http://localhost:3000`. Optionally, specify a port by supplying the `PORT` env variable.
+
+## How it works
+
+The client sends the JWT in the headers:
+
+```js
+const socket = io({
+ extraHeaders: {
+ authorization: `bearer token`
+ }
+});
+```
+
+And the Socket.IO server then parses the token and retrieves the user context:
+
+```js
+io.engine.use((req, res, next) => {
+ const isHandshake = req._query.sid === undefined;
+ if (isHandshake) {
+ passport.authenticate("jwt", { session: false })(req, res, next);
+ } else {
+ next();
+ }
+});
+```
diff --git a/examples/passport-jwt-example/assets/passport_example.gif b/examples/passport-jwt-example/assets/passport_example.gif
new file mode 100644
index 0000000000..3a3ccce4a1
Binary files /dev/null and b/examples/passport-jwt-example/assets/passport_example.gif differ
diff --git a/examples/passport-jwt-example/cjs/index.html b/examples/passport-jwt-example/cjs/index.html
new file mode 100644
index 0000000000..04936a55da
--- /dev/null
+++ b/examples/passport-jwt-example/cjs/index.html
@@ -0,0 +1,154 @@
+
+
+
+
+ Passport JWT example
+
+
+
+
+
+
Authenticated!
+
+
+
+
+ Status |
+ Disconnected |
+
+
+ Socket ID |
+ |
+
+
+ Username |
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/passport-jwt-example/cjs/index.js b/examples/passport-jwt-example/cjs/index.js
new file mode 100644
index 0000000000..00e241cf7c
--- /dev/null
+++ b/examples/passport-jwt-example/cjs/index.js
@@ -0,0 +1,100 @@
+const express = require("express");
+const { createServer } = require("node:http");
+const { join } = require("node:path");
+const passport = require("passport");
+const passportJwt = require("passport-jwt");
+const JwtStrategy = passportJwt.Strategy;
+const ExtractJwt = passportJwt.ExtractJwt;
+const bodyParser = require("body-parser");
+const { Server } = require("socket.io");
+const jwt = require("jsonwebtoken");
+
+const port = process.env.PORT || 3000;
+const jwtSecret = "Mys3cr3t";
+
+const app = express();
+const httpServer = createServer(app);
+
+app.use(bodyParser.json());
+
+app.get("/", (req, res) => {
+ res.sendFile(join(__dirname, "index.html"));
+});
+
+app.get(
+ "/self",
+ passport.authenticate("jwt", { session: false }),
+ (req, res) => {
+ if (req.user) {
+ res.send(req.user);
+ } else {
+ res.status(401).end();
+ }
+ },
+);
+
+app.post("/login", (req, res) => {
+ if (req.body.username === "john" && req.body.password === "changeit") {
+ console.log("authentication OK");
+
+ const user = {
+ id: 1,
+ username: "john",
+ };
+
+ const token = jwt.sign(
+ {
+ data: user,
+ },
+ jwtSecret,
+ {
+ issuer: "accounts.examplesoft.com",
+ audience: "yoursite.net",
+ expiresIn: "1h",
+ },
+ );
+
+ res.json({ token });
+ } else {
+ console.log("wrong credentials");
+ res.status(401).end();
+ }
+});
+
+const jwtDecodeOptions = {
+ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+ secretOrKey: jwtSecret,
+ issuer: "accounts.examplesoft.com",
+ audience: "yoursite.net",
+};
+
+passport.use(
+ new JwtStrategy(jwtDecodeOptions, (payload, done) => {
+ return done(null, payload.data);
+ }),
+);
+
+const io = new Server(httpServer);
+
+io.engine.use((req, res, next) => {
+ const isHandshake = req._query.sid === undefined;
+ if (isHandshake) {
+ passport.authenticate("jwt", { session: false })(req, res, next);
+ } else {
+ next();
+ }
+});
+
+io.on("connection", (socket) => {
+ const req = socket.request;
+
+ 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-jwt-example/cjs/package.json b/examples/passport-jwt-example/cjs/package.json
new file mode 100644
index 0000000000..616728bd4e
--- /dev/null
+++ b/examples/passport-jwt-example/cjs/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "passport-jwt-example",
+ "version": "0.0.1",
+ "private": true,
+ "type": "commonjs",
+ "description": "Example with passport and JWT (https://www.passportjs.org/packages/passport-jwt/)",
+ "scripts": {
+ "start": "node index.js"
+ },
+ "dependencies": {
+ "body-parser": "^1.20.2",
+ "express": "~4.17.3",
+ "jsonwebtoken": "^9.0.2",
+ "passport": "^0.7.0",
+ "passport-jwt": "^4.0.1",
+ "socket.io": "^4.7.2"
+ },
+ "devDependencies": {
+ "prettier": "^3.1.1"
+ }
+}
diff --git a/examples/passport-jwt-example/esm/index.html b/examples/passport-jwt-example/esm/index.html
new file mode 100644
index 0000000000..04936a55da
--- /dev/null
+++ b/examples/passport-jwt-example/esm/index.html
@@ -0,0 +1,154 @@
+
+
+
+
+ Passport JWT example
+
+
+
+
+
+
Authenticated!
+
+
+
+
+ Status |
+ Disconnected |
+
+
+ Socket ID |
+ |
+
+
+ Username |
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/passport-jwt-example/esm/index.js b/examples/passport-jwt-example/esm/index.js
new file mode 100644
index 0000000000..d2d9f37977
--- /dev/null
+++ b/examples/passport-jwt-example/esm/index.js
@@ -0,0 +1,101 @@
+import express from "express";
+import { createServer } from "node:http";
+import { dirname, join } from "node:path";
+import { fileURLToPath } from "node:url";
+import passport from "passport";
+import { Strategy as JwtStrategy, ExtractJwt } from "passport-jwt";
+import bodyParser from "body-parser";
+import { Server } from "socket.io";
+import jwt from "jsonwebtoken";
+
+const port = process.env.PORT || 3000;
+const jwtSecret = "Mys3cr3t";
+
+const app = express();
+const httpServer = createServer(app);
+
+app.use(bodyParser.json());
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+
+app.get("/", (req, res) => {
+ res.sendFile(join(__dirname, "index.html"));
+});
+
+app.get(
+ "/self",
+ passport.authenticate("jwt", { session: false }),
+ (req, res) => {
+ if (req.user) {
+ res.send(req.user);
+ } else {
+ res.status(401).end();
+ }
+ },
+);
+
+app.post("/login", (req, res) => {
+ if (req.body.username === "john" && req.body.password === "changeit") {
+ console.log("authentication OK");
+
+ const user = {
+ id: 1,
+ username: "john",
+ };
+
+ const token = jwt.sign(
+ {
+ data: user,
+ },
+ jwtSecret,
+ {
+ issuer: "accounts.examplesoft.com",
+ audience: "yoursite.net",
+ expiresIn: "1h",
+ },
+ );
+
+ res.json({ token });
+ } else {
+ console.log("wrong credentials");
+ res.status(401).end();
+ }
+});
+
+const jwtDecodeOptions = {
+ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+ secretOrKey: jwtSecret,
+ issuer: "accounts.examplesoft.com",
+ audience: "yoursite.net",
+};
+
+passport.use(
+ new JwtStrategy(jwtDecodeOptions, (payload, done) => {
+ return done(null, payload.data);
+ }),
+);
+
+const io = new Server(httpServer);
+
+io.engine.use((req, res, next) => {
+ const isHandshake = req._query.sid === undefined;
+ if (isHandshake) {
+ passport.authenticate("jwt", { session: false })(req, res, next);
+ } else {
+ next();
+ }
+});
+
+io.on("connection", (socket) => {
+ const req = socket.request;
+
+ 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-jwt-example/esm/package.json b/examples/passport-jwt-example/esm/package.json
new file mode 100644
index 0000000000..1e2d7e7b9d
--- /dev/null
+++ b/examples/passport-jwt-example/esm/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "passport-jwt-example",
+ "version": "0.0.1",
+ "private": true,
+ "type": "module",
+ "description": "Example with passport and JWT (https://www.passportjs.org/packages/passport-jwt/)",
+ "scripts": {
+ "start": "node index.js"
+ },
+ "dependencies": {
+ "body-parser": "^1.20.2",
+ "express": "~4.17.3",
+ "jsonwebtoken": "^9.0.2",
+ "passport": "^0.7.0",
+ "passport-jwt": "^4.0.1",
+ "socket.io": "^4.7.2"
+ },
+ "devDependencies": {
+ "prettier": "^3.1.1"
+ }
+}
diff --git a/examples/passport-jwt-example/ts/index.html b/examples/passport-jwt-example/ts/index.html
new file mode 100644
index 0000000000..04936a55da
--- /dev/null
+++ b/examples/passport-jwt-example/ts/index.html
@@ -0,0 +1,154 @@
+
+
+
+
+ Passport JWT example
+
+
+
+
+
+
Authenticated!
+
+
+
+
+ Status |
+ Disconnected |
+
+
+ Socket ID |
+ |
+
+
+ Username |
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/passport-jwt-example/ts/index.ts b/examples/passport-jwt-example/ts/index.ts
new file mode 100644
index 0000000000..a0ef4d74ed
--- /dev/null
+++ b/examples/passport-jwt-example/ts/index.ts
@@ -0,0 +1,113 @@
+import express from "express";
+import { type Request, type Response } from "express";
+import { createServer } from "node:http";
+import { dirname, join } from "node:path";
+import { fileURLToPath } from "node:url";
+import passport from "passport";
+import { Strategy as JwtStrategy, ExtractJwt } from "passport-jwt";
+import bodyParser from "body-parser";
+import { Server } from "socket.io";
+import jwt from "jsonwebtoken";
+
+declare global {
+ namespace Express {
+ interface User {
+ id: number;
+ username: string;
+ }
+ }
+}
+
+const port = process.env.PORT || 3000;
+const jwtSecret = "Mys3cr3t";
+
+const app = express();
+const httpServer = createServer(app);
+
+app.use(bodyParser.json());
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+
+app.get("/", (req, res) => {
+ res.sendFile(join(__dirname, "index.html"));
+});
+
+app.get(
+ "/self",
+ passport.authenticate("jwt", { session: false }),
+ (req, res) => {
+ if (req.user) {
+ res.send(req.user);
+ } else {
+ res.status(401).end();
+ }
+ },
+);
+
+app.post("/login", (req, res) => {
+ if (req.body.username === "john" && req.body.password === "changeit") {
+ console.log("authentication OK");
+
+ const user = {
+ id: 1,
+ username: "john",
+ };
+
+ const token = jwt.sign(
+ {
+ data: user,
+ },
+ jwtSecret,
+ {
+ issuer: "accounts.examplesoft.com",
+ audience: "yoursite.net",
+ expiresIn: "1h",
+ },
+ );
+
+ res.json({ token });
+ } else {
+ console.log("wrong credentials");
+ res.status(401).end();
+ }
+});
+
+const jwtDecodeOptions = {
+ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+ secretOrKey: jwtSecret,
+ issuer: "accounts.examplesoft.com",
+ audience: "yoursite.net",
+};
+
+passport.use(
+ new JwtStrategy(jwtDecodeOptions, (payload, done) => {
+ return done(null, payload.data);
+ }),
+);
+
+const io = new Server(httpServer);
+
+io.engine.use(
+ (req: { _query: Record }, res: Response, next: Function) => {
+ const isHandshake = req._query.sid === undefined;
+ if (isHandshake) {
+ passport.authenticate("jwt", { session: false })(req, res, next);
+ } else {
+ next();
+ }
+ },
+);
+
+io.on("connection", (socket) => {
+ const req = socket.request as Request & { user: Express.User };
+
+ 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-jwt-example/ts/package.json b/examples/passport-jwt-example/ts/package.json
new file mode 100644
index 0000000000..f53cea59c4
--- /dev/null
+++ b/examples/passport-jwt-example/ts/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "passport-jwt-example",
+ "version": "0.0.1",
+ "private": true,
+ "type": "module",
+ "description": "Example with passport and JWT (https://www.passportjs.org/packages/passport-jwt/)",
+ "scripts": {
+ "start": "node index.js"
+ },
+ "dependencies": {
+ "@types/express": "^4.17.21",
+ "@types/jsonwebtoken": "^9.0.5",
+ "@types/passport": "^1.0.16",
+ "@types/passport-jwt": "^4.0.0",
+ "body-parser": "^1.20.2",
+ "express": "~4.17.3",
+ "jsonwebtoken": "^9.0.2",
+ "passport": "^0.7.0",
+ "passport-jwt": "^4.0.1",
+ "socket.io": "^4.7.2",
+ "ts-node": "^10.9.2",
+ "typescript": "^5.3.3"
+ },
+ "devDependencies": {
+ "prettier": "^3.1.1"
+ }
+}
diff --git a/examples/passport-jwt-example/ts/tsconfig.json b/examples/passport-jwt-example/ts/tsconfig.json
new file mode 100644
index 0000000000..fe03ed87a1
--- /dev/null
+++ b/examples/passport-jwt-example/ts/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "target": "ES2022",
+ "strict": true
+ },
+ "ts-node": {
+ "esm": true
+ }
+}