feathersjs / feathers

The API and real-time application framework
https://feathersjs.com
MIT License
15.04k stars 751 forks source link

No apparent way to restore a Socket IO connection on the client once a JWT expires #3252

Open arcticmonkey opened 1 year ago

arcticmonkey commented 1 year ago

Issue

Expired JWT's close the client Socket IO connection entirely.

If there is an active authenticated socket io connection and the jwt expires, the connection is closed by the server and there is no apparent way to restore the connection in the client in order to re-authenticate again without creating a whole new client.

Calling app.io.connect() attempts to connect and authenticate using the same credentials and fails. Thus preventing and further requests from propagating to the server.

I'm not sure if this behaviour is by design, but if it is then I think it may be beneficial to document how to handle the reconnecting of the client following a JWT expiration.

Related To

Steps to reproduce

  1. Create a new FeathersJS app using the CLI
  2. Set up authentication using the CLI using the defaults
  3. Set the JWT configuration expiration to something quick like 15 seconds
  4. Bundle and link the client
  5. Open your frontend application code
  6. Setup the client to use SocketIO
const socket = io('http://localhost:3030');

const socketConnection = socketio<ServiceTypes>(socket)

const socketClient = createClient(socketConnection);
  1. Log in using local strategy
  2. Wait for token to expire on the server
  3. Make any form of request using the client, including: app.authenticate({ ... }).

Expected behavior

After the JWT expires, the requests should still be sent to the server and handle as not authenticated.

Actual behavior

The request just hangs in the client as there is no connection. The request promises never completes.

System configuration

Module versions (especially the part that's not working): "@feathersjs/adapter-commons": "^5.0.8", "@feathersjs/authentication": "^5.0.8", "@feathersjs/authentication-client": "^5.0.8", "@feathersjs/authentication-local": "^5.0.8", "@feathersjs/authentication-oauth": "^5.0.8", "@feathersjs/cli": "^5.0.8", "@feathersjs/configuration": "^5.0.8", "@feathersjs/errors": "^5.0.8", "@feathersjs/express": "^5.0.8", "@feathersjs/feathers": "^5.0.8", "@feathersjs/mongodb": "^5.0.8", "@feathersjs/rest-client": "^5.0.8", "@feathersjs/schema": "^5.0.8", "@feathersjs/socketio": "^5.0.8", "@feathersjs/transport-commons": "^5.0.8", "@feathersjs/typebox": "^5.0.8", "compression": "^1.7.4", "mongodb": "^5.7.0", "winston": "^3.10.0"

NodeJS version: v18.16.0

Operating System: MacOS

Browser Version: Chrome 114 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36

Related Tickets

https://github.com/feathersjs/feathers/issues/2194 https://github.com/feathersjs/feathers/issues/2194#issuecomment-762419223 https://github.com/feathersjs/feathers/issues/2194#issuecomment-762562893

Thanks in advance for your review!

daffl commented 1 year ago

This should be doable with the manual reconnection mentioned in the Socket.io documentation:

socket.on('disconnect', () => {
  socket.connect()
})
jamesholcomb commented 1 year ago

Ran into this problem early on in a React Native client. I never understood the purpose of the server disconnecting the socket at the moment of token expiration, causing you have to write your own reconnect logic on the clients...I got it mostly working but the edge cases became too much to overcome. Auto reconnect already worked great out of the box with socketio.

I ended up patching handleConnection in the authentication package by removing the emit('disconnect') call and the long timer setup/teardown and implementing a simple token expiry check on the client.