Skip to content

feat(auth): restart login on auth_error callback bounces, loop-guarded (release-2026.07.1) - #241

Merged
cyberantonz merged 3 commits into
constructorfabric:release-2026.07.1from
cyberantonz:backport/auth-error-loop-guard-2026.07.1
Jul 30, 2026
Merged

feat(auth): restart login on auth_error callback bounces, loop-guarded (release-2026.07.1)#241
cyberantonz merged 3 commits into
constructorfabric:release-2026.07.1from
cyberantonz:backport/auth-error-loop-guard-2026.07.1

Conversation

@cyberantonz

Copy link
Copy Markdown
Contributor

Summary

Backport of #239 to release-2026.07.1 — cherry-picks of ff4e199, aafbc4c, 384ab72 (clean, no conflicts). FE half of constructorfabric/insight#2032.

The SPA now consumes the authenticator's ?auth_error=<reason> callback-failure bounce: a live session ignores it, retryable reasons (state_expired, idp_error, invalid_callback, exchange_failed) restart the login once, and a sessionStorage attempt counter (fail-closed when storage is unavailable or writes fail) halts persistent failures on a full-page error screen with a manual "Try again" instead of looping browser → IdP. access_denied never auto-retries. Also resets the signIn redirect guard on bfcache restores and preserves the URL hash in the default return_to.

Deploy order

Safe ahead of the authenticator change (boot path unchanged without the parameter); the authenticator's redirect change must not ship first.

Test plan

On this branch (release base): npm run test — 703 passed; tsc -b and eslint src/ clean.

Part of constructorfabric/insight#2032

🤖 Generated with Claude Code

cyberantonz and others added 3 commits July 30, 2026 12:49
A failed OIDC callback used to dead-end the browser on the
authenticator's problem+json — nothing was loaded at /auth/callback, so
an expired login state (the 300 s Redis TTL, insight#2032) simply
stopped the login. The authenticator now 302s such failures back to the
SPA with a fixed auth_error=<reason> query parameter.

Boot consumes the parameter before the session probe: a stale bounce on
a live session is ignored, retryable reasons (state_expired, idp_error,
invalid_callback, exchange_failed) restart the login once, and a
sessionStorage attempt counter halts persistent failures on a new
full-page error screen with a manual retry instead of looping
browser -> IdP. access_denied never auto-retries — a silent SSO hop
would just reproduce it.

Safe to deploy ahead of the authenticator change: without the new
parameter the boot path is unchanged.

Part of insight#2032

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Anton Zelenov <antonz@constructor.tech>
- signIn() with no arg on auto-retry and the error screen's button:
  return to the current URL (already stripped of auth_error) instead of
  hardcoding "/", so a non-root default_return_to survives the bounce.
- Strip an empty ?auth_error= without counting it against the retry
  budget (the authenticator always sends a reason; empty means a
  hand-crafted URL) so it can't ride into return_to and stick.
- Reset the signIn redirect guard on bfcache restores (pageshow with
  persisted) — browser Back from the IdP used to leave the module's
  redirecting flag true, turning the "Try again" button into a silent
  no-op.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Anton Zelenov <antonz@constructor.tech>
- Do not auto-retry an attempt that could not be persisted (getItem
  works, setItem throws): the next bounce would read zero again and the
  loop guard would never trip. writeAttempts now reports success and
  autoRetry requires it.
- Preserve the URL hash in signIn's default return_to (covers both the
  auto-retry and the error screen's Try-again path).
- Semantic markup on the login-error screen: main landmark, h1 title,
  role=alert on the message so screen readers announce it on mount.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Anton Zelenov <antonz@constructor.tech>
@cyberantonz
cyberantonz requested a review from a team as a code owner July 30, 2026 04:50
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e8c68b0f-da9c-44cb-9721-104c54640742

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cyberantonz
cyberantonz merged commit a4f4ea8 into constructorfabric:release-2026.07.1 Jul 30, 2026
6 checks passed
@cyberantonz
cyberantonz deleted the backport/auth-error-loop-guard-2026.07.1 branch July 30, 2026 05:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant