Thanks to visit codestin.com
Credit goes to github.com

Skip to content

bug(site): External Authentication page shows a stale "token failed to validate" error next to an "Authenticated" badge #28876

Description

@blinkagent

Summary

On /settings/external-auth, a provider row can simultaneously render:

  • an Authenticated button (disabled, i.e. treated as signed in), and
  • Error: token failed to validate next to the provider name,

while a "Test Validate…" run pops the Application link is valid. toast. These three signals contradict each other.

Reproduced after re-authenticating a GitHub external auth link. A hard page reload clears the error.

Root cause

The badge and the error text are sourced from two different react-query entries that are never resynced.

site/src/pages/UserSettingsPage/ExternalAuthPage/ExternalAuthPageView.tsx:

const authenticated = externalAuth
  ? externalAuth.authenticated      // ["external-auth", providerId] -> GET /external-auth/{id}
  : (link?.authenticated ?? false); // ["external-auth"]             -> GET /external-auth
...
{link?.validate_error && (
  <span>
    <span className="pl-[1em] text-content-destructive">Error: </span>
    {link?.validate_error}
  </span>
)}
  • The button reads the per-provider query, backed by externalAuthByID (coderd/externalauth.go), which calls config.ValidateToken() live.
  • The error text reads the list query, backed by listUserExternalAuths (coderd/externalauth.go), which calls cfg.RefreshToken() and records meta.ValidateError = err.Error().

validateExternalAuth in site/src/api/queries/externalAuth.ts only writes the child cache key and never invalidates the list:

export const validateExternalAuth = (queryClient: QueryClient) => ({
  mutationFn: API.getExternalAuthProvider,
  onSuccess: (data, providerId) => {
    queryClient.setQueryData(["external-auth", providerId], data);
  },
});

The re-auth popup path has the same gap: startPollingExternalAuth only polls externalAuthProvider(providerID). Only unlinkExternalAuths invalidates ["external-auth"]. So once the list query has captured a validate_error at page load, nothing short of a reload clears it — even though the per-provider query has since flipped to authenticated: true.

Secondary contributor

The two endpoints don't execute the same logic, so they can legitimately disagree even within a single page load:

  • List path: RefreshTokenvalidateWithRetry → returns InvalidTokenError("token failed to validate") after a 1s bounded retry loop (coderd/externalauth/externalauth.go).
  • By-ID path: ValidateToken directly — no refresh, no retry.

A transient 401 from the provider immediately after a refresh (the case the retry loop in validateWithRetry was added for) will surface as an error on the list while the by-ID call succeeds.

Steps to reproduce

  1. Have a GitHub external auth link that is in a failed-validation state.
  2. Load /settings/external-auth — the row shows Error: token failed to validate.
  3. Re-authenticate via the login popup, or use the row menu → "Test Validate…".
  4. The button flips to Authenticated and a Application link is valid. toast appears, but the error text remains.

Expected

The error should not render alongside an Authenticated state.

Suggested fix

  1. Have validateExternalAuth.onSuccess (and the polling success path) also invalidateQueries({ queryKey: ["external-auth"], exact: true }) so the list refetches.
  2. Additionally / alternatively, gate the error render on !authenticated so a stale validate_error can never appear next to an Authenticated badge.

Option 2 alone fixes the visual contradiction; option 1 is needed for the displayed error to actually reflect current state.


Created on behalf of @jakehwll

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions