Skip to content

OIDC silent-refresh churn causes spurious logouts & token/userinfo storm in Thunderbird #951

Description

@aaspinwall

Describe the bug

The Send web app's OIDC access-token refresh design causes uncontrolled, concurrent silent refreshes and spurious logouts inside Thunderbird.

Three factors combine:

  1. automaticSilentRenew: true schedules background renewals, and getAccessToken() also calls userManager.signinSilent() on demand whenever the token is expired. There is no deduplication, so concurrent callers (an api.call before a request, checkAuthStatus, the 60s add-on menu timer, and the concurrent parts of a file upload) each fire their own signinSilent().
  2. With refresh-token rotation, every extra concurrent refresh races the others; the losers use an already-rotated refresh token and fail with invalid_grant, which reads as a spurious logout.
  3. loadUserInfo: true adds a userinfo round-trip after every refresh. That call can fail inside Thunderbird and reject an otherwise-successful token refresh. The library's fallback also uses an iframe flow that cannot work inside Thunderbird.

The visible symptoms are a storm of token / userinfo requests, transient auth failures that surface as spurious logouts, and downstream breakage such as GET users/me failing at load (leaving the user store unpopulated — see companion upload issue).

To Reproduce

Steps to reproduce the behavior:

  1. Use Send inside Thunderbird past the short-lived access-token expiry (~5 min).
  2. Trigger multiple concurrent authenticated requests (e.g. a multi-part file upload, or normal dashboard load with the menu timer running).
  3. Observe repeated token and userinfo requests and intermittent logout / "not signed in" behavior even though the session (refresh token) is still valid.

Expected behavior

A single in-flight refresh is shared by concurrent callers. A transient failure (network/timeout/userinfo) keeps the session intact and is retried later; only a genuine auth failure (refresh token revoked/expired, e.g. invalid_grant / login_required / session_expired) ends the session and notifies the add-on to revert its menu to logged-out.

Actual behavior

Multiple concurrent signinSilent() calls race under refresh-token rotation; losers fail with invalid_grant. userinfo failures reject otherwise-successful refreshes, and the iframe fallback cannot run in Thunderbird. The result is a token/userinfo request storm and spurious logouts / unpopulated session state.

Screenshots

Network tab shows a large number of repeated token (POST) and userinfo (GET) requests.

System

  • OS: macOS
  • Browser Version: Thunderbird (add-on / system add-on build)

Additional context

Fixed on branch fix/addon-expired-token-closes-send-tabs in packages/send/frontend/src/stores/auth-store.ts:

  • automaticSilentRenew: false and loadUserInfo: false (profile claims already come from the id_token) to remove uncontrolled background renews and the fragile post-refresh round-trip.
  • A shared in-flight refreshAccessToken() promise dedupes concurrent refreshes.
  • Distinguishes genuine auth failures (invalid_grant, login_required, session_expired) — which clear login state and notify the add-on via SIGN_OUT — from transient failures, which preserve the session for a later retry.

This is the follow-up noted in PR #949 ("Posting SIGN_OUT on silent-refresh failure would let the menu revert to logged-out cleanly").

Metadata

Metadata

Assignees

Labels

Add-onThis is an issue with the Thunderbird Pro add-onSendThis is an issue with Thunderbird Send's backend/frontend components

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions