Skip to content

fix(authenticator): redirect failed /auth/callback into the SPA with auth_error (release-2026.07.1) - #2042

Merged
cyberantonz merged 1 commit into
constructorfabric:release-2026.07.1from
cyberantonz:backport/callback-auth-error-redirect-2026.07.1
Jul 30, 2026
Merged

fix(authenticator): redirect failed /auth/callback into the SPA with auth_error (release-2026.07.1)#2042
cyberantonz merged 1 commit into
constructorfabric:release-2026.07.1from
cyberantonz:backport/callback-auth-error-redirect-2026.07.1

Conversation

@cyberantonz

Copy link
Copy Markdown
Contributor

Summary

Backport of #2040 to release-2026.07.1 — clean cherry-pick of the squash commit 70650df9. Fixes #2032 on the release line.

Every browser-facing /auth/callback failure now 302s to default_return_to with a fixed auth_error=<reason> (state_expired, idp_error, invalid_callback, exchange_failed, access_denied) instead of dead-ending the browser on problem+json. Rate-limit (429) and internal (5xx) responses stay problem+json; state consumption, session-fixation guard, and audit emissions unchanged. Includes the PRD/DESIGN/openapi updates.

Deploy order

Merge/deploy the FE backport insight-front#241 first — without the SPA loop guard a persistent failure (e.g. unknown person) would bounce between the SPA's auto-login and the IdP.

Test plan

On this branch (release base): run-e2e.sh full suite green (12 tests + endpoint-coverage gate); cargo test -p authenticator 52 unit tests; clippy clean.

🤖 Generated with Claude Code

…auth_error (constructorfabric#2040)

* fix(authenticator): redirect failed /auth/callback into the SPA with auth_error

A failed OIDC callback answered problem+json straight to the browser —
no page is loaded at that point, so an expired login state (the 300 s
Redis TTL, constructorfabric#2032), an IdP-reported error, a replayed callback, or a
denied person dead-ended the login on raw JSON.

Every browser-facing callback failure now 302s to default_return_to
with a fixed auth_error=<reason> query parameter (state_expired,
idp_error, invalid_callback, exchange_failed, access_denied) so the SPA
can restart the login from scratch. The reason vocabulary is fixed —
nothing IdP- or caller-supplied reaches the Location header. Rate-limit
(429) and internal (5xx) responses stay problem+json.

The SPA counterpart (consume auth_error, auto-retry once behind a loop
guard, error screen for access_denied/repeated failures) must be
deployed first: without it a persistent failure such as an unknown
person would bounce between the SPA's auto-login and the IdP.

Closes constructorfabric#2032

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Anton Zelenov <antonz@constructor.tech>

* fix(authenticator): review follow-ups for the auth_error redirect

- The view-as override deny (unknown target) is a browser-facing
  callback failure too — bounce it as access_denied instead of 403
  problem+json (still a denial, never a fallback to the caller).
- PersonError became unused with that — drop the type.
- Log the IdP's error_description alongside error (sanitized): the only
  place the failure cause survives now that the browser gets a redirect.
- Validate default_return_to (site-relative, no fragment, no control
  chars): it lands verbatim in Location headers, and a fragment would
  hide auth_error= from the SPA's loop guard.
- Make the new e2e states unique per run so suite re-runs within the
  per-state rate-limit window don't flake.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Anton Zelenov <antonz@constructor.tech>

* test(authenticator): unknown override target now bounces as access_denied

The e2e pinned the old 403 problem+json; the denial is a 302
auth_error=access_denied redirect since the constructorfabric#2032 change (still no
session minted, never a fallback to the caller).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Anton Zelenov <antonz@constructor.tech>

---------

Signed-off-by: Anton Zelenov <antonz@constructor.tech>
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 05:21
@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: 45152b96-a43c-43fa-8e0b-ea96ed1e33f0

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 59cd8eb into constructorfabric:release-2026.07.1 Jul 30, 2026
2 checks passed
@cyberantonz
cyberantonz deleted the backport/callback-auth-error-redirect-2026.07.1 branch July 30, 2026 05:22
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