mirror of
https://github.com/socketio/socket.io.git
synced 2026-01-09 06:58:02 -05:00
docs: add example with JWT
Related: https://github.com/socketio/socket.io/issues/4910
This commit is contained in:
41
examples/passport-jwt-example/README.md
Normal file
41
examples/passport-jwt-example/README.md
Normal file
@@ -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.
|
||||
|
||||

|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
```
|
||||
BIN
examples/passport-jwt-example/assets/passport_example.gif
Normal file
BIN
examples/passport-jwt-example/assets/passport_example.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
154
examples/passport-jwt-example/cjs/index.html
Normal file
154
examples/passport-jwt-example/cjs/index.html
Normal file
@@ -0,0 +1,154 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Passport JWT example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="login-panel" style="display: none">
|
||||
<p>Not authenticated</p>
|
||||
<form id="login-form">
|
||||
<div>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" value="john" />
|
||||
<br/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" value="changeit" />
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Submit" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="home-panel" style="display: none">
|
||||
<p>Authenticated!</p>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Status</td>
|
||||
<td><span id="status">Disconnected</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Socket ID</td>
|
||||
<td><span id="socket-id"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Username</td>
|
||||
<td><span id="name"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<form id="logout-form">
|
||||
<div>
|
||||
<input type="submit" value="Log out" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script>
|
||||
const loginPanel = document.getElementById('login-panel');
|
||||
const homePanel = document.getElementById('home-panel');
|
||||
const loginForm = document.getElementById('login-form');
|
||||
const usernameInput = document.getElementById('username');
|
||||
const passwordInput = document.getElementById('password');
|
||||
const statusSpan = document.getElementById('status');
|
||||
const socketIdSpan = document.getElementById('socket-id');
|
||||
const usernameSpan = document.getElementById('name');
|
||||
const logoutForm = document.getElementById('logout-form');
|
||||
|
||||
let socket;
|
||||
|
||||
async function main() {
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
if (!token) {
|
||||
return showLoginPanel();
|
||||
}
|
||||
|
||||
const res = await fetch('/self', {
|
||||
headers: {
|
||||
authorization: `bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
showHomePanel();
|
||||
} else {
|
||||
showLoginPanel();
|
||||
}
|
||||
}
|
||||
|
||||
function showHomePanel() {
|
||||
loginPanel.style.display = 'none';
|
||||
homePanel.style.display = 'block';
|
||||
|
||||
// this will only work if HTTP long-polling is enabled, since WebSockets do not support providing additional headers
|
||||
socket = io({
|
||||
extraHeaders: {
|
||||
authorization: `bearer ${localStorage.getItem('token')}`
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('connect', () => {
|
||||
statusSpan.innerText = 'connected';
|
||||
socketIdSpan.innerText = socket.id;
|
||||
|
||||
socket.emit('whoami', (username) => {
|
||||
usernameSpan.innerText = username;
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
statusSpan.innerText = 'disconnected';
|
||||
socketIdSpan.innerText = '-';
|
||||
});
|
||||
}
|
||||
|
||||
function showLoginPanel() {
|
||||
loginPanel.style.display = 'block';
|
||||
homePanel.style.display = 'none';
|
||||
}
|
||||
|
||||
loginForm.onsubmit = async function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const res = await fetch('/login', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: usernameInput.value,
|
||||
password: passwordInput.value
|
||||
})
|
||||
})
|
||||
|
||||
if (res.status === 200) {
|
||||
const { token } = await res.json();
|
||||
localStorage.setItem('token', token);
|
||||
|
||||
showHomePanel();
|
||||
} else {
|
||||
passwordInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
logoutForm.onsubmit = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
socket.disconnect();
|
||||
localStorage.removeItem('token');
|
||||
|
||||
showLoginPanel();
|
||||
}
|
||||
|
||||
main();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
100
examples/passport-jwt-example/cjs/index.js
Normal file
100
examples/passport-jwt-example/cjs/index.js
Normal file
@@ -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}`);
|
||||
});
|
||||
21
examples/passport-jwt-example/cjs/package.json
Normal file
21
examples/passport-jwt-example/cjs/package.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
154
examples/passport-jwt-example/esm/index.html
Normal file
154
examples/passport-jwt-example/esm/index.html
Normal file
@@ -0,0 +1,154 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Passport JWT example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="login-panel" style="display: none">
|
||||
<p>Not authenticated</p>
|
||||
<form id="login-form">
|
||||
<div>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" value="john" />
|
||||
<br/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" value="changeit" />
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Submit" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="home-panel" style="display: none">
|
||||
<p>Authenticated!</p>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Status</td>
|
||||
<td><span id="status">Disconnected</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Socket ID</td>
|
||||
<td><span id="socket-id"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Username</td>
|
||||
<td><span id="name"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<form id="logout-form">
|
||||
<div>
|
||||
<input type="submit" value="Log out" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script>
|
||||
const loginPanel = document.getElementById('login-panel');
|
||||
const homePanel = document.getElementById('home-panel');
|
||||
const loginForm = document.getElementById('login-form');
|
||||
const usernameInput = document.getElementById('username');
|
||||
const passwordInput = document.getElementById('password');
|
||||
const statusSpan = document.getElementById('status');
|
||||
const socketIdSpan = document.getElementById('socket-id');
|
||||
const usernameSpan = document.getElementById('name');
|
||||
const logoutForm = document.getElementById('logout-form');
|
||||
|
||||
let socket;
|
||||
|
||||
async function main() {
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
if (!token) {
|
||||
return showLoginPanel();
|
||||
}
|
||||
|
||||
const res = await fetch('/self', {
|
||||
headers: {
|
||||
authorization: `bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
showHomePanel();
|
||||
} else {
|
||||
showLoginPanel();
|
||||
}
|
||||
}
|
||||
|
||||
function showHomePanel() {
|
||||
loginPanel.style.display = 'none';
|
||||
homePanel.style.display = 'block';
|
||||
|
||||
// this will only work if HTTP long-polling is enabled, since WebSockets do not support providing additional headers
|
||||
socket = io({
|
||||
extraHeaders: {
|
||||
authorization: `bearer ${localStorage.getItem('token')}`
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('connect', () => {
|
||||
statusSpan.innerText = 'connected';
|
||||
socketIdSpan.innerText = socket.id;
|
||||
|
||||
socket.emit('whoami', (username) => {
|
||||
usernameSpan.innerText = username;
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
statusSpan.innerText = 'disconnected';
|
||||
socketIdSpan.innerText = '-';
|
||||
});
|
||||
}
|
||||
|
||||
function showLoginPanel() {
|
||||
loginPanel.style.display = 'block';
|
||||
homePanel.style.display = 'none';
|
||||
}
|
||||
|
||||
loginForm.onsubmit = async function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const res = await fetch('/login', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: usernameInput.value,
|
||||
password: passwordInput.value
|
||||
})
|
||||
})
|
||||
|
||||
if (res.status === 200) {
|
||||
const { token } = await res.json();
|
||||
localStorage.setItem('token', token);
|
||||
|
||||
showHomePanel();
|
||||
} else {
|
||||
passwordInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
logoutForm.onsubmit = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
socket.disconnect();
|
||||
localStorage.removeItem('token');
|
||||
|
||||
showLoginPanel();
|
||||
}
|
||||
|
||||
main();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
101
examples/passport-jwt-example/esm/index.js
Normal file
101
examples/passport-jwt-example/esm/index.js
Normal file
@@ -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}`);
|
||||
});
|
||||
21
examples/passport-jwt-example/esm/package.json
Normal file
21
examples/passport-jwt-example/esm/package.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
154
examples/passport-jwt-example/ts/index.html
Normal file
154
examples/passport-jwt-example/ts/index.html
Normal file
@@ -0,0 +1,154 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Passport JWT example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="login-panel" style="display: none">
|
||||
<p>Not authenticated</p>
|
||||
<form id="login-form">
|
||||
<div>
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" value="john" />
|
||||
<br/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" value="changeit" />
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Submit" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="home-panel" style="display: none">
|
||||
<p>Authenticated!</p>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Status</td>
|
||||
<td><span id="status">Disconnected</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Socket ID</td>
|
||||
<td><span id="socket-id"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Username</td>
|
||||
<td><span id="name"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<form id="logout-form">
|
||||
<div>
|
||||
<input type="submit" value="Log out" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script>
|
||||
const loginPanel = document.getElementById('login-panel');
|
||||
const homePanel = document.getElementById('home-panel');
|
||||
const loginForm = document.getElementById('login-form');
|
||||
const usernameInput = document.getElementById('username');
|
||||
const passwordInput = document.getElementById('password');
|
||||
const statusSpan = document.getElementById('status');
|
||||
const socketIdSpan = document.getElementById('socket-id');
|
||||
const usernameSpan = document.getElementById('name');
|
||||
const logoutForm = document.getElementById('logout-form');
|
||||
|
||||
let socket;
|
||||
|
||||
async function main() {
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
if (!token) {
|
||||
return showLoginPanel();
|
||||
}
|
||||
|
||||
const res = await fetch('/self', {
|
||||
headers: {
|
||||
authorization: `bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
showHomePanel();
|
||||
} else {
|
||||
showLoginPanel();
|
||||
}
|
||||
}
|
||||
|
||||
function showHomePanel() {
|
||||
loginPanel.style.display = 'none';
|
||||
homePanel.style.display = 'block';
|
||||
|
||||
// this will only work if HTTP long-polling is enabled, since WebSockets do not support providing additional headers
|
||||
socket = io({
|
||||
extraHeaders: {
|
||||
authorization: `bearer ${localStorage.getItem('token')}`
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('connect', () => {
|
||||
statusSpan.innerText = 'connected';
|
||||
socketIdSpan.innerText = socket.id;
|
||||
|
||||
socket.emit('whoami', (username) => {
|
||||
usernameSpan.innerText = username;
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
statusSpan.innerText = 'disconnected';
|
||||
socketIdSpan.innerText = '-';
|
||||
});
|
||||
}
|
||||
|
||||
function showLoginPanel() {
|
||||
loginPanel.style.display = 'block';
|
||||
homePanel.style.display = 'none';
|
||||
}
|
||||
|
||||
loginForm.onsubmit = async function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const res = await fetch('/login', {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: usernameInput.value,
|
||||
password: passwordInput.value
|
||||
})
|
||||
})
|
||||
|
||||
if (res.status === 200) {
|
||||
const { token } = await res.json();
|
||||
localStorage.setItem('token', token);
|
||||
|
||||
showHomePanel();
|
||||
} else {
|
||||
passwordInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
logoutForm.onsubmit = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
socket.disconnect();
|
||||
localStorage.removeItem('token');
|
||||
|
||||
showLoginPanel();
|
||||
}
|
||||
|
||||
main();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
113
examples/passport-jwt-example/ts/index.ts
Normal file
113
examples/passport-jwt-example/ts/index.ts
Normal file
@@ -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<string, string> }, 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}`);
|
||||
});
|
||||
27
examples/passport-jwt-example/ts/package.json
Normal file
27
examples/passport-jwt-example/ts/package.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
11
examples/passport-jwt-example/ts/tsconfig.json
Normal file
11
examples/passport-jwt-example/ts/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"target": "ES2022",
|
||||
"strict": true
|
||||
},
|
||||
"ts-node": {
|
||||
"esm": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user