Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best practice to renew a token for a WebSocket connection

Tags:

This might be opinion based, but I still wonder is there a best practice since I'm almost clueless about websocket practices

I have a SPA that gets a JWT token from my own OP. It then uses that JWT to connect to other services I own using both REST and WebSockets.

As far as it goes for REST, it's pretty straightforward:

  • The REST API validates the JWT (sent in Authorization: Bearer ...) and provides access to the protected resource or responds with a 401, letting the SPA know it needs to request a new token.

Now with websockets :

During the load of the SPA, once I got a token, I'm opening a WS to my webservice. First message I send is a login_message with my JWT, which then I keep on server's websocket instance to know who's sending the messages. Each subsequent message I receive, I validate the JWT to see if it's expired.

Once it has expired as far as I understand, I'm facing two options :

  1. Drop the websocket with a token_expired error of some kind and force the browser to establish a new websocket connection once the token get refreshed.

  2. Keep the websocket open, return an error message and send a new login message (once token is refreshed)

  3. Don't use a login message but just send the JWT in each request.

Question : Which method would you recommend and why? In terms of security, and performance. Are there any other common practice I did not listed?

like image 246
mati.o Avatar asked Jan 08 '17 15:01

mati.o


People also ask

How often should you refresh a token?

The most secure option is for the authorization server to issue a new refresh token each time one is used. This is the recommendation in the latest Security Best Current Practice which enables authorization servers to detect if a refresh token is stolen.

How long should a refresh token last?

The refresh token is set with a very long expiration time of 200 days. If the traffic to this API is 10 requests/second, then it can generate as many as 864,000 tokens in a day.

Where should refresh tokens be stored?

The authorization server can contain this risk by detecting refresh token reuse using refresh token rotation. If your application uses refresh token rotation, it can now store it in local storage or browser memory.


2 Answers

Quite an old question I've asked, so I'd be happy to share our chosen practice:

  1. Once the client gets his JWT for the first time (when the application starts), a WebSocket is opened.

  2. To authenticate the channel, we send a message that we define as part of our protocol, called authMessage which contains that JWT.

  3. The server stores this data on the socket's instance and verifies it's validity/expiry before sending data down the wire or receiving from the client.

  4. The token gets refreshed silently in web application minutes before it is expired and another authMessage is issued to the server (repeat from step 2).

  5. If for whatever reason it gets expired before getting renewed, the server closes that socket.

This is roughly what we have implemented in our application (without optimization) and worked really well for us.

like image 165
mati.o Avatar answered Oct 10 '22 08:10

mati.o


Oauth2 flow has two options to renew the token. As you said on of these options is prompt a message to the use to enforce a new login process.

The other option is to use the refresh_token in which you will avoid this login process to your user, every time the session expires and renew the token in a silent way.

In both case, you need to store the jwt in the client (commonly after login) and update it (after interactive login or silent regeneration). Localstorage, store or just a simple global variable are alternatives to handle the store and update the jwt in he client.

As we can see, jwt regeneration is solved following oauth2 spec and is performed at client side, SPA in your case.

So the next question is: How can I pass this jwt (new or renewed) to the external resources (classic rest api or your websocket)?

Classic Rest Api

In this case as probably you know, send the jwt is easy using http headers. In each http invocation we can send the old/valid jwt or the renewed jwt as header, commonly Authorization: Bearer ...

Websocket

In this case it's not so easy because according to a quickly review, there are not a clear way to update headers or any other "metadata" once the connection was established:

  • how to pass Authorization Bearer access token in websocket javascript client
  • HTTP headers in Websockets client API

What's more, there is no concept of headers, so you need to send this information (jwt in your case) to your websocket using:

  • protocols
var ws = new WebSocket("ws://example.com/path", ["protocol1", "protocol2"]); 
  • cookies
document.cookie = 'MyJwt=' + jwt + ';' var ws = new WebSocket(     'wss://localhost:9000/wss/' ); 
  • simple get parameters
var ws = new WebSocket("ws://example.com/service?key1=value1&key2=value2"); 

Websocket only receive text

And according to the following links, websocket can extract header, get parameters and protocol just at the stabilization stage:

  • https://medium.com/hackernoon/implementing-a-websocket-server-with-node-js-d9b78ec5ffa8
  • https://www.pubnub.com/blog/nodejs-websocket-programming-examples/
  • https://medium.com/@martin.sikora/node-js-websocket-simple-chat-tutorial-2def3a841b61

After that, websocket server only receive text:

const http = require('http'); const WebSocketServer = require('websocket').server; const server = http.createServer(); server.listen(9898); const wsServer = new WebSocketServer({     httpServer: server }); wsServer.on('request', function(request) {     const connection = request.accept(null, request.origin);     connection.on('message', function(message) {       //I can receive just text        console.log('Received Message:', message.utf8Data);       connection.sendUTF('Hi this is WebSocket server!');     });     connection.on('close', function(reasonCode, description) {         console.log('Client has disconnected.');     }); }); 

Send jwt in each request

Having analyzed the previous topics, the only way to send the new o renew token to your websocker backend is sending it in each request:

const ws = new WebSocket('ws://localhost:3210', ['json', 'xml']); ws.addEventListener('open', () => {   const data = {          jwt: '2994630d-0620-47fe-8720-7287036926cd',          message: 'Hello from the client!'       }   const json = JSON.stringify(data);   ws.send(json); }); 

Not covered topics

  • how perform a jwt regeneration using refresh_token
  • how handle silent regeneration

Let me know if you need this not covered topics.

like image 45
JRichardsz Avatar answered Oct 10 '22 08:10

JRichardsz