Turning creds persistence on/off causes an exception.
Prerequisites:
- Set up OAuth2 sign in to ArcGIS Online.
- Set up request to secured resource ("https://www.arcgis.com/sharing/rest/portals/self" in my case).
- For clean reproducing create new iOS emulator or use one where creds persistence was NOT enabled previously.
Steps to reproduce:
1. Run app with persistence DISABLED. It works OK: shows prompt for login/password and performs authorized requests after that.
2. Close app and launch it with persistence ENABLED. It will fail with an exception:
ArcGISException: code=18006; Invalid token.; An invalid token was used to access https://www.arcgis.com/sharing/rest/oauth2/token.
3. Subsequent runs witn persistence DISABLED will work OK.
4. Subsequent runs witn persistence ENABLED will throw same exception again.
At the same time, running app with enabled OR disabled persistence ONLY (without switching between modes) works well.
App reinstalling does not help.
Code that enables persistence in my app is right from docs and is executed right before 'runApp()' call:
ArcGISEnvironment.authenticationManager.arcGISCredentialStore =
await ArcGISCredentialStore.initPersistentStore();
Exception is thrown by 'oauthCredential.getTokenInfo()' call inside my internal class responsible for extracting OAuth2 token for my own server requests. Not sure why this is happening. I use all methods from SDK. Possibly, there is a call to 'oauth2/token' endpoint with outdated token, and server just returns an error instead of new token? Could you please advice something on this?
Tested via simulators:
- iPhone 15 w/ iOS 17.5
- iPhone 16 w/ iOS 18.4
- iPhone 16 Plus w/ iOS 18.4
Flutter 3.29.3, Dart 3.7.2, ArcGIS Flutter SDK 200.7.
Thanks for the report @EgorFedorov - the team is investigating if there is a bug or a workflow issue. We'll get back again when we've looked into it.
Thanks for the detailed breakdown and reproduction steps — that’s very helpful. Based on your description and the provided code, here’s what’s likely happening and how you can address it:
1. Avoid Mixing Manual Token Handling with Challenge Handler
You're currently using both:
- getToken() to manually fetch tokens.
- handleArcGISAuthenticationChallenge() to respond to SDK challenges.
This can lead to confusion and race conditions. Instead, have your challenge handler use your getToken() method so that there is only ever a single call to OAuthUserCredential.create().
2. Simplify Token Management
Instead of using a CompleterList, consider using a single shared Completer<String>? to avoid redundant token requests:
Completer<String>? _tokenCompleter;
Future<String> getToken() {
if (_tokenCompleter != null) return _tokenCompleter!.future;
_tokenCompleter = Completer<String>();
OAuthUserCredential.create(configuration: _oAuthUserConfiguration)
.then((credential) => credential.getTokenInfo())
.then((tokenInfo) {
_tokenCompleter!.complete(tokenInfo.accessToken);
})
.catchError((e, stackTrace) {
_tokenCompleter!.completeError(e, stackTrace);
});
return _tokenCompleter!.future;
}
This ensures only one token request is active at a time.
3. Ensure Authentication Flow Completes Before Token Use
Make sure that:
- The user is signed in.
- The credential is valid.
- The SDK has had a chance to invoke the challenge handler (if needed).
If you call getToken() too early (e.g., before the user signs in), it may fail.
4. Manually Validate or Clear Invalid Credentials
If you're switching between persistent and non-persistent modes, consider validating or clearing credentials before use:
final credential = ArcGISEnvironment
.authenticationManager.arcGISCredentialStore
.getCredential(uri: _oAuthUserConfiguration.portalUri);
if (credential is OAuthUserCredential) {
try {
await credential.getTokenInfo(); // Validate token
} catch (_) {
// Token is invalid, remove it
ArcGISEnvironment.authenticationManager.arcGISCredentialStore
.remove(credential: credential);
}
}
Suggestion
If possible, please share a minimal reproducible example (MRE) that isolates the issue. This will help confirm whether the problem lies in credential reuse, timing, or SDK behavior.
Summary:
- Let the SDK handle authentication via the challenge handler.
- Avoid mixing manual token fetching with SDK-managed flows.
- Use a single Completer to manage token requests.
- Validate or clear credentials when switching persistence modes.
- Share a minimal example if the issue persists.