Skip to content

fix(ios): keep the app running while credentials are renewed - #1711

Closed
Anzormumladze wants to merge 1 commit into
auth0:masterfrom
Anzormumladze:fix/ios-keep-app-alive-during-token-refresh
Closed

Anzormumladze wants to merge 1 commit into
auth0:masterfrom
Anzormumladze:fix/ios-keep-app-alive-during-token-refresh

Conversation

@Anzormumladze

Copy link
Copy Markdown

Changes

On iOS, these calls now run inside a UIKit background task (beginBackgroundTask / endBackgroundTask):

  • getCredentials (also with forceRefresh)
  • getSSOCredentials
  • getApiCredentials

The task ends in the Credentials Manager callback. Auth0.swift stores the new credentials before it calls that callback, so the task covers the request and the store.

  • New internal file: ios/CredentialsBackgroundTask.swift.
  • No public API change, no change to stored data, no change on Android or web.
  • UIApplication is looked up at runtime (the same way Auth0.swift does it), so the pod still builds for app extensions.

Why: with refresh token rotation, Auth0 replaces the refresh token as soon as it handles the exchange. If iOS suspends the app after that, and the app is terminated before it stores the response, the app keeps the old refresh token. After the rotation overlap period, the next renewal sends the old token. Reuse detection then revokes the token family, and the user must log in again. maxRetries cannot help in this case, because the app is not running when the response arrives.

Limits (best effort):

iOS may keep the app running a few seconds longer in the background while one of these calls is in progress.

This could also live in Auth0.swift's CredentialsManager. I kept it in the bridge because it is iOS-only, small, and needs no API change. I'm happy to move it, or to open a backport for the v5 line.

References

Testing

  • yarn typecheck: pass.
  • yarn lint: 0 errors. The 6 warnings are already on master.
  • yarn test: 805 of 805 pass.
  • iOS build of the example app with this change: Xcode 27.0, Swift 6 language mode, iOS Simulator. No new warnings.

Manual A/B test on an iOS 18.3 Simulator, with the example app built from this branch with and without the change. It ran against an Auth0 tenant with refresh token rotation and a 180 s overlap. The app was launched without the debugger. Steps:

  1. Log in. A proxy makes the access token short, so the next call needs a renewal.
  2. Call getCredentials(). The proxy holds the /oauth/token response for 20 s. Auth0 has already rotated the token at this point.
  3. Send the app to the background about 3 s later.
  4. Terminate the app about 35 s later, like iOS killing a suspended app.
  5. Launch again about 200 s after the rotation and call getCredentials().
Build Run 1 Run 2
Without this change 403 invalid_grant, logged out 403 invalid_grant, logged out
With this change 200, still logged in 200, still logged in

This repo has no iOS unit test target, so I could not add a unit test. Not tested on a physical device, and not on the iOS 27 Simulator.

  • This change adds unit test coverage
  • This change has been tested on the latest version of the platform/language or why not

Checklist

A Credentials Manager call can exchange the refresh token. With refresh
token rotation, Auth0 replaces the refresh token when it handles the
exchange. If iOS suspends the app after that, but before the response
arrives and the new refresh token is stored, the app keeps the old
refresh token. After the rotation overlap period the next renewal sends
that old token, Auth0's reuse detection revokes the token family, and
the user has to log in again.

Wrap getCredentials (including forceRefresh), getSSOCredentials and
getApiCredentials in a UIKit background task, so iOS keeps the app
running until the request finishes and the result is stored, within the
normal background time limit. UIApplication is looked up at runtime so
the pod still builds for app extensions.

Related to auth0#1374
@Anzormumladze
Anzormumladze requested a review from a team as a code owner October 26, 2026 22:51
@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between a10bbd8 and 60e64cc.

Files selected for processing: 2
  • ios/CredentialsBackgroundTask.swift
  • ios/NativeBridge.swift

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

📝 Summary

Summary by CodeRabbit

  • Reliability
    • Credential requests can continue while the app is temporarily in the background, helping them complete during background transitions.
    • Background work is ended when the request completes or when the system’s allotted time expires.

The iOS bridge now starts background tasks for credential requests and ends them when requests complete. A new wrapper handles application availability, task expiration, and repeated end calls.

Changes: iOS Credential Background Tasks

Layer / File(s) Summary
Background task wrapper
ios/CredentialsBackgroundTask.swift
Adds a UIKit background-task wrapper. It checks for an available application, ends the task on expiration, and resets the task identifier after ending.
Credential request integration
ios/NativeBridge.swift
getCredentials, getSSOCredentials, and getApiCredentials start a background task before their requests and end it when their completion handlers run.

Priority: Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: pmathew92, subhankarmaiti

Merge Risk: Minimal · up to 60e64

No actionable issue is established by the available evidence. The change is mergeable after normal validation.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title names the main change: iOS credential calls now run in a background task so renewal can finish. It uses the conventional fix(ios) prefix and is specific. Its scope is accurate and it does no…
Description check Passed The description states the change: iOS getCredentials (with forceRefresh), getSSOCredentials, and getApiCredentials now run inside a UIKit background task. It gives the rotation-related reason, the ne…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title names the main change: iOS credential calls now run in a background task so renewal can finish. It uses the conventional fix(ios) prefix and is specific. Its scope is accurate and it does not describe unrelated work.

Full details: Description check

Explanation

The description states the change: iOS getCredentials (with forceRefresh), getSSOCredentials, and getApiCredentials now run inside a UIKit background task. It gives the rotation-related reason, the new internal file, the limits, and the test results. It matches the changeset closely.

Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@Anzormumladze

Copy link
Copy Markdown
Author

@subhankarmaiti @NandanPrabhu could you please review this when you have time? It is a small iOS-only change for one cause of #1374. The A/B test is in the description.

@NandanPrabhu

Copy link
Copy Markdown
Contributor

Hi @Anzormumladze thanks for raising the PR! We always love contributions from the community.
After reviewing the PR we would request you to raise the same PR for the Auth0.swift repo https://github.com/auth0/Auth0.swift.

@Anzormumladze

Copy link
Copy Markdown
Author

After reviewing the PR we would request you to raise the same PR for the Auth0.swift repo https://github.com/auth0/Auth0.swift.

@NandanPrabhu done: auth0/Auth0.swift#1321

It is the same fix, now in CredentialsManager. It also covers renew(), the async and Combine APIs, and retries. A task starts only when a request is about to start.

A0Auth0.podspec pins Auth0 to 3.2.0. After the Auth0.swift PR is released, this repo only needs a version bump, and this PR is not needed.

Should I close this PR now, or keep it open until the Auth0.swift PR is merged?

@subhankarmaiti

Copy link
Copy Markdown
Contributor

Hi @Anzormumladze
Closing this PR as we are only going to merge the changes in Auth0.swift.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants