-
Notifications
You must be signed in to change notification settings - Fork 11
Description
When using an external OIDC provider (e.g., Authelia) with a pre-configured public client, token refresh fails because the clientID is not persisted to the authentication secret. The app logs show clientID=(null) during refresh attempts.
Steps to reproduce
- Configure OpenCloud server with Authelia as external OIDC provider (built-in IDP disabled via
OC_EXCLUDE_RUN_SERVICES: "nats,idp,idm") - Configure Authelia with
OpenCloudIOSpublic client (PKCE,grant_types: [authorization_code, refresh_token], redirect_uri:oc://ios.opencloud.eu) - Add account in iOS app and complete initial OIDC authentication - this works successfully
- Wait for access token to expire (~1 hour)
- Reopen app or trigger any API request
Expected behavior
The app should use the stored refresh token to obtain a new access token silently, like the OpenCloud Desktop client does with the same Authelia configuration.
Actual behavior
Token refresh fails. The app logs show clientID=(null) when attempting to load stored credentials:
[AUTH, Openid-Connect, …] Loaded from secret: clientID=(null), clientSecret=(null) [… ClientRegistration] [AUTH, Openid-Connect] Token can't be refreshed due to error=Error Domain=OCError Code=6 "Authorization failed because data was missing from the secret data for the authentication method."
The user sees "Authorization failed - No authentication data has been found for this connection."
Note: OpenCloud Desktop client works correctly with identical Authelia configuration.
Client
- iOS version: 18.6.2
- OpenCloud app version: 1.0.1 (3)
- Device model: iPhone17,1
Server configuration
- OpenCloud version: 4.0.1
- OIDC Provider: Authelia (external)
Logs
iOS app log
[AUTH, Openid-Connect, …] Loaded from secret: clientID=(null), clientSecret=(null) [… ClientRegistration] [OCAuthenticationMethodOpenIDConnect.m:530|FULL] [AUTH, Openid-Connect] OAuth2 token expired (reason: 1:0, date: 2026-01-06 09:15:33 +0000, timeout period: 3599 secs) - refreshing token for connection.. [OCAuthenticationMethodOAuth2.m:691|FULL] [AUTH, Openid-Connect] Token refresh started [OCAuthenticationMethodOAuth2.m:787|FULL] [AUTH, Openid-Connect, …] Loaded from secret: clientID=(null), clientSecret=(null) [… ClientRegistration] [OCAuthenticationMethodOpenIDConnect.m:530|FULL] [AUTH, Openid-Connect] Token can't be refreshed due to error=Error Domain=OCError Code=6 "Authorization failed because data was missing from the secret data for the authentication method. (error 6)" (-[OCAuthenticationMethodOAuth2 __refreshTokenForConnection:availabilityHandler:] [OCAuthenticationMethodOAuth2.m:849])
Root cause analysis
After investigating the source code, the issue appears to be in OCAuthenticationMethodOpenIDConnect.m:
In ios-sdk/OpenCloudSDK/Authentication/OCAuthenticationMethodOpenIDConnect.m:
if (_clientRegistrationResponse != nil)
{
authSecret.clientID = ...
authSecret.clientSecret = ...
}The clientID is only stored when _clientRegistrationResponse != nil. For external OIDC providers with pre-configured public clients, dynamic client registration never occurs, so _clientRegistrationResponse is always nil. This means clientID is never persisted to the authentication secret. In startTokenRefreshWithCompletionHandler: The code retrieves clientID from the stored secret, but since it was never persisted, it returns nil:
NSString *clientID = authSecret.clientID; // Returns nil
NSString *clientSecret = authSecret.clientSecret; // Returns nilSuggested fix
In postProcessAuthenticationDataDict, store the clientID even when dynamic registration was not used:
// Always store the effective clientID, whether from dynamic registration or pre-configured
NSString *effectiveClientID = _clientRegistrationResponse.clientID ?: _clientID ?: [super clientID];
if (effectiveClientID != nil)
{
authSecret.clientID = effectiveClientID;
}
// Only store clientSecret if we have one (from dynamic registration)
if (_clientRegistrationResponse.clientSecret != nil)
{
authSecret.clientSecret = _clientRegistrationResponse.clientSecret;
}Additional context
This issue probably affects all external OIDC providers that use pre-configured public clients (common for PKCE flows)
The Desktop client looks like it handles this correctly
Let me know if I'm on the right path. Sill puzzeling...