hocuspocus icon indicating copy to clipboard operation
hocuspocus copied to clipboard

Periodic reauthentication

Open haines opened this issue 2 years ago • 10 comments

The problem I am facing We check that the user is authorized to edit the document during the initial authentication handshake. But if the user's access is revoked while they have a connection open, they can continue to edit the document until they reconnect.

The solution I would like I would like the server to keep track of the last time it received an authentication message, to be able to configure a timeout after which a client who has not reauthenticated would be disconnected, and to be able to configure the provider to reauthenticate on a given interval.

haines avatar Nov 20 '23 13:11 haines

you can use debounce on onChange hook, for example:

import { debounce } from "debounce";
import { Server } from "@hocuspocus/server";

let debounced;

const server = Server.configure({
  async onChange(data) {
    const reauth = () => {
        // your logic
    };

    debounced?.clear();
    debounced = debounce(reauth, 30000);
    debounced();
  },
});

server.listen();

solirpa avatar Nov 21 '23 08:11 solirpa

That wouldn't work for us, unfortunately. We have short-lived access tokens stored in the user's session cookie and we need to refresh those and update the cookie, which requires some participation of the provider rather than just doing it in the server.

Our token function in the provider performs an HTTP request to issue a token, and the auth logic is handled within that HTTP handler. So if we could just have the provider call the token function on an interval and send an authentication message to the server, that'd be ideal.

haines avatar Nov 21 '23 10:11 haines

I think @solirpa's solution would work for me, but having a re-auth period as a configuration option would feel a bit cleaner

mortenson avatar Dec 17 '23 00:12 mortenson

In order to re-evaluate the permissions of a user before applying a message, you can use the beforeHandleMessage hook. It's called before every received message, so you might want to cache expensive permission checks.

I'll keep this open to track progress on resending authentication information from the provider side, which we currently don't officially support.

janthurau avatar Jan 04 '24 09:01 janthurau

In order to re-evaluate the permissions of a user before applying a message, you can use the beforeHandleMessage hook. It's called before every received message, so you might want to cache expensive permission checks.

I'll keep this open to track progress on resending authentication information from the provider side, which we currently don't officially support.

In case the process to re-evaluate the permissions of each user while is using editing the doc is fetching a record in a relational database (or course, a future improvement would be to replicate this in something like Redis), which hook would you recommend @janthurau ? beforeHandleMessage mentioned by you, or onChange mentioned by @solirpa ? What could be the different between those two? I guess that performance should be the variable to take in account for answering this question.

Separate question, depending of which hook is used, if the check passes nothing happens, but if the user is no longer authorized to read-write that doc, an error is returned to the client side, and I guess the connection is closed in the provider? And then how can this be handled in the client side? In which listener this error is received?

p1nox avatar Jan 13 '24 16:01 p1nox

You can use stateless messages to send newly issued tokens(by your API) from the client to the server via the socket. The token can be persisted/associated with the connection on the server side and then used to apply/discard updates or even drop the connection when needed.

talhazubairbutt avatar Feb 15 '24 07:02 talhazubairbutt

@p1nox stumbled across this ticket b/c we're trying to periodically re-authenticate too. Our approach was to use beforeHandleMessage but we strap on a lastAuthCheck onto context in the onAuthenticate hook first. Then, we only re-check authentication every N seconds based on lastAuthCheck (which is updated every time re-authentication happens). This is pretty effective since it's performant (only checking authentication using out API every N seconds), but it also ensures a user that is working with a document maintains access/is re-authenticated on a decent frequency.

BrentFarese avatar Mar 14 '24 00:03 BrentFarese

I'll keep this open to track progress on resending authentication information from the provider side, which we currently don't officially support.

Now this logic is sorely lacking, there is only one output, invoke disconnect on server 🙁

Ideally, I need

  • connection.getToken() method, which will send an event to the provider and receive a token from it
  • or authenticationRecheckFrequency (msec) option + onAuthenticationRecheck where I can close the connection, or change readOnly

RubaXa avatar May 02 '24 16:05 RubaXa