parse-server
parse-server copied to clipboard
fix: Use LRUCache for `extendSessionOnUse`
Pull Request
- Report security issues confidentially.
- Any contribution is under this license.
- Link this pull request to an issue.
Issue
Currently, extendSessionOnUse functions as a debounce, and does not clear the throttle store.
Closes: #8682
Approach
- Change timeout logic (debounce) to a throttle, where the first session call is executed, and then subsequent calls do not extend
- Sessions are no longer looked up and extended if they have been pulled from the session cache, as the only way they get into the session cache in the first place, is if they have been used recently (I believe it's safe to assume they have been extended already)
Tasks
- [ ] Add new Parse Error codes to Parse JS SDK
Thanks for opening this pull request!
@Moumouls could you add your thoughts?
Codecov Report
Attention: Patch coverage is 87.50000% with 2 lines in your changes missing coverage. Please review.
Project coverage is 93.55%. Comparing base (
34867b7) to head (f8680e7). Report is 22 commits behind head on alpha.
| Files with missing lines | Patch % | Lines |
|---|---|---|
| src/Auth.js | 87.50% | 2 Missing :warning: |
Additional details and impacted files
@@ Coverage Diff @@
## alpha #8683 +/- ##
==========================================
- Coverage 93.56% 93.55% -0.02%
==========================================
Files 186 186
Lines 14836 14838 +2
==========================================
Hits 13881 13881
- Misses 955 957 +2
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
🚀 New features to boost your workflow:
- ❄ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
I will reformat the title to use the proper commit message syntax.
Is this only a perf PR or does it actually fix a bug, or both - for the changelog entry.
Bug and perf i think @mtrezza
Then what would be a better PR title, to inform developers what the bug is.
@dblythy Could you rewrite the PR title? If the PR includes a bug fix and a perf improvement, then the PR is a fix since that is higher ranked and the perf improvement could be added as additional note.
@Moumouls Do you think you could take a look at this Auth thing as well? We just need to resolve the conflict to merge this, but not sure how to do that. If you could make a suggestion I'd commit the change.
The cache is just to reduce the method from being called constantly when the same session token is used. If there are multiple servers, the session will just be continued to extended every time - it could be optimised by using a distributed cache.
Ok, I understand that a shared cache in this case is a separate feature that doesn't exist currently, correct?
Sessions are no longer looked up and extended if they have been pulled from the session cache, as the only way they get into the session cache in the first place, is if they have been used recently (I believe it's safe to assume they have been extended already)
Is the session expiration date also in the cache? Would it be possible that a session has expired since getting into the cache and is still in the cache?
Ok, I understand that a shared cache in this case is a separate feature that doesn't exist currently, correct?
Correct
Is the session expiration date also in the cache
The ttl on the cache is 500ms. It's just to stop bursty requests from continuously extending the session
How do we get to the 500ms? Why not 100ms or 10s?
It's an arbitrary number that is currently used - it just represents the window in which using the same token won't be extended (it will at the start of the 500ms, but not again until 500ms after). Would you suggest higher?
Just thinking out loud: could there happen a situation where long burst of repeating requests within that window would use the token from LRUCache so many times that they would essentially prevent it from auto extending and make it expire? Thinking LRUCache timeout of 5 seconds and token validity say 60 seconds?
No, I don't think so, the mechanism is a throttle so it should throttle it to one request every 500ms. Previously with the mechanism at a debouncer, this could occur.
The only changes to the current implementation in this PR is:
- Use a
LRUcache instead of aPOJO - Change the debouncer to a throttle
Not sure I follow, @dblythy you answered with 500ms to @mman's example of 5s cache TTL. What if the cache TTL was 60 seconds, and the token TTL was 10 seconds? I'm trying to figure out where the boundaries are for the cache TTL, based on a token TTL that the developer can set freely in Parse Server options, lowest value being 1s I believe.
The LRU cache here is a fixed, un-configurable time of 500ms. I think the 5s was just presented as a potential scenario, which wouldn't make a difference anyway as again, the mechanism is a throttle (it will trigger once at the start of every ttl)
In any case, I'm not sure configuration of the ttl value is within the scope, the current implementation is fixed at 500ms.
Yes, it was a theoretical number to see if we can come up with a scenario that it would break the auto-extend-algorithm. I think we all agree that we want to:
- serve requests fast without necessarily checking the sessionToken (requiring a read) on every use
- extend the sessionToken validity (thus incurring a write) if we meet the criteria, and only once.
- make the algorithm work reliably so when users use the valid sessionToken, it always gets extended and we do not end up in situation where it expires despite being used...
Point 3 being the most important, because this is how Parse Server worked for a long long time, where user would be kicked out after months of actively using the app... and only workaround was to set session length to nonsense number like 10 years... which is a bad practice...
I would make the quick point that in order to properly implement all 3 points, we would need to transitions to JWTs with refresh tokens. This current mechanism is a convenience method but the long term solution is JWTs.
I would make the quick point that in order to properly implement all 3 points, we would need to transitions to JWTs with refresh tokens. This current mechanism is a convenience method but the long term solution is JWTs.
On the other hand, transitioning to JWT will make everything more complex... the current system is easy to understand and if we get this one right, I think it will work flawlessly...
I agree that the scope here is just the fix as currently presented. I'm trying to understand what constraints the TTL value has, and based on that determine a value that makes most sense.
What would happen if the cache TTL was 60 seconds and the token TTL was 10 seconds? Just to understand whether @mman's concern is valid.
It would
- add 10 seconds to the token expiry the first time it is used
- not extend the token for the next 60 seconds (meaning it would expire)
- meaning that the token would expire
No, the only way the concern is valid is if the session length is less than the throttle ttl (which is why I set it to 500ms)
Ok, so we have identified the constraint that the cache TTL must be greater than the token TTL. Given that the cache TTL is not configurable, and if the minimum configurable token TTL is 1s, the cache TTL must be < 1s. To avoid race conditions, we'd need to take the cache lookup time into account, which is likely <1ms for a local cache. Taking also other code execution times into account, it should be enough to reserve 10ms in total, but let's make this super safe with 100ms.
So the cache TTL could be up to 900ms.
-
What could be the difference between 500ms and 900ms cache TTL in practice?
-
Would it make sense to set the cache TTL as a function of the token TTL, which we know from the Parse Server config? In theory, someone could issue tokens with different TTLs, so maybe that's not feasible?
-
Another aspect: is it currently possible to configure a token TTL of 0s, and if yes, do we open any vulnerabilities by caching it for 1s?
What could be the difference between 500ms and 900ms cache TTL in practice?
No real difference. The intent is to just prevent the extension being called every time for bursty requests. 900ms would extend the burst window.
With this feature we want to extend the session each time it is used. But if there is a burst of requests, it will continuously extend the session for each request, causing unnecessary load. That's the intent of the throttle.
Another aspect: is it currently possible to configure a token TTL of 0s
Session lengths must be greater than 0
The intent is to just prevent the extension being called every time for bursty requests
Why and when do these bursts occur in the first place, and in what frequency and duration are they expected?
Why and when do these bursts occur in the first place
It's down to the specific use case, but consider simple functionality that queries an object and then saves it - it would be using the same sessionToken 2 times within <10ms
Got it, I think that means max. cache TTL must be < min. session token expiration, which is 1 second. If the cache TTL is 2s and the token expiration is 1s, then the cache TTL would effectively override the token expiration with 2s. 900ms may be too close and cause a race condition, still causing the override. So your suggested 500ms seems to be a fair value.
That also answer my previous question "Would it make sense to set the cache TTL as a function of the token TTL," with "no", since token deletion can happen at any time, regardless of what the standard token expiration is set to.
🎉 This change has been released in version 8.0.1-alpha.1
🎉 This change has been released in version 8.0.1