Add support for disabling credential auto-renewal
- [x] All new/changed/fixed functionality is covered by tests (or N/A)
- [x] I have added documentation for all new/changed functionality (or N/A)
📋 Changes
- Added new optional parameter to
CredentialsManagerconstructor (not changing default behavior) to allow disabling credential renewals. - This is useful to be able to use Auth0 from an extension, where we wouldn't want requests to automatically refresh credentials potentially leading to race conditions with the main app target doing the same.
Hi @NachoSoto, thanks for raising this.
This is useful to be able to use Auth0 from an extension, where we wouldn't want requests to automatically refresh credentials potentially leading to race conditions with the main app target doing the same.
Would configuring the Rotation Overlap Period work for this purpose? Given two concurrent renewal requests (one from the app and one from the extension), once the first succeeds, if the second one completes within the rotation overlap period, it will succeed as well, and no token rotation error will be returned.
See https://auth0.com/docs/secure/tokens/refresh-tokens/configure-refresh-token-rotation#configure-in-the-dashboard for more information.
You can also configure a lower timeout value to ensure that the second request either succeeds or fails within the rotation overlap period. To do so, you need to use a custom URLSession:
var configuration = URLSessionConfiguration.default
// configure it...
let customURLSession = URLSession(configuration: configuration)
let auth = Auth0.authentication(session: customURLSession)
let credentialsManager = CredentialsManager(authentication: auth)
Given two concurrent renewal requests (one from the app and one from the extension), once the first succeeds
Are there any guarantees that the two can safely complete concurrently? And after that, there could be a race condition where the first one that completed is the one that ends up in the keychain, leading to a broken token, right?
Are there any guarantees that the two can safely complete concurrently? And after that, there could be a race condition where the first one that completed is the one that ends up in the keychain, leading to a broken token, right?
The first one that completes will be automatically saved in the Keychain by the Credentials Manager. But, if the rotation overlap period is configured, then the second request will still succeed, and will also be automatically saved in the Keychain.
I just tried it out, and exchanging the same refresh token concurrently yielded the same access token, so there should be no issues on that front.
@NachoSoto did setting the rotation overlap period work for your use case?
I haven't had a chance yet. But even if it handles race conditions correctly with 2 processes potentially writing in the keychain simultaneously, we would still prefer to run Auth0 from an app extension in a read-only configuration (which is what we'd accomplish with this PR). Does that makes sense?
I'd suggest trying out the rotation overlap period first and seeing if you run into any issues.
FYI I won't be working as a maintainer of this library anymore. In the future please work with @NandanPrabhu.
How can you guarantee there can’t be race conditions with 2 processes writing into the keychain though?
How can you guarantee there can’t be race conditions with 2 processes writing into the keychain though?
It seems I did not explain the purpose of the Rotation Overlap Period setting clearly. My bad. Let's try again.
Configuring this value (e.g., to 30 seconds), along with a shorter timeout value (e.g., 5 seconds), means:
- Both processes will still send concurrent renewal requests.
- Both concurrent renewal requests will succeed.
- Both responses will come with the same access token.
- The same access token will be written twice to the Keychain. The
SecItemAPIs are atomic, and the locks that make it possible are managed by the OS. See https://developer.apple.com/forums/thread/117092?answerId=361922022#361922022.
In other words, this will not prevent concurrent renewal requests; it will make the concurrency irrelevant.
If not, please feel free to create a reproduction (e.g., a minimal app with an extension, both writing to the Keychain in a loop) and share it with us.