Skip to content

fix: keep the credential when a token refresh fails for want of a connection - #162

Merged
christian2denker merged 1 commit into
mainfrom
fix/refresh-connectivity-no-login
Sep 14, 2026
Merged

christian2denker merged 1 commit into
mainfrom
fix/refresh-connectivity-no-login

Conversation

@christian2denker

Copy link
Copy Markdown
Contributor

Problem

ApptiveGridAuthenticator._performCheckAuthentication refreshes a saved token when none is in memory (cold start) or the current one is about to expire. It caught every refresh failure alike and fell through to authenticate():

  • auth server rejects the credential (OpenIdException) → login. Right.
  • no connection, timeout, 5xx from the token endpoint → also login. Wrong — the session is fine, the server just wasn't reachable.

In Apptive Teams this is the craftsman in the basement: everything is cached, the refresh fails on the dead link, and the app opens a login page that cannot load. Mid-session it opens the login browser over a half-filled form, minutes after the read that triggered it was already answered from the cache.

Fix

  • Only OpenIdException clears the credential and leads to authenticate().
  • Any other failure keeps the credential for the next attempt and propagates from checkAuthentication. Callers (getMe, the 401 retry in performApptiveLink, the attachment processor) already handle connectivity errors; an offline-aware client can answer from its cache.
  • performSetup wraps the silent restore in try/finally, so _setupCompleter completes and isAuthenticated never hangs on a failed restore.

Version bumped to 2.3.1 with a CHANGELOG entry.

Tests

  • Rewrote "Storage Provided · Error · Calls authenticate" into "Connection error while refreshing · Keeps the credential, throws, does not log in": checkAuthentication throws, authorize never called, saveCredential(null) never called.
  • New: "Connection error while restoring at setup · Completes setup without a token": isAuthenticatedWithToken resolves to false, no login, credential kept.
  • flutter test --coverage: all passed, 0 uncovered lines. dart format --set-exit-if-changed, dart analyze --fatal-infos clean. flutter pub publish --dry-run only warns about the (then) uncommitted files.

Behaviour change for consumers

checkAuthentication() can now throw where it used to swallow and open the login. Apps that await it directly should expect connectivity errors; the ApptiveGrid client's own callers already propagate them.

🤖 Generated with Claude Code

…nection

_performCheckAuthentication caught every refresh failure alike and fell
through to authenticate(). A rejected credential (OpenIdException) is the
one case where that is right. A refresh that fails because there is no
connection — SocketException, timeout, a 5xx from the token endpoint —
says nothing about the session, yet it sent the user to a login page they
could not load, over an app that could have answered from its cache.

Now only OpenIdException clears the credential and leads to the login. Any
other failure keeps the credential for the next attempt and propagates out
of checkAuthentication, where callers treat it like any other connectivity
failure. performSetup completes in both cases, so isAuthenticated never
hangs on a failed silent restore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the p:core Changes to apptive_grid_core label Sep 14, 2026
@christian2denker
christian2denker merged commit 34eab33 into main Sep 14, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p:core Changes to apptive_grid_core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant