Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document / Advise on User-based caching for Authenticated Content #1354

Closed
josephliccini opened this issue Mar 9, 2018 · 7 comments
Closed
Labels
Needs More Info Waiting on additional information from the community. workbox-strategies

Comments

@josephliccini
Copy link
Contributor

Library Affected:
workbox-sw

Browser & Platform:
All

Issue or Feature Request Description:
Hi again!

So we are working through handling multiple users logged in to our site. Without going into too much detail.. there are cases where 2 different users' runtime API caches could overlap, so we'd like to make sure caches are scoped by user ID.

Does workbox have a suggestion for this? It seems like a pattern not discussed in very many places. I was working through it and it seems like it can get a bit complex..

Maybe we can brainstorm a solution here?

Thanks!
Joe

When reporting bugs, please include relevant JavaScript Console logs and links to public URLs at which the issue could be reproduced.

@jeffposnick jeffposnick added Needs More Info Waiting on additional information from the community. workbox-strategies labels Mar 12, 2018
@jeffposnick
Copy link
Contributor

We don't have an out-of-the-box solution.

If you only need to ensure that a single user can be logged in at any given time, a simple model is storing all personalized data in a single runtime cache and then calling caches.delete('personalized-data') from the context of your window when there's a logout event.

If you need to support multiple logged in users in a single browser session, then that leads to a few follow-up questions, like how you determine from looking at an HTTP request/response pair which user it's associated with—e.g., is there a specific header value that will remain constant, and which uniquely determines that info?

@josephliccini
Copy link
Contributor Author

Hi Jeff,

Without getting into too much detail of the specific auth implementation (it's pretty complex in our case..), let's simplify things, and say we support 1 user at a time.

I am assuming a lot of other Authenticated SPAs will have a similar scenario to the one I will outline.

  1. User josephliccini logs in to the SPA, SW is registered and begins caching runtime data
  2. josephliccini closes browser, and comes back to page. Caches/IndexedDB are still available and gets an instant boot, with a staleWhileRevalidate update to refresh API calls for authorized content.
  3. josephliccini clicks sign out.
  4. A navigation request to /logout occurs
  5. SW sees /logout, and clears runtime Caches and IndexedDB. (Note Assets are able to be kept since they are not user-specific)
  6. It also makes a request to /logout in order to log out the user from the server (i.e. clear cookies)
  7. User jeffposnick logs in on the same machine into the SPA
  8. There should be no trace of josephliccini at any point in IndexedDB or Caches

Here are the suggestions/discussion points I'd make for this scenario.

  • in Step 1. the SW author will need to include a plugin for each authorized content call before storing Request/Response pair that will ensure that the Authorization header is removed. (It is moderately dangerous to store tokens on disk, so I don't think it should be done unless truly required). A side note, Tokens are usually stored in sessionStorage

  • In Step 5. maybe workbox has a way to say 'Here are all keys for routes' Runtime Caches Data (not asset data)' and 'Here are all the keys for routes' IndexedDB Runtime Expiration data (not asset expiration data)'
    -- It is possible to obtain this through some regex but maybe if others need this scenario it could be useful for the workbox to support.

@jeffposnick
Copy link
Contributor

Regarding 1), I'm personally not qualified to comment one way or another about the guarantees that the Cache Storage API offers when storing a Request object with headers that you consider sensitive information, and how that compares to other mechanisms in which caching occurs.

The code that's used for writing to the runtime caches currently looks like

await cache.put(request, responseToCache);

and if there were a need to use an "empty" Request object as a key rather than the actual Request object that triggered the runtime caching, I think it would be as straightforward as switching that line of code to

await cache.put(request.url, responseToCache);

Regarding 5), even if you don't use a consistent naming scheme for your runtime caches, you can synthesize some of that information by making use of workbox.core.cacheNames.precache, and then doing something like

const nonPrecacheCacheNames = await caches.keys()
  .filter(cacheName => cacheName !== workbox.core.cacheNames.precache);

// Do something with nonPrecacheCacheNames, like delete them.

One thing that I'm not sure about is how well the current workbox-cache-expiration code will behave if you do an "out of band" caches.delete() call to remove a specific runtime cache without also deleting the IDB entries corresponding to its expiration, and then later on recreate the same runtime cache. I'd imagine that it could lead to some confusion—@gauntface?

Maybe the solution there is to expose a workbox.expiration.deleteCache() method that took in a cache name and did both the Cache Storage API deletion as well as the IDB cleanup in one step.

@josephliccini
Copy link
Contributor Author

josephliccini commented Mar 14, 2018

Just brainstorming here.. It might make sense to have a plugin / callback that enables the developer to set their custom Request key logic.

Yes (regarding your response to 5.) this is basically how we've implemented /logout today, and the next step is to go to IDB and clear those out too. I would love a deleteCache() method that does IDB and Cache deletion for a workbox cache 👍

@jeffposnick
Copy link
Contributor

Looking back at the v2 branch, we did have support for passing in an alternative cache key to use, other than the actual Request object:

* @param {Request} [input.cacheKey] Supply a cacheKey if you wish to cache
* the response against an alternative request to the `request`
* argument.

I think the only place we were using it was in the precaching code, and it wasn't easy to hook into a plugin event to override:

Anyway... let me break out your two suggestions into separate feature requests so that they could each be tracked.

@jeffposnick
Copy link
Contributor

With those two issues created, are you cool with closing this one and following up on each independently, @josephliccini?

@josephliccini
Copy link
Contributor Author

Yeah will do! Thanks so much for your work and openness to suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs More Info Waiting on additional information from the community. workbox-strategies
Projects
None yet
Development

No branches or pull requests

2 participants