2020-12-27 17:50:47 +01:00
< h1 align = "center" > Thream/socketio-jwt< / h1 >
2019-07-23 15:22:23 +02:00
2020-12-27 17:50:47 +01:00
< p align = "center" >
< strong > Authenticate socket.io incoming connections with JWTs.< / strong >
< / p >
2015-05-29 14:42:14 +02:00
2020-12-27 17:50:47 +01:00
< p align = "center" >
< a href = "https://github.com/Thream/socketio-jwt/actions?query=workflow%3A%22Node.js+CI%22" > < img src = "https://github.com/Thream/socketio-jwt/workflows/Node.js%20CI/badge.svg" alt = "Node.js CI" / > < / a >
< a href = "https://dependabot.com/" > < img src = "https://badgen.net/github/dependabot/Thream/socketio-jwt?icon=dependabot" alt = "Dependabot badge" / > < / a >
< a href = "https://www.npmjs.com/package/ts-standard" > < img alt = "TypeScript Standard Style" src = "https://camo.githubusercontent.com/f87caadb70f384c0361ec72ccf07714ef69a5c0a/68747470733a2f2f62616467656e2e6e65742f62616467652f636f64652532307374796c652f74732d7374616e646172642f626c75653f69636f6e3d74797065736372697074" / > < / a >
< a href = "./LICENSE" > < img src = "https://img.shields.io/badge/licence-MIT-blue.svg" alt = "Licence MIT" / > < / a >
< a href = "https://conventionalcommits.org" > < img src = "https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg" alt = "Conventional Commits" / > < / a >
< a href = "./.github/CODE_OF_CONDUCT.md" > < img src = "https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt = "Contributor Covenant" / > < / a >
< / p >
2019-07-16 11:33:44 +02:00
2020-12-27 17:50:47 +01:00
## 📜 About
2019-07-16 11:33:44 +02:00
2019-10-29 11:23:42 +01:00
Authenticate socket.io incoming connections with JWTs. This is useful if you are building a single page application and you are not using cookies as explained in this blog post: [Cookies vs Tokens. Getting auth right with Angular.JS ](http://blog.auth0.com/2014/01/07/angularjs-authentication-with-cookies-vs-token/ ).
2012-09-05 20:14:36 +02:00
2020-12-27 17:50:47 +01:00
This repository was originally forked from [auth0-socketio-jwt ](https://github.com/auth0-community/auth0-socketio-jwt ) & it is not intended to take any credit but to improve the code from now on.
2019-01-15 17:43:44 +01:00
2019-07-24 16:40:37 +02:00
## Installation
2019-01-15 17:43:44 +01:00
2020-12-27 17:50:47 +01:00
```sh
2014-01-13 20:00:21 +01:00
npm install socketio-jwt
2012-09-05 20:14:36 +02:00
```
2020-12-27 17:50:47 +01:00
## ⚙️ Usage
2012-09-05 20:14:36 +02:00
2014-01-14 12:30:39 +01:00
```javascript
// set authorization for socket.io
io.sockets
2020-12-27 17:25:44 +01:00
.on(
'connection',
socketioJwt.authorize({
secret: 'your secret or public key',
timeout: 15000 // 15 seconds to send the authentication message
})
)
2019-10-14 01:46:30 +02:00
.on('authenticated', (socket) => {
2014-01-14 12:30:39 +01:00
//this socket is authenticated, we are good to handle more events from it.
2020-12-27 17:25:44 +01:00
console.log(`hello! ${socket.decoded_token.name}`)
})
2014-01-14 12:30:39 +01:00
```
2014-04-30 02:27:43 +02:00
**Note:** If you are using a base64-encoded secret (e.g. your Auth0 secret key), you need to convert it to a Buffer: `Buffer('your secret key', 'base64')`
2019-01-15 17:43:44 +01:00
**Client side**
2014-01-14 12:30:39 +01:00
```javascript
2020-12-27 17:25:44 +01:00
const socket = io.connect('http://localhost:9000')
2019-10-14 01:46:30 +02:00
socket.on('connect', () => {
2014-01-14 12:30:39 +01:00
socket
2019-10-14 01:46:30 +02:00
.emit('authenticate', { token: jwt }) //send the jwt
.on('authenticated', () => {
2014-01-14 12:30:39 +01:00
//do other things
})
2019-10-14 01:46:30 +02:00
.on('unauthorized', (msg) => {
2020-12-27 17:25:44 +01:00
console.log(`unauthorized: ${JSON.stringify(msg.data)}`)
throw new Error(msg.data.type)
2016-08-19 17:01:36 +02:00
})
2020-12-27 17:25:44 +01:00
})
2014-01-14 12:30:39 +01:00
```
2019-01-15 17:43:44 +01:00
### One roundtrip
2014-01-14 12:30:39 +01:00
2019-01-15 17:43:44 +01:00
The previous approach uses a second roundtrip to send the jwt. There is a way you can authenticate on the handshake by sending the JWT as a query string, the caveat is that intermediary HTTP servers can log the url.
2012-09-05 20:14:36 +02:00
```javascript
2020-12-27 17:25:44 +01:00
const io = require('socket.io')(server)
const socketioJwt = require('socketio-jwt')
2019-07-24 16:40:37 +02:00
```
2012-09-05 20:14:36 +02:00
2019-07-24 16:40:37 +02:00
With socket.io < 1.0:
```javascript
2020-12-27 17:25:44 +01:00
io.set(
'authorization',
socketioJwt.authorize({
secret: 'your secret or public key',
handshake: true
})
)
2014-06-03 13:12:07 +02:00
2019-10-14 01:46:30 +02:00
io.on('connection', (socket) => {
2020-12-27 17:25:44 +01:00
console.log('hello!', socket.handshake.decoded_token.name)
})
2019-07-24 16:40:37 +02:00
```
With socket.io >= 1.0:
```javascript
2020-12-27 17:25:44 +01:00
io.use(
socketioJwt.authorize({
secret: 'your secret or public key',
handshake: true
})
)
2014-01-13 22:41:10 +01:00
2019-10-14 01:46:30 +02:00
io.on('connection', (socket) => {
2020-12-27 17:25:44 +01:00
console.log('hello!', socket.decoded_token.name)
})
2013-11-15 15:16:16 +01:00
```
2012-11-16 16:43:12 +01:00
2014-01-13 20:00:21 +01:00
For more validation options see [auth0/jsonwebtoken ](https://github.com/auth0/node-jsonwebtoken ).
2012-11-16 16:43:12 +01:00
2019-01-15 17:43:44 +01:00
**Client side**
2013-11-15 15:16:16 +01:00
2014-01-13 22:41:10 +01:00
Append the jwt token using query string:
2013-11-19 10:52:36 +01:00
```javascript
2019-10-13 15:52:14 +02:00
const socket = io.connect('http://localhost:9000', {
2019-10-14 01:46:30 +02:00
query: `token=${your_jwt}`
2020-12-27 17:25:44 +01:00
})
2013-11-19 10:52:36 +01:00
```
2019-07-24 16:40:37 +02:00
Append the jwt token using 'Authorization Header' (Bearer Token):
```javascript
2019-10-13 15:52:14 +02:00
const socket = io.connect('http://localhost:9000', {
2019-10-14 01:46:30 +02:00
extraHeaders: { Authorization: `Bearer ${your_jwt}` }
2020-12-27 17:25:44 +01:00
})
2019-07-24 16:40:37 +02:00
```
Both options can be combined or used optionally.
2016-10-20 18:18:40 +02:00
### Authorization Header Requirement
Require Bearer Tokens to be passed in as an Authorization Header
**Server side**:
```javascript
2020-12-27 17:25:44 +01:00
io.use(
socketioJwt.authorize({
secret: 'your secret or public key',
handshake: true,
auth_header_required: true
})
)
2016-10-20 18:18:40 +02:00
```
2019-01-15 17:43:44 +01:00
### Handling token expiration
2014-09-04 07:47:17 +02:00
2019-01-15 17:43:44 +01:00
**Server side**
2014-09-04 07:47:17 +02:00
2019-07-24 16:40:37 +02:00
When you sign the token with an expiration time (example: 60 minutes):
2014-09-04 07:47:17 +02:00
```javascript
2020-12-27 17:25:44 +01:00
const token = jwt.sign(user_profile, jwt_secret, { expiresIn: 60 * 60 })
2014-09-04 07:47:17 +02:00
```
2019-01-15 17:43:44 +01:00
Your client-side code should handle it as below:
2014-09-04 07:47:17 +02:00
2019-01-15 17:43:44 +01:00
**Client side**
2014-09-04 07:47:17 +02:00
```javascript
2019-10-14 01:46:30 +02:00
socket.on('unauthorized', (error) => {
2020-12-27 17:25:44 +01:00
if (
error.data.type == 'UnauthorizedError' ||
error.data.code == 'invalid_token'
) {
2014-09-04 07:47:17 +02:00
// redirect user to login page perhaps?
2020-12-27 17:25:44 +01:00
console.log('User token has expired')
2014-09-04 07:47:17 +02:00
}
2020-12-27 17:25:44 +01:00
})
2015-11-18 21:36:24 +01:00
```
2015-12-26 05:17:01 +01:00
2019-01-15 17:43:44 +01:00
### Handling invalid token
2015-12-26 05:17:01 +01:00
Token sent by client is invalid.
2019-01-15 17:43:44 +01:00
**Server side**:
2015-12-26 05:17:01 +01:00
No further configuration needed.
2019-01-15 17:43:44 +01:00
**Client side**
2015-12-26 05:17:01 +01:00
Add a callback client-side to execute socket disconnect server-side.
```javascript
2019-10-14 01:46:30 +02:00
socket.on('unauthorized', (error, callback) => {
2020-12-27 17:25:44 +01:00
if (
error.data.type == 'UnauthorizedError' ||
error.data.code == 'invalid_token'
) {
2015-12-26 05:17:01 +01:00
// redirect user to login page perhaps or execute callback:
2020-12-27 17:25:44 +01:00
callback()
console.log('User token has expired')
2015-12-26 05:17:01 +01:00
}
2020-12-27 17:25:44 +01:00
})
2015-12-26 05:17:01 +01:00
```
2019-01-15 17:43:44 +01:00
**Server side**
2015-12-26 05:17:01 +01:00
To disconnect socket server-side without client-side callback:
```javascript
2020-12-27 17:25:44 +01:00
io.sockets.on(
'connection',
socketioJwt.authorize({
secret: 'secret goes here',
// No client-side callback, terminate connection server-side
callback: false
})
)
2015-12-26 05:17:01 +01:00
```
2019-01-15 17:43:44 +01:00
**Client side**
2015-12-26 05:17:01 +01:00
Nothing needs to be changed client-side if callback is false.
2019-01-15 17:43:44 +01:00
**Server side**
2015-12-26 05:17:01 +01:00
To disconnect socket server-side while giving client-side 15 seconds to execute callback:
```javascript
2020-12-27 17:25:44 +01:00
io.sockets.on(
'connection',
socketioJwt.authorize({
secret: 'secret goes here',
// Delay server-side socket disconnect to wait for client-side callback
callback: 15000
})
)
2015-12-26 05:17:01 +01:00
```
2019-01-15 17:43:44 +01:00
Your client-side code should handle it as below:
2015-12-26 05:17:01 +01:00
2019-01-15 17:43:44 +01:00
**Client side**
2015-12-26 05:17:01 +01:00
```javascript
2019-10-14 01:46:30 +02:00
socket.on('unauthorized', (error, callback) => {
2020-12-27 17:25:44 +01:00
if (
error.data.type == 'UnauthorizedError' ||
error.data.code == 'invalid_token'
) {
2015-12-26 05:17:01 +01:00
// redirect user to login page perhaps or execute callback:
2020-12-27 17:25:44 +01:00
callback()
console.log('User token has expired')
2015-12-26 05:17:01 +01:00
}
2020-12-27 17:25:44 +01:00
})
2015-12-26 05:17:01 +01:00
```
2019-01-15 17:43:44 +01:00
### Getting the secret dynamically
You can pass a function instead of a string when configuring secret.
2015-11-18 21:36:24 +01:00
This function receives the request, the decoded token and a callback. This
way, you are allowed to use a different secret based on the request and / or
the provided token.
2019-01-15 17:43:44 +01:00
**Server side**
2015-11-18 21:36:24 +01:00
```javascript
2019-10-13 15:52:14 +02:00
const SECRETS = {
2020-12-27 17:25:44 +01:00
user1: 'secret 1',
user2: 'secret 2'
2015-11-18 21:36:24 +01:00
}
2020-12-27 17:25:44 +01:00
io.use(
socketioJwt.authorize({
secret: (request, decodedToken, callback) => {
// SECRETS[decodedToken.userId] will be used as a secret or
// public key for connection user.
2015-11-18 21:36:24 +01:00
2020-12-27 17:25:44 +01:00
callback(null, SECRETS[decodedToken.userId])
},
handshake: false
})
)
2014-09-04 07:47:17 +02:00
```
2019-10-29 11:39:38 +01:00
### Altering the value of the decoded token
You can pass a function to change the value of the decoded token
```javascript
io.on(
'connection',
socketIOJwt.authorize({
customDecoded: (decoded) => {
2020-12-27 17:25:44 +01:00
return 'new decoded token'
2019-10-29 11:39:38 +01:00
},
secret: 'my_secret_key',
2020-12-27 17:25:44 +01:00
decodedPropertyName: 'my_decoded_token'
})
)
2019-10-29 11:39:38 +01:00
io.on('authenticated', (socket) => {
2020-12-27 17:25:44 +01:00
console.log(socket.my_decoded_token) // new decoded token
})
2019-10-29 11:39:38 +01:00
```
2020-12-27 17:50:47 +01:00
## 💡 Contributing
2020-12-27 17:25:44 +01:00
2020-12-27 17:50:47 +01:00
Anyone can help to improve the project, submit a Feature Request, a bug report or even correct a simple spelling mistake.
2020-12-27 17:25:44 +01:00
2020-12-27 17:50:47 +01:00
The steps to contribute can be found in the [CONTRIBUTING.md ](./.github/CONTRIBUTING.md ) file.
2019-01-15 17:43:44 +01:00
2020-12-27 17:50:47 +01:00
## 📄 License
2019-01-15 17:43:44 +01:00
2020-12-27 17:50:47 +01:00
[MIT ](./LICENSE )