Auth0.swift icon indicating copy to clipboard operation
Auth0.swift copied to clipboard

Add support for disabling credential auto-renewal

Open NachoSoto opened this issue 5 months ago • 10 comments

  • [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 CredentialsManager constructor (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.

NachoSoto avatar Jul 23 '25 18:07 NachoSoto

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.

Screenshot 2025-07-24 at 10 43 35

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)

Widcket avatar Jul 24 '25 10:07 Widcket

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?

NachoSoto avatar Jul 24 '25 15:07 NachoSoto

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.

Widcket avatar Jul 24 '25 17:07 Widcket

@NachoSoto did setting the rotation overlap period work for your use case?

Widcket avatar Jul 29 '25 10:07 Widcket

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?

NachoSoto avatar Aug 06 '25 17:08 NachoSoto

I'd suggest trying out the rotation overlap period first and seeing if you run into any issues.

Widcket avatar Aug 06 '25 19:08 Widcket

FYI I won't be working as a maintainer of this library anymore. In the future please work with @NandanPrabhu.

Widcket avatar Aug 06 '25 19:08 Widcket

How can you guarantee there can’t be race conditions with 2 processes writing into the keychain though?

NachoSoto avatar Aug 06 '25 23:08 NachoSoto

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 SecItem APIs 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.

Widcket avatar Aug 07 '25 10:08 Widcket

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.

Widcket avatar Aug 07 '25 11:08 Widcket