solid-client-authn-js icon indicating copy to clipboard operation
solid-client-authn-js copied to clipboard

[node] Using an authenticated session across multiple processes eg. Service + MQ Worker

Open elf-pavlik opened this issue 3 years ago • 8 comments

We are planning to add a message queue with a worker to https://github.com/janeirodigital/sai-impl-service/ It will handle things like subscribing to Solid Notifications - Webhook and other background jobs that will require an authenticated session.

I wanted to ask if we should expect any issues when the authenticated session gets used by more than one node process. I haven't dug into how refreshing of the tokens is being scheduled but I can imagine some potential issues if two processes would try refreshing tokens without coordinating it via storage. Especially when the refresh token is rotated.

Not sure how related it is to #308 but it might be 🤔

elf-pavlik avatar Sep 23 '22 02:09 elf-pavlik

This library has not been designed for multiple processes being authenticated using the same session concurrently. In order for this to be successful, you'll have to be careful about how the Storage implementation that gets passed to the library handles the refresh token. One approach that would probably work (but that's just me thinking out loud) is if each process gets the session from storage before performing an authenticated request, which will enforce the latest refresh token being used (since the refresh token may be rotated on each usage). Concurrency will need to be managed very carefully.

NSeydoux avatar Sep 26 '22 11:09 NSeydoux

I think each process still needs an instance of Session which gets created from serialized data in Storage. Probably there might need to be a way to disable refreshing tokens by each session and instead enable re-hydrating the session from the storage instead. In that case, there would be one instance of the Session which would still be refreshing the token, would it keep serialized tokens in the Storage valid? (also thinking out loud)

elf-pavlik avatar Sep 26 '22 13:09 elf-pavlik

Re-hydrating the session from storage is actually based on the refresh token being present: the access token is short-lived, and it is a sensitive piece of data, so it isn't persisted in storage. This means re-hydrating the session necessarily refreshes the token (and rotates the refresh token).

NSeydoux avatar Sep 29 '22 12:09 NSeydoux

If each process has to use refresh-token (which results in it being rotated). I don't really see how the session could be used across multiple processes. At least not without some pesky locking.

Looking at CSS' OP config

It sets the Access Token expiry to 1h and the Refresh token to 24h. Having an option that opts-in to store the Access Token in the storage would make it easier to share the session across processes. Only one process would be responsible for refreshing it, possibly using a scheduler.

BTW we should be already talking about storing the ID Token as in the latest Solid-OIDC. We can already treat the Access Token as it was an ID Token for the sake of converstaion.

elf-pavlik avatar Sep 29 '22 12:09 elf-pavlik

That's absolutely true, we are planning on a big refactor of this library to stop relying on the Access Token being issued by the OpenID Provider. When we do it, the way things are stored will evolve to rely on the Credential Management API.

NSeydoux avatar Sep 30 '22 08:09 NSeydoux

@elf-pavlik in the meantime, if you want to go outside of in-memory storage, you'd need to implement this interface/file, but backed by some sort of database (e.g., redis) — keep in mind that you'll almost definitely want to encrypt whatever you store in there, as the data stored is equivalent to a password.

But as @NSeydoux suggests, all this is planned for a major upheaval.

ThisIsMissEm avatar Jan 13 '23 00:01 ThisIsMissEm

Thank you @ThisIsMissEm. We do have simple redis backed implementation of IStorage. We also use BullMQ for a job queue. The problem comes when different workers use getSessionFromStorage and each returned instances of Session in each worker will try to refresh tokens.

I imagine that following approach could work in the future (with latest Solid-OIDC):

  • One dedicated worker which schedules jobs to refresh ID Token with OP and stores latest in the redis storage
  • The main service and all other workers rely on latest ID Token being available in redis and get it from there instead of trying to refresh it with OP. Each worker would still have the session do the dance with AS to push the ID Token (as UMA claim) and get an access token based on it.

elf-pavlik avatar Jan 13 '23 12:01 elf-pavlik

@elf-pavlik yeah, that'd make sense to me. That or have some sort of "refresh token lock", where by there's a race to the first worker to start the refresh, with an expiry in case it fails/crashes, and then perhaps go from there; But actually having a dedicated worker (not horizontally scalable, but potentially shard-able), which tries to semi-eagerly refresh the token such that the other workers all just grab the latest token & don't try refreshes. Or you could expire the access tokens in redis in accordance with the expiry information (I don't think the SDK exposes that at the moment, see: https://github.com/inrupt/solid-client-authn-js/pull/2122

ThisIsMissEm avatar Jan 13 '23 23:01 ThisIsMissEm