From 2e5d281f4680461aa1d1dfa10f00228d4a889022 Mon Sep 17 00:00:00 2001 From: divlo Date: Sun, 27 Dec 2020 17:25:44 +0100 Subject: [PATCH] chore: initial commit --- .all-contributorsrc | 60 ---- .editorconfig | 11 + .gitignore | 2 +- .npmrc | 2 + .travis.yml | 7 - .yarnrc | 2 + CHANGELOG.md | 20 +- ISSUE_TEMPLATE.md | 5 +- LICENSE | 6 +- PULL_REQUEST_TEMPLATE.md | 6 +- README.md | 218 ++++++++------ assets/join_auth0_community_badge.png | Bin 10804 -> 0 bytes example/.gitignore | 1 - example/README.md | 2 +- lib/UnauthorizedError.js | 14 +- lib/index.js | 299 +++++++++++-------- package.json | 25 +- test/authorizer.test.js | 183 ++++++------ test/authorizer_namespaces.test.js | 125 ++++---- test/authorizer_noqs.test.js | 72 ++--- test/authorizer_secret_function_noqs.test.js | 86 +++--- test/authorizer_secret_function_qs.test.js | 89 +++--- test/fixture/index.js | 86 +++--- test/fixture/namespace.js | 75 +++-- test/fixture/secret_function.js | 92 +++--- test/mocha.opts | 2 +- types/index.d.ts | 63 ++-- 27 files changed, 813 insertions(+), 740 deletions(-) delete mode 100644 .all-contributorsrc create mode 100644 .editorconfig create mode 100644 .npmrc delete mode 100644 .travis.yml create mode 100644 .yarnrc delete mode 100644 assets/join_auth0_community_badge.png diff --git a/.all-contributorsrc b/.all-contributorsrc deleted file mode 100644 index 3f8e778..0000000 --- a/.all-contributorsrc +++ /dev/null @@ -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" -} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1103496 --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.gitignore b/.gitignore index a504cda..a72ade1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ .idea -node_modules/* \ No newline at end of file +node_modules/* diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..0ca8d2a --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +package-lock=false +save-exact=true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0173c35..0000000 --- a/.travis.yml +++ /dev/null @@ -1,7 +0,0 @@ -language: node_js -node_js: - - 4 - - 8 - - 10 - - 12 - - node diff --git a/.yarnrc b/.yarnrc new file mode 100644 index 0000000..4fc1bf6 --- /dev/null +++ b/.yarnrc @@ -0,0 +1,2 @@ +install.no-lockfile true +save-exact true diff --git a/CHANGELOG.md b/CHANGELOG.md index 69134e0..184d459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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`) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 22810c1..601edb9 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -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 🤜🏼🤛🏻** diff --git a/LICENSE b/LICENSE index 72de587..b33a1bf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -The MIT License (MIT) +MIT License -Copyright (c) 2015 Auth0, Inc. (http://auth0.com) +Copyright (c) Auth0, Inc. (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. \ No newline at end of file +SOFTWARE. diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index fa3bf58..06b9a63 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -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 diff --git a/README.md b/README.md index b5c9d15..f4395d2 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # socketio-jwt [![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors) -
+
-### 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 diff --git a/assets/join_auth0_community_badge.png b/assets/join_auth0_community_badge.png deleted file mode 100644 index 72c7058e6957a45db6d31b4562b88e2d40dbbaf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10804 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 { - '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 diff --git a/package.json b/package.json index 58d320c..f963d9b 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/test/authorizer.test.js b/test/authorizer.test.js index fd1b561..96d9980 100644 --- a/test/authorizer.test.js +++ b/test/authorizer.test.js @@ -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() + }) + }) + }) +}) diff --git a/test/authorizer_namespaces.test.js b/test/authorizer_namespaces.test.js index 98554b2..5ff4f92 100644 --- a/test/authorizer_namespaces.test.js +++ b/test/authorizer_namespaces.test.js @@ -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()); - }); - }); - -}); \ No newline at end of file + io.connect('http://localhost:9000/admin_hs', { + forceNew: false, + query: 'token=' + this.token + }).once('hi admin', () => done()) + }) + }) +}) diff --git a/test/authorizer_noqs.test.js b/test/authorizer_noqs.test.js index f9cc045..c2d92a1 100644 --- a/test/authorizer_noqs.test.js +++ b/test/authorizer_noqs.test.js @@ -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 }); - }); - }); - -}); \ No newline at end of file + .on('authenticated', () => { + socket.emit('echo') + }) + .emit('authenticate', { token: this.token }) + }) + }) +}) diff --git a/test/authorizer_secret_function_noqs.test.js b/test/authorizer_secret_function_noqs.test.js index 5470ebf..e711116 100644 --- a/test/authorizer_secret_function_noqs.test.js +++ b/test/authorizer_secret_function_noqs.test.js @@ -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 }) + }) + }) +}) diff --git a/test/authorizer_secret_function_qs.test.js b/test/authorizer_secret_function_qs.test.js index dd38d97..56476b9 100644 --- a/test/authorizer_secret_function_qs.test.js +++ b/test/authorizer_secret_function_qs.test.js @@ -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() + }) + }) + }) +}) diff --git a/test/fixture/index.js b/test/fixture/index.js index 2f77105..f569fc8 100644 --- a/test/fixture/index.js +++ b/test/fixture/index.js @@ -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() +} diff --git a/test/fixture/namespace.js b/test/fixture/namespace.js index 8f1b4bf..04c7dd0 100644 --- a/test/fixture/namespace.js +++ b/test/fixture/namespace.js @@ -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() +} diff --git a/test/fixture/secret_function.js b/test/fixture/secret_function.js index 1ee17cd..b6c55c8 100644 --- a/test/fixture/secret_function.js +++ b/test/fixture/secret_function.js @@ -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() +} diff --git a/test/mocha.opts b/test/mocha.opts index d82740e..81f4179 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,3 +1,3 @@ --require should --reporter spec ---timeout 15000 \ No newline at end of file +--timeout 15000 diff --git a/types/index.d.ts b/types/index.d.ts index b216a80..57df9d9 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -6,68 +6,81 @@ /// 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 } }