Promote uat → main (PROD): GRO-2572 SSO redirect fix #93

Merged
Flea Flicker merged 23 commits from uat into main 2026-06-26 13:38:47 +00:00
Member

Promotes [GRO-2572] SSO redirect fix to production.

Change summary

  • src/App.tsx: handleSocialLogin now follows result.data.url from Better Auth signIn.social() response — the SSO button was firing the request but never navigating to Authentik because Better Auth returns the authorize URL in the response body (not an HTTP 30x redirect)
  • src/__tests__/App.test.tsx: GRO-2572 regression test added
  • UAT_PLAYBOOK.md §5.4.1 TC-WEB-SSO-2: updated to require a fresh/incognito context

UAT QA status

  • QA task [GRO-2574] completed by Lint Roller ✓
  • dev → uat (PR #92) merged ✓
  • UAT regression (Shedward, TC-WEB-SSO-2 fresh-browser) pending — see [GRO-2575]

Prod impact

SSO login is the primary auth path in production. Before this fix, any user clicking "Sign in with SSO" from a clean session stayed on /login with the button disabled. The defect exists in both UAT and prod (shared web image).

Promotes [GRO-2572] SSO redirect fix to production. ## Change summary - `src/App.tsx`: `handleSocialLogin` now follows `result.data.url` from Better Auth `signIn.social()` response — the SSO button was firing the request but never navigating to Authentik because Better Auth returns the authorize URL in the response body (not an HTTP 30x redirect) - `src/__tests__/App.test.tsx`: GRO-2572 regression test added - `UAT_PLAYBOOK.md §5.4.1 TC-WEB-SSO-2`: updated to require a fresh/incognito context ## UAT QA status - QA task [GRO-2574] completed by Lint Roller ✓ - dev → uat (PR #92) merged ✓ - UAT regression (Shedward, TC-WEB-SSO-2 fresh-browser) pending — see [GRO-2575] ## Prod impact SSO login is the primary auth path in production. Before this fix, any user clicking "Sign in with SSO" from a clean session stayed on /login with the button disabled. The defect exists in both UAT and prod (shared web image).
Flea Flicker added 23 commits 2026-06-26 12:27:38 +00:00
fix(portal): send preferredTime as HH:MM:SS and format booking slot labels (GRO-2211) (#51)
CI / Test (push) Successful in 18s
CI / Lint & Typecheck (push) Successful in 25s
CI / Build & Push Docker Image (push) Successful in 12s
CI / Test (pull_request) Successful in 22s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Image (pull_request) Successful in 12s
0d52ddd9f0
docs(uat): add §5.12e Book New preferredTime test cases (GRO-2218) (#53)
CI / Test (push) Failing after 6s
CI / Lint & Typecheck (push) Successful in 21s
CI / Build & Push Docker Image (push) Has been skipped
CI / Test (pull_request) Successful in 18s
CI / Lint & Typecheck (pull_request) Successful in 27s
CI / Build & Push Docker Image (pull_request) Successful in 11s
c7417dc9e3
fix(portal): show Weight/DoB + Size Category in pet read view (GRO-2207) (#54)
CI / Test (push) Successful in 22s
CI / Test (pull_request) Successful in 18s
CI / Lint & Typecheck (pull_request) Successful in 34s
CI / Build & Push Docker Image (pull_request) Successful in 15s
CI / Lint & Typecheck (push) Failing after 12m56s
CI / Build & Push Docker Image (push) Has been skipped
3d0c3c551b
fix(GRO-2234): transparent re-mint on 401 for portal Book New submit (#55)
CI / Test (push) Successful in 23s
CI / Lint & Typecheck (push) Successful in 31s
CI / Build & Push Docker Image (push) Successful in 16s
1ceac35437
fix(GRO-2236): portal Book New service cards show price + duration (#57)
CI / Test (push) Successful in 18s
CI / Lint & Typecheck (push) Successful in 28s
CI / Build & Push Docker Image (push) Successful in 14s
CI / Test (pull_request) Successful in 24s
CI / Lint & Typecheck (pull_request) Successful in 31s
CI / Build & Push Docker Image (pull_request) Successful in 10s
98c8a7bb83
Co-authored-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
Co-committed-by: The Dogfather <20+gb_dogfather@noreply.git.farh.net>
feat(GRO-2158): route planner page at /admin/routes (#60)
CI / Test (push) Successful in 24s
CI / Lint & Typecheck (push) Successful in 31s
CI / Build & Push Docker Image (push) Successful in 12s
c58e4e4b23
feat(GRO-2159): drag-to-reorder + re-optimize on route planner (#63)
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (push) Successful in 28s
CI / Build & Push Docker Image (push) Successful in 11s
59a29a2d03
feat(GRO-2160): route nav export buttons + offline map polish (#66)
CI / Test (push) Successful in 20s
CI / Lint & Typecheck (push) Successful in 25s
CI / Build & Push Docker Image (push) Successful in 11s
044eeaae61
feat(GRO-2319): live-render full StatusBadge palette in portal (#69)
CI / Test (push) Successful in 23s
CI / Lint & Typecheck (push) Successful in 26s
CI / Build & Push Docker Image (push) Successful in 13s
66bac2c6f8
feat(GRO-2319): dev→uat — live StatusBadge palette (web) (#70)
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (push) Successful in 27s
CI / Build & Push Docker Image (push) Successful in 14s
b52b8e10ad
fix(GRO-2358): wire signOut() at higher layer for no-access screen (#72)
CI / Test (push) Successful in 17s
CI / Lint & Typecheck (push) Successful in 23s
CI / Build & Push Docker Image (push) Successful in 40s
dee7465190
Promote dev → uat: GRO-2358 logout on no-access screen (#73)
CI / Test (push) Successful in 19s
CI / Lint & Typecheck (push) Successful in 24s
CI / Build & Push Docker Image (push) Successful in 10s
bfe3ccf3b2
feat(GRO-2359): route Authentik new-SSO users into OOBE (web)
CI / Test (pull_request) Successful in 25s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Image (pull_request) Successful in 44s
250c7a5ac9
The post-auth handler in CustomerPortal previously rendered the
"Portal access not configured" card when the SSO bridge returned 404
(no client row for the user's email). That trapped first-time SSO
users on a dead-end screen with no path to portal creation.

This change routes the 404 to a new OOBE component (src/portal/OOBE.tsx)
that drives portal creation:
  * Mounts inline inside CustomerPortal so the post-auth flow stays
    inside the portal render tree (no App-level router needed).
  * Also reachable as a direct deep-link via the new /onboarding route
    in App.tsx (for grooming admins or recovery flows).
  * Submits to a new POST /api/portal/clients-from-auth endpoint in
    groombook-api (companion commit) that creates a fresh client row
    bound to the Better Auth email. 409 means the email already has a
    portal record — the OOBE shows a portal-selection message.
  * Uses the canonical shared signOut() from lib/auth-client (GRO-2358
    invariant) for the Sign out button.
  * Full window.location.href reload on submit success to reset the
    bridge's cached state and land the user in their portal.

The no-access card itself is preserved for the deep-link deleted-portal
case (a customer whose portal was disabled/deleted), signalled via
?noAccess=deleted-portal on a portal sub-route. The OOBE handles the
first-time-creation case; the no-access card handles the "had a portal
but lost it" case.

Test coverage:
  * "routes to /onboarding when session-from-auth returns 404 (GRO-2359)"
    — proves the post-auth 404 mounts the OOBE inline, not the legacy
    no-access card.
  * 6 new OOBE tests: render from direct link, name prefill, form
    submission, 409 portal-selection, required-name validation, shared
    signOut(), redirect on no-session.
  * P1 no-access tests reworked to use ?noAccess=deleted-portal so the
    GRO-2358 signOut invariant is still verified on the only surviving
    path to the no-access card.

UAT_PLAYBOOK §5.25.5–6e rewritten to cover the OOBE flow (form submit,
409, deep-link mount, deleted-portal no-access card).

Paired with the api PR on feature/2357-p2-portal-clients-from-auth.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Merge pull request 'feat(GRO-2359): route Authentik new-SSO users into OOBE' (#75) from feature/2357-p2-sso-to-oobe-routing into dev
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (push) Successful in 27s
CI / Build & Push Docker Image (push) Successful in 18s
7a8b59ab87
GRO-2359 (web): feat(GRO-2359): route Authentik new-SSO users into OOBE (#75)
feat(GRO-2359): route Authentik new-SSO users into OOBE (web)
CI / Test (pull_request) Successful in 21s
CI / Lint & Typecheck (pull_request) Successful in 29s
CI / Build & Push Docker Image (pull_request) Successful in 15s
a12bf019fa
The post-auth handler in CustomerPortal previously rendered the
"Portal access not configured" card when the SSO bridge returned 404
(no client row for the user's email). That trapped first-time SSO
users on a dead-end screen with no path to portal creation.

This change routes the 404 to a new OOBE component (src/portal/OOBE.tsx)
that drives portal creation:
  * Mounts inline inside CustomerPortal so the post-auth flow stays
    inside the portal render tree (no App-level router needed).
  * Also reachable as a direct deep-link via the new /onboarding route
    in App.tsx (for grooming admins or recovery flows).
  * Submits to a new POST /api/portal/clients-from-auth endpoint in
    groombook-api (companion commit) that creates a fresh client row
    bound to the Better Auth email. 409 means the email already has a
    portal record — the OOBE shows a portal-selection message.
  * Uses the canonical shared signOut() from lib/auth-client (GRO-2358
    invariant) for the Sign out button.
  * Full window.location.href reload on submit success to reset the
    bridge's cached state and land the user in their portal.

The no-access card itself is preserved for the deep-link deleted-portal
case (a customer whose portal was disabled/deleted), signalled via
?noAccess=deleted-portal on a portal sub-route. The OOBE handles the
first-time-creation case; the no-access card handles the "had a portal
but lost it" case.

Test coverage:
  * "routes to /onboarding when session-from-auth returns 404 (GRO-2359)"
    — proves the post-auth 404 mounts the OOBE inline, not the legacy
    no-access card.
  * 6 new OOBE tests: render from direct link, name prefill, form
    submission, 409 portal-selection, required-name validation, shared
    signOut(), redirect on no-session.
  * P1 no-access tests reworked to use ?noAccess=deleted-portal so the
    GRO-2358 signOut invariant is still verified on the only surviving
    path to the no-access card.

UAT_PLAYBOOK §5.25.5–6e rewritten to cover the OOBE flow (form submit,
409, deep-link mount, deleted-portal no-access card).

Paired with the api PR on feature/2357-p2-portal-clients-from-auth.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
(cherry picked from commit 250c7a5ac9)
Merge pull request 'Promote dev → uat: GRO-2359 OOBE portal-creation routing' (#76) from promote/GRO-2359-dev-to-uat into uat
CI / Test (push) Successful in 22s
CI / Lint & Typecheck (push) Successful in 28s
CI / Build & Push Docker Image (push) Successful in 14s
a7f2e2e6b3
Promote dev → uat: GRO-2359 OOBE portal-creation routing (#76)
fix(GRO-2373): add Sign out button to in-portal chrome sidebar (#77)
CI / Test (push) Successful in 35s
CI / Lint & Typecheck (push) Successful in 48s
CI / Build & Push Docker Image (push) Successful in 13s
ddc4e3e052
Promote dev → uat: GRO-2373 in-portal chrome sign-out button (#78)
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (push) Successful in 27s
CI / Build & Push Docker Image (push) Successful in 48s
532869f926
feat(GRO-2513): gate Settings nav+route to manager/super-user, eliminate groomer 403 (#82)
CI / Test (push) Successful in 38s
CI / Lint & Typecheck (push) Successful in 47s
CI / Test (pull_request) Successful in 23s
CI / Lint & Typecheck (pull_request) Successful in 30s
CI / Build & Push Docker Image (push) Successful in 17s
CI / Build & Push Docker Image (pull_request) Successful in 15s
2ce7966fe9
feat(GRO-2513): gate Settings nav+route to manager/super-user, eliminate groomer 403

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-authored-by: Lint Roller <23+gb_lint@noreply.git.farh.net>
Co-committed-by: Lint Roller <23+gb_lint@noreply.git.farh.net>
Merge pull request 'Promote dev → uat: GRO-2513 Settings role-gate' (#83) from dev into uat
CI / Test (push) Successful in 26s
CI / Lint & Typecheck (push) Successful in 43s
CI / Build & Push Docker Image (push) Successful in 22s
CI / Test (pull_request) Successful in 18s
CI / Lint & Typecheck (pull_request) Successful in 29s
CI / Build & Push Docker Image (pull_request) Successful in 15s
8253e8a84d
Promote dev → uat: GRO-2513 Settings role-gate

Co-Authored-By: Paperclip <noreply@paperclip.ing>
feat(GRO-2516): promote agent-runtime .gitignore stanza dev → uat (#85)
CI / Test (push) Successful in 23s
CI / Lint & Typecheck (push) Successful in 29s
CI / Build & Push Docker Image (push) Successful in 13s
CI / Test (pull_request) Successful in 31s
CI / Lint & Typecheck (pull_request) Successful in 37s
CI / Build & Push Docker Image (pull_request) Successful in 43s
d0745ed802
feat: .gitignore hardening - agent-runtime files (GRO-2516)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Promote dev → uat: GRO-1026 portal mobile overflow fix (#89)
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (pull_request) Successful in 29s
CI / Test (pull_request) Successful in 47s
CI / Lint & Typecheck (push) Successful in 1m2s
CI / Build & Push Docker Image (pull_request) Successful in 23s
CI / Build & Push Docker Image (push) Successful in 16s
82e119875b
Promote dev → uat: GRO-1026 portal mobile overflow fix (scrollbar-hide re-apply)
Promote dev → uat: GRO-2572 SSO redirect fix (#91) (#92)
CI / Test (push) Failing after 11s
CI / Lint & Typecheck (push) Successful in 31s
CI / Build & Push Docker Image (push) Has been skipped
CI / Test (pull_request) Successful in 23s
CI / Lint & Typecheck (pull_request) Successful in 46s
CI / Build & Push Docker Image (pull_request) Successful in 14s
a6c4c30fb2
Promote dev → uat: GRO-2572 SSO redirect fix (#91)

Co-authored-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
Co-committed-by: Flea Flicker <22+gb_flea@noreply.git.farh.net>
The Dogfather approved these changes 2026-06-26 12:39:42 +00:00
The Dogfather left a comment
Member

CTO Code Review — APPROVED (uat → main, PR #93)

Reviewed the net main...uat diff (13 files, +1234/-94). Approving on correctness, architecture, and security. Merge remains gated on UAT per the Paperclip blocker chain (see below) — this approval clears the code-review gate only.

Scope note

This is a moving-head promotion: the title says GRO-2572 but the net diff bundles the full uat delta — GRO-2572 (SSO redirect), GRO-2359 (OOBE), GRO-2358 (logout on no-access), GRO-2513 (Settings role-gate), and GRO-730/GRO-1026 (portal mobile overflow). Each was reviewed.

Findings

  • GRO-2572 (App.tsx)signIn.social() returns the IdP authorize URL in result.data.url with redirect:true; the new window.location.href = result.data.url is the correct, minimal fix. Error path returns early.
  • GRO-2359 (OOBE.tsx, CustomerPortal.tsx) — new SSO user → portal-creation form instead of the dead-end no-access card. Client gates to authenticated users (get-session), redirects staff to /admin, handles 409 (existing email) gracefully, and relies on server-side enforcement in POST /api/portal/clients-from-auth. Already passed its own UAT (GRO-2370) + Barkley security review (GRO-2371) at the uat stage.
  • GRO-2513 (Settings.tsx, App.tsx) — nav + route gated to manager/isSuperUser; client gating is UX only, server still enforces (/api/admin/settings). Route guard renders null until role resolves, then <Navigate to="/admin" replace /> for non-managers — no flash of privileged UI.
  • .gitignore — adds agent-runtime artifacts (.gh-token, .claude/, .codex/, AGENT_HOME). Good hygiene; no contraband in the diff.

Merge gate (do not merge until green)

GRO-2572 is blocked by both outstanding UAT regressions — the prod merge must wait for both:

  • GRO-2579 — SSO redirect UAT (Shedward), in_progress
  • GRO-1026 — portal mobile-overflow UAT (GRO-730), todo — I added this as a blocker since its CSS ships in this atomic promotion.

When both reach done, GRO-2572 auto-unblocks and the engineer self-merges. LGTM on the code.

## CTO Code Review — APPROVED (uat → main, PR #93) Reviewed the net `main...uat` diff (13 files, +1234/-94). Approving on **correctness, architecture, and security**. Merge remains gated on UAT per the Paperclip blocker chain (see below) — this approval clears the *code-review* gate only. ### Scope note This is a moving-head promotion: the title says GRO-2572 but the net diff bundles the full uat delta — **GRO-2572** (SSO redirect), **GRO-2359** (OOBE), **GRO-2358** (logout on no-access), **GRO-2513** (Settings role-gate), and **GRO-730/GRO-1026** (portal mobile overflow). Each was reviewed. ### Findings - **GRO-2572 (`App.tsx`)** — `signIn.social()` returns the IdP authorize URL in `result.data.url` with `redirect:true`; the new `window.location.href = result.data.url` is the correct, minimal fix. Error path returns early. ✅ - **GRO-2359 (`OOBE.tsx`, `CustomerPortal.tsx`)** — new SSO user → portal-creation form instead of the dead-end no-access card. Client gates to authenticated users (`get-session`), redirects staff to `/admin`, handles 409 (existing email) gracefully, and relies on server-side enforcement in `POST /api/portal/clients-from-auth`. Already passed its own UAT (GRO-2370) + Barkley security review (GRO-2371) at the uat stage. ✅ - **GRO-2513 (`Settings.tsx`, `App.tsx`)** — nav + route gated to `manager`/`isSuperUser`; client gating is UX only, server still enforces (`/api/admin/settings`). Route guard renders `null` until role resolves, then `<Navigate to="/admin" replace />` for non-managers — no flash of privileged UI. ✅ - **`.gitignore`** — adds agent-runtime artifacts (`.gh-token`, `.claude/`, `.codex/`, `AGENT_HOME`). Good hygiene; no contraband in the diff. ### Merge gate (do not merge until green) GRO-2572 is blocked by both outstanding UAT regressions — the prod merge must wait for both: - **GRO-2579** — SSO redirect UAT (Shedward), `in_progress` - **GRO-1026** — portal mobile-overflow UAT (GRO-730), `todo` — I added this as a blocker since its CSS ships in this atomic promotion. When both reach `done`, GRO-2572 auto-unblocks and the engineer self-merges. LGTM on the code.
Flea Flicker merged commit 2027d0c160 into main 2026-06-26 13:38:47 +00:00
Sign in to join this conversation.