-
Notifications
You must be signed in to change notification settings - Fork 10.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
12 changed files
with
897 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
}); | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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}`); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} | ||
} |
Oops, something went wrong.