2
1
mirror of https://github.com/Thream/socketio-jwt.git synced 2024-07-04 02:30:11 +02:00

chore: initial commit

This commit is contained in:
divlo 2020-12-27 17:25:44 +01:00
parent 04294c69c5
commit 2e5d281f46
27 changed files with 813 additions and 740 deletions

View File

@ -1,60 +0,0 @@
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "beardaway",
"name": "Conrad Sopala",
"avatar_url": "https://avatars3.githubusercontent.com/u/11062800?v=4",
"profile": "https://twitter.com/beardaway",
"contributions": [
"review",
"maintenance"
]
},
{
"login": "Annyv2",
"name": "Annyv2",
"avatar_url": "https://avatars3.githubusercontent.com/u/5016479?v=4",
"profile": "https://github.com/Annyv2",
"contributions": [
"code"
]
},
{
"login": "Amialc",
"name": "Vladyslav Martynets",
"avatar_url": "https://avatars0.githubusercontent.com/u/1114365?v=4",
"profile": "https://github.com/Amialc",
"contributions": [
"code"
]
},
{
"login": "pose",
"name": "Alberto Pose",
"avatar_url": "https://avatars3.githubusercontent.com/u/419703?v=4",
"profile": "https://github.com/pose",
"contributions": [
"code"
]
},
{
"login": "Root-Core",
"name": "Root-Core",
"avatar_url": "https://avatars2.githubusercontent.com/u/5329652?v=4",
"profile": "https://github.com/Root-Core",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
"projectName": "auth0-socketio-jwt",
"projectOwner": "auth0-community",
"repoType": "github",
"repoHost": "https://github.com"
}

11
.editorconfig Normal file
View File

@ -0,0 +1,11 @@
# For more information see: https://editorconfig.org/
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
.idea
node_modules/*
node_modules/*

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
package-lock=false
save-exact=true

View File

@ -1,7 +0,0 @@
language: node_js
node_js:
- 4
- 8
- 10
- 12
- node

2
.yarnrc Normal file
View File

@ -0,0 +1,2 @@
install.no-lockfile true
save-exact true

View File

@ -1,27 +1,36 @@
# Changelog
## [4.0.1] - 2015-04-22
- [4cf0651] Minor. replace regexp for native comparisoon (`Nikita Gusakov`)
## [4.0.0] - 2015-04-22
- [7e53470] Updated to jsonwebtoken@5.0.0 (`Alberto Pose`)
## [3.0.0] - 2015-03-16
- [089bfe5] update jsonwebtoken dependency (`José F. Romaniello`)
- [dbb4e95] Merge pull request #21 from kennyki/patch-1 (`José F. Romaniello`)
- [b1e5530] Added example of handling token expiration (`Kenny Ki`)
## [2.3.5] - 2014-09-05
- [f357dd7] update jsonwebtoken (`José F. Romaniello`)
## [2.3.4] - 2014-07-16
- [2490770] Merge pull request #18 from yads/master (`José F. Romaniello`)
- [cae2123] test fixes (`Vadim Kazakov`)
## [2.3.3] - 2014-07-16
- [55d5e43] Merge branch 'yads-master' (`José F. Romaniello`)
- [2897f90] merge (`José F. Romaniello`)
- [1398434] add data to UnauthorizedError so that more information can be returned to client (`Vadim Kazakov`)
## [2.3.2] - 2014-07-16
- [9d5abf9] update jsonwebtoken module to fix security issue (`José F. Romaniello`)
- [870a274] update example (`José F. Romaniello`)
- [e9b8ea4] fix readme (`José F. Romaniello`)
@ -29,28 +38,34 @@
- [e6ea64d] Update README.md (`Mark Rendle`)
## [2.3.1] - 2014-06-09
- [fe39d2c] Merge pull request #12 from otothea/master (`José F. Romaniello`)
- [f072f91] update readme and fix #11 (`José F. Romaniello`)
- [29b3882] Make it look for both kinds of query (`Oscar`)
- [452cc19] req._query is now req.query (`Oscar`)
- [452cc19] req.\_query is now req.query (`Oscar`)
## [2.3.0] - 2014-06-05 (YANKED)
## [2.2.0] - 2014-06-05 (YANKED)
## [2.1.0] - 2014-06-03
- [e8380c1] add support for socket.io 1.0 (`José F. Romaniello`)
- [0577d07] missing parenthesis closes #7 (`José F. Romaniello`)
## [2.0.2] - 2014-03-20
- [9a9f7d0] added newest xtend to prevent deprecation warning from object-keys (`kjellski`)
- [9a58d94] add license, close #2 (`José F. Romaniello`)
- [8e567b9] fix #3 (`José F. Romaniello`)
## [2.0.1] - 2014-01-23
- [54a33c2] change user to decoded_token (`José F. Romaniello`)
- [e626188] add example (`José F. Romaniello`)
## [2.0.0] - 2014-01-14
- [b292ab7] change the API (`José F. Romaniello`)
- [b0f4354] add noqs method (`José F. Romaniello`)
- [14a34ae] initial commit after fork of passport-socketio (`José F. Romaniello`)
@ -64,14 +79,17 @@
- [49f35c3] Missing Paren in Example (`Jason Nichols`)
## [2.2.1] - 2014-01-13
- [efbef7a] move request to devDeps, closes #44 (`José F. Romaniello`)
## [2.2.0] - 2013-11-21
- [bd0980e] Merge pull request #36 from TeamSynergy/cors_workaround (`José F. Romaniello`)
- [1a3b3e1] step 2, updated readme (`Screeny`)
- [f31dc4a] step 1 (`Screeny`)
## [2.1.2] - 2013-11-18
- [599a614] fixed a security issue (`Amir`)
- [91750bb] Merge pull request #33 from TeamSynergy/master (`José F. Romaniello`)
- [2d257bf] Update README.md (`Screeny`)

View File

@ -4,13 +4,13 @@ If you are reporting a bug, please fill the sections below (if they are applicab
### Description
What are you reporting?
What are you reporting?
### Expected behaviour
Tell us what you think should happen.
### Actual behaviour
### Actual behaviour
Tell us what actually happens.
@ -32,5 +32,4 @@ Tell us what we should do to reproduce the issue.
Feel free to insert here any screenshots that you consider helpful in solving your issue.
**Filling this, you're helping yourself and repo maintainers in solving your issues quicker! Teamwork makes the dreamwork 🤜🏼🤛🏻**

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
MIT License
Copyright (c) 2015 Auth0, Inc. <support@auth0.com> (http://auth0.com)
Copyright (c) Auth0, Inc. <support@auth0.com> (http://auth0.com) and Thream contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

View File

@ -18,9 +18,9 @@ Please describe the tests that you ran to verify your changes. Provide any instr
**Test Configuration**
* Framework version:
* Language version:
* Browser version:
- Framework version:
- Language version:
- Browser version:
### Additional info

218
README.md
View File

@ -1,9 +1,9 @@
# socketio-jwt
[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors)
<img src="https://img.shields.io/badge/community-driven-brightgreen.svg"/> <br>
<img src="https://img.shields.io/badge/community-driven-brightgreen.svg"/> <br>
### Contributors
## Contributors
Thanks goes to these wonderful people who contribute(d) or maintain(ed) this repo ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
@ -38,14 +38,17 @@ npm install socketio-jwt
```javascript
// set authorization for socket.io
io.sockets
.on('connection', socketioJwt.authorize({
secret: 'your secret or public key',
timeout: 15000 // 15 seconds to send the authentication message
}))
.on(
'connection',
socketioJwt.authorize({
secret: 'your secret or public key',
timeout: 15000 // 15 seconds to send the authentication message
})
)
.on('authenticated', (socket) => {
//this socket is authenticated, we are good to handle more events from it.
console.log(`hello! ${socket.decoded_token.name}`);
});
console.log(`hello! ${socket.decoded_token.name}`)
})
```
**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')`
@ -53,7 +56,7 @@ io.sockets
**Client side**
```javascript
const socket = io.connect('http://localhost:9000');
const socket = io.connect('http://localhost:9000')
socket.on('connect', () => {
socket
.emit('authenticate', { token: jwt }) //send the jwt
@ -61,10 +64,10 @@ socket.on('connect', () => {
//do other things
})
.on('unauthorized', (msg) => {
console.log(`unauthorized: ${JSON.stringify(msg.data)}`);
throw new Error(msg.data.type);
console.log(`unauthorized: ${JSON.stringify(msg.data)}`)
throw new Error(msg.data.type)
})
});
})
```
### One roundtrip
@ -72,34 +75,39 @@ socket.on('connect', () => {
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.
```javascript
const io = require('socket.io')(server);
const socketioJwt = require('socketio-jwt');
const io = require('socket.io')(server)
const socketioJwt = require('socketio-jwt')
```
With socket.io < 1.0:
```javascript
io.set('authorization', socketioJwt.authorize({
secret: 'your secret or public key',
handshake: true
}));
io.set(
'authorization',
socketioJwt.authorize({
secret: 'your secret or public key',
handshake: true
})
)
io.on('connection', (socket) => {
console.log('hello!', socket.handshake.decoded_token.name);
});
console.log('hello!', socket.handshake.decoded_token.name)
})
```
With socket.io >= 1.0:
```javascript
io.use(socketioJwt.authorize({
secret: 'your secret or public key',
handshake: true
}));
io.use(
socketioJwt.authorize({
secret: 'your secret or public key',
handshake: true
})
)
io.on('connection', (socket) => {
console.log('hello!', socket.decoded_token.name);
});
console.log('hello!', socket.decoded_token.name)
})
```
For more validation options see [auth0/jsonwebtoken](https://github.com/auth0/node-jsonwebtoken).
@ -111,7 +119,7 @@ Append the jwt token using query string:
```javascript
const socket = io.connect('http://localhost:9000', {
query: `token=${your_jwt}`
});
})
```
Append the jwt token using 'Authorization Header' (Bearer Token):
@ -119,7 +127,7 @@ Append the jwt token using 'Authorization Header' (Bearer Token):
```javascript
const socket = io.connect('http://localhost:9000', {
extraHeaders: { Authorization: `Bearer ${your_jwt}` }
});
})
```
Both options can be combined or used optionally.
@ -131,11 +139,13 @@ Require Bearer Tokens to be passed in as an Authorization Header
**Server side**:
```javascript
io.use(socketioJwt.authorize({
secret: 'your secret or public key',
handshake: true,
auth_header_required: true
}));
io.use(
socketioJwt.authorize({
secret: 'your secret or public key',
handshake: true,
auth_header_required: true
})
)
```
### Handling token expiration
@ -145,7 +155,7 @@ io.use(socketioJwt.authorize({
When you sign the token with an expiration time (example: 60 minutes):
```javascript
const token = jwt.sign(user_profile, jwt_secret, { expiresIn: 60*60 });
const token = jwt.sign(user_profile, jwt_secret, { expiresIn: 60 * 60 })
```
Your client-side code should handle it as below:
@ -154,11 +164,14 @@ Your client-side code should handle it as below:
```javascript
socket.on('unauthorized', (error) => {
if (error.data.type == 'UnauthorizedError' || error.data.code == 'invalid_token') {
if (
error.data.type == 'UnauthorizedError' ||
error.data.code == 'invalid_token'
) {
// redirect user to login page perhaps?
console.log('User token has expired');
console.log('User token has expired')
}
});
})
```
### Handling invalid token
@ -175,12 +188,15 @@ Add a callback client-side to execute socket disconnect server-side.
```javascript
socket.on('unauthorized', (error, callback) => {
if (error.data.type == 'UnauthorizedError' || error.data.code == 'invalid_token') {
if (
error.data.type == 'UnauthorizedError' ||
error.data.code == 'invalid_token'
) {
// redirect user to login page perhaps or execute callback:
callback();
console.log('User token has expired');
callback()
console.log('User token has expired')
}
});
})
```
**Server side**
@ -188,11 +204,14 @@ socket.on('unauthorized', (error, callback) => {
To disconnect socket server-side without client-side callback:
```javascript
io.sockets.on('connection', socketioJwt.authorize({
secret: 'secret goes here',
// No client-side callback, terminate connection server-side
callback: false
}))
io.sockets.on(
'connection',
socketioJwt.authorize({
secret: 'secret goes here',
// No client-side callback, terminate connection server-side
callback: false
})
)
```
**Client side**
@ -204,11 +223,14 @@ Nothing needs to be changed client-side if callback is false.
To disconnect socket server-side while giving client-side 15 seconds to execute callback:
```javascript
io.sockets.on('connection', socketioJwt.authorize({
secret: 'secret goes here',
// Delay server-side socket disconnect to wait for client-side callback
callback: 15000
}))
io.sockets.on(
'connection',
socketioJwt.authorize({
secret: 'secret goes here',
// Delay server-side socket disconnect to wait for client-side callback
callback: 15000
})
)
```
Your client-side code should handle it as below:
@ -217,12 +239,15 @@ Your client-side code should handle it as below:
```javascript
socket.on('unauthorized', (error, callback) => {
if (error.data.type == 'UnauthorizedError' || error.data.code == 'invalid_token') {
if (
error.data.type == 'UnauthorizedError' ||
error.data.code == 'invalid_token'
) {
// redirect user to login page perhaps or execute callback:
callback();
console.log('User token has expired');
callback()
console.log('User token has expired')
}
});
})
```
### Getting the secret dynamically
@ -236,19 +261,21 @@ the provided token.
```javascript
const SECRETS = {
'user1': 'secret 1',
'user2': 'secret 2'
user1: 'secret 1',
user2: 'secret 2'
}
io.use(socketioJwt.authorize({
secret: (request, decodedToken, callback) => {
// SECRETS[decodedToken.userId] will be used as a secret or
// public key for connection user.
io.use(
socketioJwt.authorize({
secret: (request, decodedToken, callback) => {
// SECRETS[decodedToken.userId] will be used as a secret or
// public key for connection user.
callback(null, SECRETS[decodedToken.userId]);
},
handshake: false
}));
callback(null, SECRETS[decodedToken.userId])
},
handshake: false
})
)
```
### Altering the value of the decoded token
@ -256,22 +283,20 @@ io.use(socketioJwt.authorize({
You can pass a function to change the value of the decoded token
```javascript
io.on(
'connection',
socketIOJwt.authorize({
customDecoded: (decoded) => {
return "new decoded token";
return 'new decoded token'
},
secret: 'my_secret_key',
decodedPropertyName: 'my_decoded_token',
}),
);
decodedPropertyName: 'my_decoded_token'
})
)
io.on('authenticated', (socket) => {
console.log(socket.my_decoded_token); // new decoded token
});
console.log(socket.my_decoded_token) // new decoded token
})
```
## Contribute
@ -281,6 +306,7 @@ Feel like contributing to this repo? We're glad to hear that! Before you start c
Here you can also find the [PR template](https://github.com/auth0-community/socketio-jwt/blob/master/PULL_REQUEST_TEMPLATE.md) to fill once creating a PR. It will automatically appear once you open a pull request.
You might run the unit tests, before creating a PR:
```bash
npm test
```
@ -307,31 +333,33 @@ This project is licensed under the MIT license. See the [LICENSE](https://github
Auth0 helps you to:
* Add authentication with [multiple authentication sources](https://docs.auth0.com/identityproviders), either social like
* Google
* Facebook
* Microsoft
* Linkedin
* GitHub
* Twitter
* Box
* Salesforce
* etc.
- Add authentication with [multiple authentication sources](https://docs.auth0.com/identityproviders), either social like
- Google
- Facebook
- Microsoft
- Linkedin
- GitHub
- Twitter
- Box
- Salesforce
- etc.
**or** enterprise identity systems like:
* Windows Azure AD
* Google Apps
* Active Directory
* ADFS
* Any SAML Identity Provider
* Add authentication through more traditional [username/password databases](https://docs.auth0.com/mysql-connection-tutorial)
* Add support for [linking different user accounts](https://docs.auth0.com/link-accounts) with the same user
* Support for generating signed [JSON Web Tokens](https://docs.auth0.com/jwt) to call your APIs and create user identity flow securely
* Analytics of how, when and where users are logging in
* Pull data from other sources and add it to user profile, through [JavaScript rules](https://docs.auth0.com/rules)
- Windows Azure AD
- Google Apps
- Active Directory
- ADFS
- Any SAML Identity Provider
- Add authentication through more traditional [username/password databases](https://docs.auth0.com/mysql-connection-tutorial)
- Add support for [linking different user accounts](https://docs.auth0.com/link-accounts) with the same user
- Support for generating signed [JSON Web Tokens](https://docs.auth0.com/jwt) to call your APIs and create user identity flow securely
- Analytics of how, when and where users are logging in
- Pull data from other sources and add it to user profile, through [JavaScript rules](https://docs.auth0.com/rules)
## Create a free Auth0 account
* Go to [Auth0 website](https://auth0.com/signup)
* Hit the **SIGN UP** button in the upper-right corner
- Go to [Auth0 website](https://auth0.com/signup)
- Hit the **SIGN UP** button in the upper-right corner

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

1
example/.gitignore vendored
View File

@ -1,2 +1 @@
node_modules

View File

@ -1,3 +1,3 @@
# Deprecation Notice
This sample has been deprecated. Please see the [auth0-samples](https://github.com/auth0-samples) org for the latest Auth0 samples.
This sample has been deprecated. Please see the [auth0-samples](https://github.com/auth0-samples) org for the latest Auth0 samples.

View File

@ -1,15 +1,15 @@
function UnauthorizedError (code, error) {
Error.call(this, error.message);
this.message = error.message;
this.inner = error;
Error.call(this, error.message)
this.message = error.message
this.inner = error
this.data = {
message: this.message,
code: code,
type: 'UnauthorizedError'
};
}
}
UnauthorizedError.prototype = Object.create(Error.prototype);
UnauthorizedError.prototype.constructor = UnauthorizedError;
UnauthorizedError.prototype = Object.create(Error.prototype)
UnauthorizedError.prototype.constructor = UnauthorizedError
module.exports = UnauthorizedError;
module.exports = UnauthorizedError

View File

@ -1,264 +1,301 @@
const xtend = require('xtend');
const jwt = require('jsonwebtoken');
const UnauthorizedError = require('./UnauthorizedError');
const xtend = require('xtend')
const jwt = require('jsonwebtoken')
const UnauthorizedError = require('./UnauthorizedError')
function noQsMethod (options) {
const defaults = { required: true };
options = xtend(defaults, options);
const defaults = { required: true }
options = xtend(defaults, options)
return (socket) => {
'use strict'; // Node 4.x workaround
const server = this.server || socket.server;
'use strict' // Node 4.x workaround
const server = this.server || socket.server
if (!server.$emit) {
//then is socket.io 1.0
const Namespace = Object.getPrototypeOf(server.sockets).constructor;
const Namespace = Object.getPrototypeOf(server.sockets).constructor
if (!~Namespace.events.indexOf('authenticated')) {
Namespace.events.push('authenticated');
Namespace.events.push('authenticated')
}
}
let auth_timeout = null;
let auth_timeout = null
if (options.required) {
auth_timeout = setTimeout(() => {
socket.disconnect('unauthorized');
}, options.timeout || 5000);
socket.disconnect('unauthorized')
}, options.timeout || 5000)
}
socket.on('authenticate', (data) => {
if (options.required) {
clearTimeout(auth_timeout);
clearTimeout(auth_timeout)
}
// error handler
const onError = (err, code) => {
if (err) {
code = code || 'unknown';
code = code || 'unknown'
const error = new UnauthorizedError(code, {
message: (Object.prototype.toString.call(err) === '[object Object]' && err.message) ? err.message : err
});
message:
Object.prototype.toString.call(err) === '[object Object]' &&
err.message
? err.message
: err
})
let callback_timeout;
let callback_timeout
// If callback explicitly set to false, start timeout to disconnect socket
if (options.callback === false || typeof options.callback === 'number') {
if (
options.callback === false ||
typeof options.callback === 'number'
) {
if (typeof options.callback === 'number') {
if (options.callback < 0) {
// If callback is negative(invalid value), make it positive
options.callback = Math.abs(options.callback);
options.callback = Math.abs(options.callback)
}
}
callback_timeout = setTimeout(() => {
socket.disconnect('unauthorized');
}, (options.callback === false ? 0 : options.callback));
callback_timeout = setTimeout(
() => {
socket.disconnect('unauthorized')
},
options.callback === false ? 0 : options.callback
)
}
socket.emit('unauthorized', error, () => {
if (typeof options.callback === 'number') {
clearTimeout(callback_timeout);
clearTimeout(callback_timeout)
}
socket.disconnect('unauthorized');
});
return; // stop logic, socket will be close on next tick
socket.disconnect('unauthorized')
})
return // stop logic, socket will be close on next tick
}
};
}
const token = options.cookie ? socket.request.cookies[options.cookie] : (data ? data.token : undefined);
const token = options.cookie
? socket.request.cookies[options.cookie]
: data
? data.token
: undefined
if (!token || typeof token !== 'string') {
return onError({ message: 'invalid token datatype' }, 'invalid_token');
return onError({ message: 'invalid token datatype' }, 'invalid_token')
}
// Store encoded JWT
socket[options.encodedPropertyName] = token;
socket[options.encodedPropertyName] = token
const onJwtVerificationReady = (err, decoded) => {
if (err) {
return onError(err, 'invalid_token');
return onError(err, 'invalid_token')
}
// success handler
const onSuccess = () => {
socket[options.decodedPropertyName] = options.customDecoded
? options.customDecoded(decoded)
: decoded;
socket.emit('authenticated');
: decoded
socket.emit('authenticated')
if (server.$emit) {
server.$emit('authenticated', socket);
server.$emit('authenticated', socket)
} else {
//try getting the current namespace otherwise fallback to all sockets.
const namespace = (server.nsps && socket.nsp &&
server.nsps[socket.nsp.name]) ||
server.sockets;
const namespace =
(server.nsps && socket.nsp && server.nsps[socket.nsp.name]) ||
server.sockets
// explicit namespace
namespace.emit('authenticated', socket);
namespace.emit('authenticated', socket)
}
};
if (options.additional_auth && typeof options.additional_auth === 'function') {
options.additional_auth(decoded, onSuccess, onError);
} else {
onSuccess();
}
};
if (
options.additional_auth &&
typeof options.additional_auth === 'function'
) {
options.additional_auth(decoded, onSuccess, onError)
} else {
onSuccess()
}
}
const onSecretReady = (err, secret) => {
if (err || !secret) {
return onError(err, 'invalid_secret');
return onError(err, 'invalid_secret')
}
jwt.verify(token, secret, options, onJwtVerificationReady);
};
jwt.verify(token, secret, options, onJwtVerificationReady)
}
getSecret(socket.request, options.secret, token, onSecretReady);
});
};
getSecret(socket.request, options.secret, token, onSecretReady)
})
}
}
function authorize (options) {
options = xtend({ decodedPropertyName: 'decoded_token', encodedPropertyName: 'encoded_token' }, options);
options = xtend(
{
decodedPropertyName: 'decoded_token',
encodedPropertyName: 'encoded_token'
},
options
)
if (typeof options.secret !== 'string' && typeof options.secret !== 'function') {
throw new Error(`Provided secret ${options.secret} is invalid, must be of type string or function.`);
if (
typeof options.secret !== 'string' &&
typeof options.secret !== 'function'
) {
throw new Error(
`Provided secret ${options.secret} is invalid, must be of type string or function.`
)
}
if (!options.handshake) {
return noQsMethod(options);
return noQsMethod(options)
}
const defaults = {
success: (socket, accept) => {
if (socket.request) {
accept();
accept()
} else {
accept(null, true);
accept(null, true)
}
},
fail: (error, socket, accept) => {
if (socket.request) {
accept(error);
accept(error)
} else {
accept(null, false);
accept(null, false)
}
}
};
}
const auth = xtend(defaults, options);
const auth = xtend(defaults, options)
return (socket, accept) => {
'use strict'; // Node 4.x workaround
let token, error;
'use strict' // Node 4.x workaround
let token, error
const handshake = socket.handshake;
const req = socket.request || socket;
const authorization_header = (req.headers || {}).authorization;
const handshake = socket.handshake
const req = socket.request || socket
const authorization_header = (req.headers || {}).authorization
if (authorization_header) {
const parts = authorization_header.split(' ');
const parts = authorization_header.split(' ')
if (parts.length == 2) {
const scheme = parts[0],
credentials = parts[1];
const scheme = parts[0],
credentials = parts[1]
if (scheme.toLowerCase() === 'bearer') {
token = credentials;
token = credentials
}
} else {
error = new UnauthorizedError('credentials_bad_format', {
message: 'Format is Authorization: Bearer [token]'
});
return auth.fail(error, socket, accept);
})
return auth.fail(error, socket, accept)
}
}
// Check if the header has to include authentication
if (options.auth_header_required && !token) {
return auth.fail(new UnauthorizedError('missing_authorization_header', {
message: 'Server requires Authorization Header'
}), socket, accept);
return auth.fail(
new UnauthorizedError('missing_authorization_header', {
message: 'Server requires Authorization Header'
}),
socket,
accept
)
}
// Get the token from handshake or query string
if (handshake && handshake.query.token) {
token = handshake.query.token;
}
else if (req._query && req._query.token) {
token = req._query.token;
}
else if (req.query && req.query.token) {
token = req.query.token;
token = handshake.query.token
} else if (req._query && req._query.token) {
token = req._query.token
} else if (req.query && req.query.token) {
token = req.query.token
}
if (!token) {
error = new UnauthorizedError('credentials_required', {
message: 'no token provided'
});
return auth.fail(error, socket, accept);
})
return auth.fail(error, socket, accept)
}
// Store encoded JWT
socket[options.encodedPropertyName] = token;
socket[options.encodedPropertyName] = token
const onJwtVerificationReady = (err, decoded) => {
if (err) {
error = new UnauthorizedError(err.code || 'invalid_token', err);
return auth.fail(error, socket, accept);
error = new UnauthorizedError(err.code || 'invalid_token', err)
return auth.fail(error, socket, accept)
}
socket[options.decodedPropertyName] = options.customDecoded
? options.customDecoded(decoded)
: decoded;
: decoded
return auth.success(socket, accept);
};
return auth.success(socket, accept)
}
const onSecretReady = (err, secret) => {
if (err) {
error = new UnauthorizedError(err.code || 'invalid_secret', err);
return auth.fail(error, socket, accept);
error = new UnauthorizedError(err.code || 'invalid_secret', err)
return auth.fail(error, socket, accept)
}
jwt.verify(token, secret, options, onJwtVerificationReady);
};
getSecret(req, options.secret, token, onSecretReady);
};
}
function getSecret (request, secret, token, callback) {
'use strict'; // Node 4.x workaround
if (typeof secret === 'function') {
if (!token) {
return callback({ code: 'invalid_token', message: 'jwt must be provided' });
jwt.verify(token, secret, options, onJwtVerificationReady)
}
const parts = token.split('.');
if (parts.length < 3) {
return callback({ code: 'invalid_token', message: 'jwt malformed' });
}
if (parts[2].trim() === '') {
return callback({ code: 'invalid_token', message: 'jwt signature is required' });
}
let decodedToken = jwt.decode(token, { complete: true });
if (!decodedToken) {
return callback({ code: 'invalid_token', message: 'jwt malformed' });
}
const arity = secret.length;
if (arity == 4) {
secret(request, decodedToken.header, decodedToken.payload, callback);
} else { // arity == 3
secret(request, decodedToken.payload, callback);
}
} else {
callback(null, secret);
getSecret(req, options.secret, token, onSecretReady)
}
}
exports.authorize = authorize;
exports.UnauthorizedError = UnauthorizedError;
function getSecret (request, secret, token, callback) {
'use strict' // Node 4.x workaround
if (typeof secret === 'function') {
if (!token) {
return callback({
code: 'invalid_token',
message: 'jwt must be provided'
})
}
const parts = token.split('.')
if (parts.length < 3) {
return callback({ code: 'invalid_token', message: 'jwt malformed' })
}
if (parts[2].trim() === '') {
return callback({
code: 'invalid_token',
message: 'jwt signature is required'
})
}
let decodedToken = jwt.decode(token, { complete: true })
if (!decodedToken) {
return callback({ code: 'invalid_token', message: 'jwt malformed' })
}
const arity = secret.length
if (arity == 4) {
secret(request, decodedToken.header, decodedToken.payload, callback)
} else {
// arity == 3
secret(request, decodedToken.payload, callback)
}
} else {
callback(null, secret)
}
}
exports.authorize = authorize
exports.UnauthorizedError = UnauthorizedError

View File

@ -23,20 +23,19 @@
},
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^8.3.0",
"xtend": "~2.1.2"
"jsonwebtoken": "8.3.0",
"xtend": "2.1.2"
},
"devDependencies": {
"@types/socket.io": "~1.4.29",
"body-parser": "~1.17.1",
"express": "~4.15.2",
"mocha": "~3.2.0",
"request": "~2.81.0",
"serve-static": "^1.13.2",
"q": "^1.5.1",
"server-destroy": "~1.0.1",
"should": "~11.2.1",
"socket.io": "^1.7.3",
"socket.io-client": "^1.7.3"
"@types/socket.io": "2.1.12",
"express": "4.17.1",
"mocha": "3.2.0",
"request": "2.81.0",
"serve-static": "1.13.2",
"q": "1.5.1",
"server-destroy": "1.0.1",
"should": "11.2.1",
"socket.io": "2.3.0",
"socket.io-client": "2.3.0"
}
}

View File

@ -1,141 +1,148 @@
const Q = require('q');
const fixture = require('./fixture');
const request = require('request');
const io = require('socket.io-client');
const Q = require('q')
const fixture = require('./fixture')
const request = require('request')
const io = require('socket.io-client')
describe('authorizer', () => {
//start and stop the server
before((done) => { fixture.start({ }, done) });
after(fixture.stop);
before((done) => {
fixture.start({}, done)
})
after(fixture.stop)
describe('when the user is not logged in', () => {
it('should emit error with unauthorized handshake', (done) => {
const socket = io.connect('http://localhost:9000?token=boooooo', {
forceNew: true
});
})
socket.on('error', (err) => {
err.message.should.eql('jwt malformed');
err.code.should.eql('invalid_token');
socket.close();
done();
});
});
});
err.message.should.eql('jwt malformed')
err.code.should.eql('invalid_token')
socket.close()
done()
})
})
})
describe('when the user is logged in', () => {
before((done) => {
request.post({
url: 'http://localhost:9000/login',
form: { username: 'jose', password: 'Pa123' },
json: true
}, (err, resp, body) => {
this.token = body.token;
done();
});
});
request.post(
{
url: 'http://localhost:9000/login',
form: { username: 'jose', password: 'Pa123' },
json: true
},
(err, resp, body) => {
this.token = body.token
done()
}
)
})
describe('authorizer disallows query string token when specified in startup options', () => {
before((done) => {
Q.ninvoke(fixture, 'stop')
.then(() => Q.ninvoke(fixture, 'start', { auth_header_required: true }))
.done(done);
});
.then(() =>
Q.ninvoke(fixture, 'start', { auth_header_required: true })
)
.done(done)
})
after((done) => {
Q.ninvoke(fixture, 'stop')
.then(() => Q.ninvoke(fixture, 'start', { }))
.done(done);
});
it('auth headers are supported', (done) => {
const socket = io.connect('http://localhost:9000', {
forceNew: true,
extraHeaders: { Authorization: 'Bearer ' + this.token}
});
socket
.on('connect', () => {
socket.close();
done();
})
.on('error', done);
});
it('auth token in query string is disallowed', (done) => {
const socket = io.connect('http://localhost:9000', {
forceNew: true,
query: 'token=' + this.token
});
socket.on('error', (err) => {
err.message.should.eql('Server requires Authorization Header');
err.code.should.eql('missing_authorization_header');
socket.close();
done();
});
});
})
describe('authorizer all auth types allowed', () => {
before((done) => {
Q.ninvoke(fixture, 'stop')
.then(() => Q.ninvoke(fixture, 'start', {}))
.done(done);
.done(done)
})
it('auth headers are supported', (done) => {
const socket = io.connect('http://localhost:9000', {
forceNew: true,
extraHeaders: { Authorization: 'Bearer ' + this.token }
});
})
socket
.on('connect', () => {
socket.close();
done();
socket.close()
done()
})
.on('error', done);
});
.on('error', done)
})
it('auth token in query string is disallowed', (done) => {
const socket = io.connect('http://localhost:9000', {
forceNew: true,
query: 'token=' + this.token
})
socket.on('error', (err) => {
err.message.should.eql('Server requires Authorization Header')
err.code.should.eql('missing_authorization_header')
socket.close()
done()
})
})
})
describe('authorizer all auth types allowed', () => {
before((done) => {
Q.ninvoke(fixture, 'stop')
.then(() => Q.ninvoke(fixture, 'start', {}))
.done(done)
})
it('auth headers are supported', (done) => {
const socket = io.connect('http://localhost:9000', {
forceNew: true,
extraHeaders: { Authorization: 'Bearer ' + this.token }
})
socket
.on('connect', () => {
socket.close()
done()
})
.on('error', done)
})
it('should do the handshake and connect', (done) => {
const socket = io.connect('http://localhost:9000', {
forceNew: true,
query: 'token=' + this.token
});
})
socket
.on('connect', () => {
socket.close();
done();
socket.close()
done()
})
.on('error', done);
});
});
});
.on('error', done)
})
})
})
describe('unsigned token', () => {
beforeEach(() => {
this.token = 'eyJhbGciOiJub25lIiwiY3R5IjoiSldUIn0.eyJuYW1lIjoiSm9obiBGb28ifQ.';
});
this.token =
'eyJhbGciOiJub25lIiwiY3R5IjoiSldUIn0.eyJuYW1lIjoiSm9obiBGb28ifQ.'
})
it('should not do the handshake and connect', (done) => {
const socket = io.connect('http://localhost:9000', {
forceNew: true,
query: 'token=' + this.token
});
})
socket
.on('connect', () => {
socket.close();
done(new Error('this shouldnt happen'));
socket.close()
done(new Error('this shouldnt happen'))
})
.on('error', (err) => {
socket.close();
err.message.should.eql('jwt signature is required');
done();
});
});
});
});
socket.close()
err.message.should.eql('jwt signature is required')
done()
})
})
})
})

View File

@ -1,91 +1,108 @@
const fixture = require('./fixture/namespace');
const request = require('request');
const io = require('socket.io-client');
const fixture = require('./fixture/namespace')
const request = require('request')
const io = require('socket.io-client')
describe('authorizer with namespaces', () => {
//start and stop the server
before(fixture.start);
after(fixture.stop);
before(fixture.start)
after(fixture.stop)
describe('when the user is not logged in', () => {
it('should be able to connect to the default namespace', (done) => {
io.connect('http://localhost:9000')
.once('hi', () => done())
.on('error', done);
});
.on('error', done)
})
it('should not be able to connect to the admin namespace', (done) => {
io.connect('http://localhost:9000/admin')
.once('disconnect', () => done())
.once('hi admin', () => done(new Error('unauthenticated client was able to connect to the admin namespace')));
});
.once('hi admin', () =>
done(
new Error(
'unauthenticated client was able to connect to the admin namespace'
)
)
)
})
it('should not be able to connect to the admin_hs namespace', (done) => {
io.connect('http://localhost:9000/admin_hs')
.once('hi admin', () => done(new Error('unauthenticated client was able to connect to the admin_hs namespace')))
.once('hi admin', () =>
done(
new Error(
'unauthenticated client was able to connect to the admin_hs namespace'
)
)
)
.on('error', (err) => {
if (err === 'Invalid namespace') { // SocketIO throws this error, if auth failed
return;
if (err === 'Invalid namespace') {
// SocketIO throws this error, if auth failed
return
} else if (err && err.type == 'UnauthorizedError') {
done();
done()
} else {
done(err);
done(err)
}
});
});
});
})
})
})
describe('when the user is logged in', () => {
beforeEach((done) => {
request.post({
url: 'http://localhost:9000/login',
form: { username: 'jose', password: 'Pa123' },
json: true
}, (err, resp, body) => {
this.token = body.token;
done();
});
});
request.post(
{
url: 'http://localhost:9000/login',
form: { username: 'jose', password: 'Pa123' },
json: true
},
(err, resp, body) => {
this.token = body.token
done()
}
)
})
it('should do the authentication and connect', (done) => {
io.connect('http://localhost:9000/admin', { forceNew: true })
.on('hi admin', () => done())
.emit('authenticate', { token: this.token });
});
.emit('authenticate', { token: this.token })
})
it('should do the authentication and connect without "forceNew"', (done) => {
io.connect('http://localhost:9000/admin', { forceNew: false })
.on('hi admin', () => done())
.emit('authenticate', { token: this.token });
});
});
.emit('authenticate', { token: this.token })
})
})
describe('when the user is logged in via handshake', () => {
beforeEach((done) => {
request.post({
url: 'http://localhost:9000/login',
form: { username: 'jose', password: 'Pa123' },
json: true
}, (err, resp, body) => {
this.token = body.token;
done();
});
});
request.post(
{
url: 'http://localhost:9000/login',
form: { username: 'jose', password: 'Pa123' },
json: true
},
(err, resp, body) => {
this.token = body.token
done()
}
)
})
it('should do the handshake and connect', (done) => {
io.connect('http://localhost:9000/admin_hs', { forceNew: true, query: 'token=' + this.token })
.once('hi admin', () => done());
});
io.connect('http://localhost:9000/admin_hs', {
forceNew: true,
query: 'token=' + this.token
}).once('hi admin', () => done())
})
it('should do the handshake and connect without "forceNew"', (done) => {
io.connect('http://localhost:9000/admin_hs', { forceNew: false, query: 'token=' + this.token })
.once('hi admin', () => done());
});
});
});
io.connect('http://localhost:9000/admin_hs', {
forceNew: false,
query: 'token=' + this.token
}).once('hi admin', () => done())
})
})
})

View File

@ -1,57 +1,59 @@
const fixture = require('./fixture');
const request = require('request');
const io = require('socket.io-client');
const fixture = require('./fixture')
const request = require('request')
const io = require('socket.io-client')
describe('authorizer without querystring', () => {
//start and stop the server
before((done) => {
fixture.start({ handshake: false }, done);
});
fixture.start({ handshake: false }, done)
})
after(fixture.stop);
after(fixture.stop)
describe('when the user is not logged in', () => {
it('should close the connection after a timeout if no auth message is received', (done) => {
io.connect('http://localhost:9000', { forceNew: true })
.once('disconnect', () => done());
});
io.connect('http://localhost:9000', { forceNew: true }).once(
'disconnect',
() => done()
)
})
it('should not respond echo', (done) => {
io.connect('http://localhost:9000', { forceNew: true })
.on('echo-response', () => done(new Error('this should not happen')))
.emit('echo', { hi: 123 });
.emit('echo', { hi: 123 })
setTimeout(done, 1200);
});
});
setTimeout(done, 1200)
})
})
describe('when the user is logged in', () => {
beforeEach((done) => {
request.post({
url: 'http://localhost:9000/login',
form: { username: 'jose', password: 'Pa123' },
json: true
}, (err, resp, body) => {
this.token = body.token;
done();
});
});
request.post(
{
url: 'http://localhost:9000/login',
form: { username: 'jose', password: 'Pa123' },
json: true
},
(err, resp, body) => {
this.token = body.token
done()
}
)
})
it('should do the authentication and connect', (done) => {
const socket = io.connect('http://localhost:9000', { forceNew: true });
const socket = io.connect('http://localhost:9000', { forceNew: true })
socket
.on('echo-response', () => {
socket.close();
done();
socket.close()
done()
})
.on('authenticated', () => { socket.emit('echo'); })
.emit('authenticate', { token: this.token });
});
});
});
.on('authenticated', () => {
socket.emit('echo')
})
.emit('authenticate', { token: this.token })
})
})
})

View File

@ -1,63 +1,69 @@
const fixture = require('./fixture/secret_function');
const request = require('request');
const io = require('socket.io-client');
const fixture = require('./fixture/secret_function')
const request = require('request')
const io = require('socket.io-client')
describe('authorizer with secret function', () => {
//start and stop the server
before((done) => {
fixture.start({
handshake: false
}, done);
});
fixture.start(
{
handshake: false
},
done
)
})
after(fixture.stop);
after(fixture.stop)
describe('when the user is not logged in', () => {
describe('and when token is not valid', () => {
beforeEach((done) => {
request.post({
url: 'http://localhost:9000/login',
json: { username: 'invalid_signature', password: 'Pa123' }
}, (err, resp, body) => {
this.invalidToken = body.token;
done();
});
});
request.post(
{
url: 'http://localhost:9000/login',
json: { username: 'invalid_signature', password: 'Pa123' }
},
(err, resp, body) => {
this.invalidToken = body.token
done()
}
)
})
it('should emit unauthorized', (done) => {
io.connect('http://localhost:9000', { forceNew: true })
.on('unauthorized', () => done())
.emit('authenticate', { token: this.invalidToken + 'ass' })
});
});
});
})
})
})
describe('when the user is logged in', () => {
beforeEach((done) => {
request.post({
url: 'http://localhost:9000/login',
json: { username: 'valid_signature', password: 'Pa123' }
}, (err, resp, body) => {
this.token = body.token;
done();
});
});
request.post(
{
url: 'http://localhost:9000/login',
json: { username: 'valid_signature', password: 'Pa123' }
},
(err, resp, body) => {
this.token = body.token
done()
}
)
})
it('should do the authentication and connect', (done) => {
const socket = io.connect('http://localhost:9000', { forceNew: true });
const socket = io.connect('http://localhost:9000', { forceNew: true })
socket
.on('echo-response', () => {
socket.close();
done();
socket.close()
done()
})
.on('authenticated', () => { socket.emit('echo'); })
.emit('authenticate', { token: this.token });
});
});
});
.on('authenticated', () => {
socket.emit('echo')
})
.emit('authenticate', { token: this.token })
})
})
})

View File

@ -1,77 +1,78 @@
const fixture = require('./fixture/secret_function');
const request = require('request');
const io = require('socket.io-client');
const fixture = require('./fixture/secret_function')
const request = require('request')
const io = require('socket.io-client')
describe('authorizer with secret function', () => {
//start and stop the server
before(fixture.start);
after(fixture.stop);
before(fixture.start)
after(fixture.stop)
describe('when the user is not logged in', () => {
it('should emit error with unauthorized handshake', (done) => {
const socket = io.connect('http://localhost:9000?token=boooooo', { forceNew: true });
const socket = io.connect('http://localhost:9000?token=boooooo', {
forceNew: true
})
socket.on('error', (err) => {
err.message.should.eql('jwt malformed');
err.code.should.eql('invalid_token');
socket.close();
done();
});
});
});
err.message.should.eql('jwt malformed')
err.code.should.eql('invalid_token')
socket.close()
done()
})
})
})
describe('when the user is logged in', () => {
beforeEach((done) => {
request.post({
url: 'http://localhost:9000/login',
json: { username: 'valid_signature', password: 'Pa123' }
}, (err, resp, body) => {
this.token = body.token;
done();
});
});
request.post(
{
url: 'http://localhost:9000/login',
json: { username: 'valid_signature', password: 'Pa123' }
},
(err, resp, body) => {
this.token = body.token
done()
}
)
})
it('should do the handshake and connect', (done) => {
const socket = io.connect('http://localhost:9000', {
forceNew: true,
query: 'token=' + this.token
});
})
socket
.on('connect', () => {
socket.close();
done();
socket.close()
done()
})
.on('error', done);
});
});
.on('error', done)
})
})
describe('unsigned token', () => {
beforeEach(() => {
this.token = 'eyJhbGciOiJub25lIiwiY3R5IjoiSldUIn0.eyJuYW1lIjoiSm9obiBGb28ifQ.';
});
this.token =
'eyJhbGciOiJub25lIiwiY3R5IjoiSldUIn0.eyJuYW1lIjoiSm9obiBGb28ifQ.'
})
it('should not do the handshake and connect', (done) => {
const socket = io.connect('http://localhost:9000', {
forceNew: true,
query: 'token=' + this.token
});
})
socket
.on('connect', () => {
socket.close();
done(new Error('this shouldnt happen'));
socket.close()
done(new Error('this shouldnt happen'))
})
.on('error', (err) => {
socket.close();
err.message.should.eql('jwt signature is required');
done();
});
});
});
});
socket.close()
err.message.should.eql('jwt signature is required')
done()
})
})
})
})

View File

@ -1,78 +1,78 @@
'use strict'; // Node 4.x workaround
'use strict' // Node 4.x workaround
const express = require('express');
const http = require('http');
const express = require('express')
const http = require('http')
const socketIo = require('socket.io');
const socketio_jwt = require('../../lib');
const socketIo = require('socket.io')
const socketio_jwt = require('../../lib')
const jwt = require('jsonwebtoken');
const xtend = require('xtend');
const bodyParser = require('body-parser');
const enableDestroy = require('server-destroy');
const jwt = require('jsonwebtoken')
const xtend = require('xtend')
const enableDestroy = require('server-destroy')
let sio;
let sio
exports.start = (options, callback) => {
if (typeof options == 'function') {
callback = options;
options = {};
callback = options
options = {}
}
options = xtend({
secret: 'aaafoo super sercret',
timeout: 1000,
handshake: true
}, options);
options = xtend(
{
secret: 'aaafoo super sercret',
timeout: 1000,
handshake: true
},
options
)
const app = express();
const server = http.createServer(app);
sio = socketIo.listen(server);
const app = express()
const server = http.createServer(app)
sio = socketIo.listen(server)
app.use(bodyParser.json());
app.use(express.json())
app.post('/login', (req, res) => {
const profile = {
first_name: 'John',
last_name: 'Doe',
email: 'john@doe.com',
id: 123
};
}
// We are sending the profile inside the token
const token = jwt.sign(profile, options.secret, { expiresIn: 60*60*5 });
res.json({token: token});
});
const token = jwt.sign(profile, options.secret, { expiresIn: 60 * 60 * 5 })
res.json({ token: token })
})
if (options.handshake) {
sio.use(socketio_jwt.authorize(options));
sio.use(socketio_jwt.authorize(options))
sio.sockets.on('echo', (m) => {
sio.sockets.emit('echo-response', m);
});
sio.sockets.emit('echo-response', m)
})
} else {
sio.sockets
.on('connection', socketio_jwt.authorize(options))
.on('authenticated', (socket) => {
socket.on('echo', (m) => {
socket.emit('echo-response', m);
});
});
socket.emit('echo-response', m)
})
})
}
server.__sockets = [];
server.__sockets = []
server.on('connection', (c) => {
server.__sockets.push(c);
});
server.listen(9000, callback);
enableDestroy(server);
};
server.__sockets.push(c)
})
server.listen(9000, callback)
enableDestroy(server)
}
exports.stop = (callback) => {
sio.close();
sio.close()
try {
server.destroy();
server.destroy()
} catch (er) {}
callback();
};
callback()
}

View File

@ -1,17 +1,16 @@
'use strict'; // Node 4.x workaround
'use strict' // Node 4.x workaround
const express = require('express');
const http = require('http');
const express = require('express')
const http = require('http')
const socketIo = require('socket.io');
const socketio_jwt = require('../../lib');
const socketIo = require('socket.io')
const socketio_jwt = require('../../lib')
const jwt = require('jsonwebtoken');
const xtend = require('xtend');
const enableDestroy = require('server-destroy');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken')
const xtend = require('xtend')
const enableDestroy = require('server-destroy')
let sio;
let sio
/**
* This is an example server that shows how to do namespace authentication.
@ -19,62 +18,60 @@ let sio;
* The /admin namespace is protected by JWTs while the global namespace is public.
*/
exports.start = (callback) => {
const options = {
secret: 'aaafoo super sercret',
timeout: 1000,
handshake: false
};
}
const app = express();
const server = http.createServer(app);
sio = socketIo.listen(server);
const app = express()
const server = http.createServer(app)
sio = socketIo.listen(server)
app.use(bodyParser.json());
app.use(express.json())
app.post('/login', (req, res) => {
const profile = {
first_name: 'John',
last_name: 'Doe',
email: 'john@doe.com',
id: 123
};
}
// We are sending the profile inside the token
const token = jwt.sign(profile, options.secret, { expiresIn: 60*60*5 });
res.json({ token: token });
});
const token = jwt.sign(profile, options.secret, { expiresIn: 60 * 60 * 5 })
res.json({ token: token })
})
// Global namespace (public)
sio.on('connection', (socket) => {
socket.emit('hi');
});
socket.emit('hi')
})
// Second roundtrip
const admin_nsp = sio.of('/admin');
const admin_nsp = sio.of('/admin')
admin_nsp.on('connection', socketio_jwt.authorize(options))
admin_nsp
.on('connection', socketio_jwt.authorize(options))
.on('authenticated', (socket) => {
socket.emit('hi admin');
});
socket.emit('hi admin')
})
// One roundtrip
const admin_nsp_hs = sio.of('/admin_hs');
const admin_nsp_hs = sio.of('/admin_hs')
admin_nsp_hs.use(socketio_jwt.authorize(xtend(options, { handshake: true })));
admin_nsp_hs.use(socketio_jwt.authorize(xtend(options, { handshake: true })))
admin_nsp_hs.on('connection', (socket) => {
socket.emit('hi admin');
});
socket.emit('hi admin')
})
server.listen(9000, callback);
enableDestroy(server);
};
server.listen(9000, callback)
enableDestroy(server)
}
exports.stop = (callback) => {
sio.close();
sio.close()
try {
server.destroy();
server.destroy()
} catch (er) {}
callback();
};
callback()
}

View File

@ -1,85 +1,87 @@
'use strict'; // Node 4.x workaround
'use strict' // Node 4.x workaround
const express = require('express');
const http = require('http');
const express = require('express')
const http = require('http')
const socketIo = require('socket.io');
const socketio_jwt = require('../../lib');
const socketIo = require('socket.io')
const socketio_jwt = require('../../lib')
const jwt = require('jsonwebtoken');
const xtend = require('xtend');
const bodyParser = require('body-parser');
const enableDestroy = require('server-destroy');
const jwt = require('jsonwebtoken')
const xtend = require('xtend')
const enableDestroy = require('server-destroy')
let sio;
let sio
exports.start = (options, callback) => {
const SECRETS = {
123: 'aaafoo super sercret',
555: 'other'
};
if (typeof options == 'function') {
callback = options;
options = {};
}
options = xtend({
secret: (request, decodedToken, callback) => {
callback(null, SECRETS[decodedToken.id]);
if (typeof options == 'function') {
callback = options
options = {}
}
options = xtend(
{
secret: (request, decodedToken, callback) => {
callback(null, SECRETS[decodedToken.id])
},
timeout: 1000,
handshake: true
},
timeout: 1000,
handshake: true
}, options);
options
)
const app = express();
const server = http.createServer(app);
sio = socketIo.listen(server);
const app = express()
const server = http.createServer(app)
sio = socketIo.listen(server)
app.use(bodyParser.json());
app.use(express.json())
app.post('/login', (req, res) => {
const profile = {
first_name: 'John',
last_name: 'Doe',
email: 'john@doe.com',
id: req.body.username === 'valid_signature' ? 123 : 555
};
}
// We are sending the profile inside the token
const token = jwt.sign(profile, SECRETS[123], { expiresIn: 60*60*5 });
res.json({token: token});
});
const token = jwt.sign(profile, SECRETS[123], { expiresIn: 60 * 60 * 5 })
res.json({ token: token })
})
if (options.handshake) {
sio.use(socketio_jwt.authorize(options));
sio.use(socketio_jwt.authorize(options))
sio.sockets.on('echo', (m) => {
sio.sockets.emit('echo-response', m);
});
sio.sockets.emit('echo-response', m)
})
} else {
sio.sockets
.on('connection', socketio_jwt.authorize(options))
.on('authenticated', (socket) => {
socket.on('echo', (m) => {
socket.emit('echo-response', m);
});
});
socket.emit('echo-response', m)
})
})
}
server.__sockets = [];
server.__sockets = []
server.on('connection', (c) => {
server.__sockets.push(c);
});
server.__sockets.push(c)
})
server.listen(9000, callback);
enableDestroy(server);
};
server.listen(9000, callback)
enableDestroy(server)
}
exports.stop = (callback) => {
sio.close();
sio.close()
try {
server.destroy();
server.destroy()
} catch (er) {}
callback();
};
callback()
}

View File

@ -1,3 +1,3 @@
--require should
--reporter spec
--timeout 15000
--timeout 15000

63
types/index.d.ts vendored
View File

@ -6,68 +6,81 @@
/// <reference types="socket.io" />
declare module 'socketio-jwt' {
/**
* Defines possible errors for the secret-callback.
*/
interface ISocketIOError {
readonly code: string;
readonly message: string;
readonly code: string
readonly message: string
}
/**
* Callback gets called, if secret is given dynamically.
*/
interface ISocketCallback {
(err: ISocketIOError, success: string): void;
(err: ISocketIOError, success: string): void
}
interface ISocketIOMiddleware {
(socket: SocketIO.Socket, fn: (err?: any) => void): void;
(socket: SocketIO.Socket, fn: (err?: any) => void): void
}
interface IOptions {
additional_auth?: (decoded: object, onSuccess: () => void, onError: (err: (string | ISocketIOError), code: string) => void) => void;
customDecoded?: (decoded: object) => object;
additional_auth?: (
decoded: object,
onSuccess: () => void,
onError: (err: string | ISocketIOError, code: string) => void
) => void
customDecoded?: (decoded: object) => object
callback?: (false | number);
secret: (string | ((request: any, decodedToken: object, callback: ISocketCallback) => void));
callback?: false | number
secret:
| string
| ((
request: any,
decodedToken: object,
callback: ISocketCallback
) => void)
encodedPropertyName?: string;
decodedPropertyName?: string;
auth_header_required?: boolean;
handshake?: boolean;
required?: boolean;
timeout?: number;
cookie?: string;
encodedPropertyName?: string
decodedPropertyName?: string
auth_header_required?: boolean
handshake?: boolean
required?: boolean
timeout?: number
cookie?: string
}
function authorize(options: IOptions/*, onConnection: Function*/): ISocketIOMiddleware;
function authorize(
options: IOptions /*, onConnection: Function*/
): ISocketIOMiddleware
interface UnauthorizedError extends Error {
readonly message: string;
readonly inner: object;
readonly data: { message: string, code: string, type: 'UnauthorizedError' }
readonly message: string
readonly inner: object
readonly data: { message: string; code: string; type: 'UnauthorizedError' }
}
var UnauthorizedError: {
prototype: UnauthorizedError;
new (code: string, error: { message: string }): UnauthorizedError;
prototype: UnauthorizedError
new (code: string, error: { message: string }): UnauthorizedError
}
/**
* This is an augmented version of the SocketIO.Server.
* It knows the 'authenticated' event and should be extended in future.
* @see SocketIO.Server
* @see SocketIO.Server
*/
export interface JWTServer extends SocketIO.Server {
/**
* The event gets fired when a new connection is authenticated via JWT.
* @param event The event being fired: 'authenticated'
* @param listener A listener that should take one parameter of type Socket
* @return The default '/' Namespace
*/
on(event: ('authenticated' | string), listener: (socket: SocketIO.Socket) => void): SocketIO.Namespace;
on(
event: 'authenticated' | string,
listener: (socket: SocketIO.Socket) => void
): SocketIO.Namespace
}
}