Title
[BUG] Refresh token may be reused after rotation between app and File Provider, causing invalid_grant
Steps to reproduce
- Configure the iOS app against an OpenCloud server that uses an external OIDC provider.
- In this setup, the external IdP is Nauthilus.
- Log in successfully on iPhone.
- Let the app run for some time so token refresh happens in the background.
- Use both the main app and the File Provider extension over time.
- Eventually one of the clients fails with
invalid_grant during grant_type=refresh_token.
Expected behavior
A refresh token issued to the iOS app should remain usable until it is rotated, and all iOS components sharing the same account state should consistently use the newest refresh token.
Actual behavior
After a successful refresh, a later refresh request is sent again with an older refresh token, and the IdP responds with:
{"error":"invalid_grant"}
After that, the app reports authorization failure and may consider the refresh token invalid until the account is re-authenticated.
Why this looks different from existing issues
This does not look like a pure external IdP discovery/client configuration issue such as:
It also looks more specific than the general "session is not refreshed" report in #16.
From the logs, this looks like a stale refresh token is reused after rotation, possibly between:
- the main iOS app (
OpenCloud)
- the File Provider extension (
OpenCloud File Provider)
Evidence from logs
A successful refresh returned a new refresh token at:
2026-04-14 11:28:45 +0200
Later, that same newly issued refresh token was reused and rejected with invalid_grant at:
2026-04-14 11:34:01 +0200
2026-04-14 12:07:27 +0200
The interesting part is that the failing requests appear to come from different iOS processes:
OpenCloud File Provider
OpenCloud
That suggests token rotation may succeed in one process, but the updated refresh token is not reliably persisted or synchronized to the other process.
Example client log pattern
Successful refresh:
- old refresh token used
- the IdP returns a new refresh token
Later failure:
- the same refresh token that was previously issued is sent again
- the IdP returns
400 {"error":"invalid_grant"}
After that, the client starts logging messages like:
Authorization failed. (error 3, openid-connect: invalid_grant)
Previous token refresh attempts indicated an invalid refresh token
Environment
- iOS version: 26.4.1
- OpenCloud app version: 1.0.1 (build 3)
- Device: iPhone
- OpenCloud server with external OIDC provider
- External IdP: Nauthilus
- Same account used in app and File Provider extension
Important note
On the IdP side, the token endpoint behavior appears correct.
The Nauthilus logs show that the failing requests are grant_type=refresh_token requests returning 400, and the server-side analysis indicates that the rejected token was already invalid, expired, or already rotated.
This suggests the problem is likely on the iOS client side, in how the latest refresh token is stored and shared across processes.
Question / hypothesis
Could there be a race or persistence problem in the shared account/token storage between the main app and the File Provider extension, where one process rotates the refresh token but another process still uses the previous value?
Title
[BUG] Refresh token may be reused after rotation between app and File Provider, causing
invalid_grantSteps to reproduce
invalid_grantduringgrant_type=refresh_token.Expected behavior
A refresh token issued to the iOS app should remain usable until it is rotated, and all iOS components sharing the same account state should consistently use the newest refresh token.
Actual behavior
After a successful refresh, a later refresh request is sent again with an older refresh token, and the IdP responds with:
{"error":"invalid_grant"}After that, the app reports authorization failure and may consider the refresh token invalid until the account is re-authenticated.
Why this looks different from existing issues
This does not look like a pure external IdP discovery/client configuration issue such as:
It also looks more specific than the general "session is not refreshed" report in #16.
From the logs, this looks like a stale refresh token is reused after rotation, possibly between:
OpenCloud)OpenCloud File Provider)Evidence from logs
A successful refresh returned a new refresh token at:
2026-04-14 11:28:45 +0200Later, that same newly issued refresh token was reused and rejected with
invalid_grantat:2026-04-14 11:34:01 +02002026-04-14 12:07:27 +0200The interesting part is that the failing requests appear to come from different iOS processes:
OpenCloud File ProviderOpenCloudThat suggests token rotation may succeed in one process, but the updated refresh token is not reliably persisted or synchronized to the other process.
Example client log pattern
Successful refresh:
Later failure:
400 {"error":"invalid_grant"}After that, the client starts logging messages like:
Authorization failed. (error 3, openid-connect: invalid_grant)Previous token refresh attempts indicated an invalid refresh tokenEnvironment
Important note
On the IdP side, the token endpoint behavior appears correct.
The Nauthilus logs show that the failing requests are
grant_type=refresh_tokenrequests returning400, and the server-side analysis indicates that the rejected token was already invalid, expired, or already rotated.This suggests the problem is likely on the iOS client side, in how the latest refresh token is stored and shared across processes.
Question / hypothesis
Could there be a race or persistence problem in the shared account/token storage between the main app and the File Provider extension, where one process rotates the refresh token but another process still uses the previous value?