thream / socketio-jwt Goto Github PK
View Code? Open in Web Editor NEWAuthenticate socket.io incoming connections with JWTs.
Home Page: https://www.npmjs.com/package/@thream/socketio-jwt
License: MIT License
Authenticate socket.io incoming connections with JWTs.
Home Page: https://www.npmjs.com/package/@thream/socketio-jwt
License: MIT License
Instead to name our package @thream/socketio-jwt
, we could rename it socket.io-jwt
, shorter and easier name + nearly the same name as the original package.
Once done, we should deprecate the old one @thream/socketio-jwt
.
Hopefully, once we want to do it the npm
will still be available, currently it is: https://www.npmjs.com/package/socket.io-jwt
Note: It is a BREAKING CHANGE.
I've created new React 18 app using https://reactjs.org/docs/create-a-new-react-app.html
I've added the required packages and code from the readme but when I try to run the app I'm getting this error:
[ERROR in ./node_modules/buffer-equal-constant-time/index.js 4:13-37
Module not found: Error: Can't resolve 'buffer' in 'E:\React\socket-react\node_modules\buffer-equal-constant-time'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
- install 'buffer'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "buffer": false }ERROR in ./node_modules/jwa/index.js 5:13-30
Module not found: Error: Can't resolve 'crypto' in 'E:\React\socket-react\node_modules\jwa'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "crypto": require.resolve("crypto-browserify") }'
- install 'crypto-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "crypto": false }ERROR in ./node_modules/jwa/index.js 9:11-26
Module not found: Error: Can't resolve 'util' in 'E:\React\socket-react\node_modules\jwa'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "util": require.resolve("util/") }'
- install 'util'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "util": false }ERROR in ./node_modules/jws/lib/data-stream.js 4:13-30
Module not found: Error: Can't resolve 'stream' in 'E:\React\socket-react\node_modules\jws\lib'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "stream": require.resolve("stream-browserify") }'
- install 'stream-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "stream": false }ERROR in ./node_modules/jws/lib/data-stream.js 6:11-26
Module not found: Error: Can't resolve 'util' in 'E:\React\socket-react\node_modules\jws\lib'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "util": require.resolve("util/") }'
- install 'util'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "util": false }ERROR in ./node_modules/jws/lib/sign-stream.js 8:13-30
Module not found: Error: Can't resolve 'stream' in 'E:\React\socket-react\node_modules\jws\lib'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "stream": require.resolve("stream-browserify") }'
- install 'stream-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "stream": false }ERROR in ./node_modules/jws/lib/sign-stream.js 12:11-26
Module not found: Error: Can't resolve 'util' in 'E:\React\socket-react\node_modules\jws\lib'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "util": require.resolve("util/") }'
- install 'util'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "util": false }ERROR in ./node_modules/jws/lib/tostring.js 2:13-37
Module not found: Error: Can't resolve 'buffer' in 'E:\React\socket-react\node_modules\jws\lib'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
- install 'buffer'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "buffer": false }ERROR in ./node_modules/jws/lib/verify-stream.js 8:13-30
Module not found: Error: Can't resolve 'stream' in 'E:\React\socket-react\node_modules\jws\lib'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "stream": require.resolve("stream-browserify") }'
- install 'stream-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "stream": false }ERROR in ./node_modules/jws/lib/verify-stream.js 12:11-26
Module not found: Error: Can't resolve 'util' in 'E:\React\socket-react\node_modules\jws\lib'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "util": require.resolve("util/") }'
- install 'util'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "util": false }ERROR in ./node_modules/safe-buffer/index.js 2:13-30
Module not found: Error: Can't resolve 'buffer' in 'E:\React\socket-react\node_modules\safe-buffer'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "buffer": require.resolve("buffer/") }'
- install 'buffer'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "buffer": false }
I'm getting errors
No errors
Using [email protected], the example given for adding the Authorization header does not work.
This:
const socket = io('http://localhost:9000', {
extraHeaders: { Authorization: `Bearer ${yourJWT}` }
})
Should be:
const socket = io('http://localhost:9000', {
transportOptions: {
polling: {
extraHeaders: {
Authorization: `Bearer ${yourJWT}`,
},
},
},
});
Using jwks-rsa
version 1.12.2.
Also, if debugging key
, it returns children kid
, alg
, nbf
, publicKey
and getPublicKey
. No "rsaPublicKey".
decodedToken
is weakly typed.
I managed to get the example to compile with the following code:
secret: async (decodedToken) => {
console.debug("I'm trying to authorize...", decodedToken)
if (
decodedToken &&
typeof decodedToken === "object" &&
"header" in decodedToken &&
decodedToken.header.kid
) {
const key = await client.getSigningKeyAsync(decodedToken.header.kid)
return key.getPublicKey()
}
return ""
},
However, the problem that remains is that io.on("connection"
never fires and there is no sign that the connection is established at all.
The token and authentication in place is the same that already successfully works with my express-jwt
middleware.
The above does not happen.
The addition of the support of jwks-RSA (https://www.npmjs.com/package/jwks-rsa)
Support for this feature can be seen in the fork of ssnxd (https://www.npmjs.com/package/@ssnxd/socketio-jwt/v/4.5.3).
The original repo does not support this feature as far as I know.
Example of the implementation currently in use:
io.sockets
.use(
authorize({
algorithms: ['RS256'],
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${process.env.DOMAIN}/oauth2/jwks`
}),
timeout: 15000
})
)
We can use socket.io
with mutiple transports type, as it is described in the socket.io
documentation : https://socket.io/docs/v3/client-initialization/#transports
Currently, we only support polling
transport.
If you use the websocket
transport, you can't pass the token in the extraHeaders
: https://socket.io/docs/v3/client-initialization/#extraHeaders, so we don't support it.
Maybe we could retrieve the token thanks to query string instead of the extraHeaders
: https://socket.io/docs/v3/client-initialization/#query
Apologies if this is poorly worded as I'm just getting started with Typescript.
I'm trying to implement types as instructed in the Socket.io documentation, and my IDE is throwing an error on the connect_error event for this package.
This bit:
this.socket.on('connect_error', (err) => {
if (err.data && err.data.type === 'UnauthorizedError') {
console.log('User token has expired');
}
});
Results in:
Property 'data' does not exist on type 'Error'.ts(2339)
Is there a best practice way of fixing this? Thanks.
Write now, the socket object has the encoded and decoded tokens as custom properties. One QoL improvement would be to have the user object too, much like how passport injects req.user
The solution would be similar to passport wherein a new option can be added which takes in a function which is executed after the token is decoded and validated. The result of this function can then be added to the user
property of the socket object.
The alternative would be to use the decodedToken
to fetch the user object which is fine in itself but I think this feature would go a long way in making life easy.
I already have a PoC available and if you agree with this feature, I will raise a PR.
Currently, if you require a bearer token thusly in your server...
// Require Bearer Token
const socket = io('http://localhost:9000', {
auth: { token: `Bearer ${yourJWT}` }
})
The only way you can be authorized is to connect with a client something like this...
this.io = io(`${socketioAddress}`, <SocketIOClient.ConnectOpts>{
perMessageDeflate: false,
transports: ['websocket'],
upgrade: false,
auth: { token: `Bearer ${this.tSessionService.token}` }
});
However, some web socket clients (like WebSocket4Net for C#) don't have support for this.
Would it be possible to also support including the bearer token in a header as well? Something in authorize.js pehaps...
const authorize = (options) => {
const { secret, algorithms = ['HS256'], onAuthentication } = options;
return async (socket, next) => {
let encodedToken = null;
const { token } = socket.handshake.auth;
const { authentication } = socket.handshake.headers;
const authenticationToken = token || authentication;
if (authenticationToken != null) {
const tokenSplitted = authenticationToken.split(' ');
if (tokenSplitted.length !== 2 || tokenSplitted[0] !== 'Bearer') {
return next(new UnauthorizedError_1.UnauthorizedError('credentials_bad_format', {
message: 'Format is Authorization: Bearer [token]'
}));
Tests
We should update jest
(with ts-jest
) to latest version, but if we do the tests are failing.
See: https://jestjs.io/blog/2021/05/25/jest-27
Currently, it's not my priority, but if anyone want to contribute, you can open a PR, I'll review it, thank you for your help. ๐
We should use ESM imports instead of CommonJS.
See: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
Blocked by microsoft/TypeScript#33079.
Badly, currently jest
doesn't support very well ESM imports, we should wait until it is correctly supported.
See: jestjs/jest#11167 and jestjs/jest#10025
Note: It is a BREAKING CHANGE.
this.io = new Server(this.di.server.listener);
this.io.use(
authorize({
secret: "test"
})
);
this.io
.on('connection', socket => {
console.log("authenticated", socket.decodedToken); // No any messages in console
});
io({
extraHeaders: { Authorization: `Bearer ${Cookie.get('jwt')}}` }
});
"socket.io": "^3.0.5",
"socket.io-client": "^3.0.5"
No any actions happens after middleware of socket.io using this package.
I mean on.("connection")
never fired after adding use(authorize(...)
. Once I'm removing use(authorize(...))
sockets are connects fine.
I see the source code is using HS256 algorithm - is there anyway to change this?
Releases
We should use semantic-release, it is better than using the workflow we currently have.
Also, we should update the badges on the README.md
.
Extend socket property type so its properties are visible for TypeScript.
interface ExtendedSocket extends Socket {
encodedToken?: string;
decodedToken?: any;
}
type SocketIOMiddleware = (
socket: ExtendedSocket,
next: (err?: ExtendedError) => void
) => void
What I can see socketio-jwt makes sure the jwt is valid and has not expired during the connection phase. But if the token expires after a while, the token will still be considered valid and the user will be able to perform actions without having a valid token?
Is there any protection against that that I have missed?
I am switching from socketio-jwt to @thream/socketio-jwt.
My original authorization options object include a timeout. I don't see anything about timeouts not being supported in the documentation despite this item being forked from socketio-jwt.
TS error:
Argument of type '{ secret: string; timeout: number; algorithms: "HS256"[]; }' is not assignable to parameter of type 'AuthorizeOptions'. Object literal may only specify known properties, and 'timeout' does not exist in type 'AuthorizeOptions'.ts(2345)
I cannot add a timeout option in the AuthorizeOptions
I can include a timeout option in the AuthorizeOptions
I feel like this is a bug but if this should be feature request instead, feel free to change th label. Thanks!
There is a security issue with jsonwebtoken
library in v8.5.1
This could be nice to bump jsonwebtoken
to v9.0.0
where a fix have been made
Hey!
Can you help me please regarding using @thream/socketio-jwt with 'require'? Currently, I am getting error code: 'ERR_REQUIRE_ESM'. I can't use the module type (import) since my entire nodejs server is built with require.
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.