Phase 05 Plan S09: Staging Switch and Rollback Rehearsal Summary¶
Ordinary staging was switched to the BFF with native Identity, the live matrix passed on OpenIddict, and the rollback was rehearsed and passed. After S09, staging runs the BFF on Auth0 until S27 reapplies OpenIddict.
Timeline¶
| When (UTC) | Event | Revision |
|---|---|---|
| Before 2026-10-04 | Staging users imported from Auth0: 4 imported/matched, no emails sent | camaradesuk/cluster-gitops#1537, #1538, #1539 |
| 2026-10-04 04:14:43 | Switch: Web and API to the BFF plus native Identity, generation s09-staging-openiddict-1, same-origin /api (#1528), 2 API replicas, Swagger on the syrf-swagger PKCE client |
camaradesuk/cluster-gitops#1529 |
| 2026-10-05 / 2026-10-07 | Task 2 live matrix on OpenIddict: extended rows 10-05, base rows 10-07 | evidence files below |
| 2026-10-06 | Chris redefines Task 3 as option B | |
| 2026-10-07 04:31:00 | Rollback (option B): BFF stays enabled, provider auth0, generation s09-staging-auth0-rollback-2. Rollout to 2/2 ready in 3 m 18 s |
camaradesuk/cluster-gitops#1530 |
| 2026-10-07 | Task 3 live run on the BFF with Auth0 | evidence file below |
Task 2: live matrix on OpenIddict (passed)¶
Evidence (redaction-checked, mode 0600, git-ignored): .superpowers/sdd/auth-s30-2026-09-25/ ->
s09-task2-evidence-2026-10-07.json and s09-task2-extended-evidence-2026-10-05.json.
- Base rows: password sign-in,
/api/auth/me, admin/account, protected API, SignalR reconnect, refresh/logout, emailed reset, two replicas sharing one session (2/2), Swagger PKCE without a client secret, and claims. - Extended rows: optional MFA (authenticator and recovery codes, including that a recovery code is single-use), passkeys (add, sign in, remove), and Google unlink after an emailed step-up.
- Google sign-in is operator-attested by Chris, because the run host is headless.
- Old-cookie rejection was not part of Task 2 (
old_cookies_unauthorized_checked: 0); it belongs to Task 3.
Task 3: rollback rehearsal, option B (passed)¶
What changed. The plan said to restore direct Auth0 with the BFF disabled. On 2026-10-06 Chris changed it: the BFF stays enabled and its provider swaps OpenIddict to Auth0. That is the same shape as the production S13 rollback and the S11 baseline, so the rehearsal exercises the path production would take.
GitOps change (#1530). Provider auth0, authority https://signin.syrf.org.uk, a new Auth0 Regular
Web App "SyRF staging BFF", its secret in GCP Secret Manager syrf-auth0-bff-staging through ExternalSecret
auth0-bff, identityProvider: auth0, and Swagger kept on OpenIddict. Evidence:
s09-task3-option-b-evidence-2026-10-07.json.
Live run, 2026-10-07 (result: pass):
- The retained OpenIddict-generation cookie was refused (401) on both replicas.
- A fresh Auth0 sign-in through the BFF was accepted on both replicas.
/me, protected API, SignalR and refresh/logout passed.- Auth0 IdP logout was proven: the next sign-in shows Universal Login.
- Not run, by design: the emailed reset row and the admin row, because the Auth0 tenant is shared
with production. The evidence records both under
not_run. - Google is operator-attested. Chris's manual checks passed: Auth0 password, Google, the account page, a project, the admin page, and staying signed out after sign-out.
Recovery time was not measured separately. The only figure is the 3 m 18 s rollout to 2/2 ready.
Bugs found and fixed¶
| PR | Found in | Fix |
|---|---|---|
| #4026 | Task 2 | Passkey Register failed silently on an implicit [Required] binding; also PasswordRetry; a reflection guard test now pins it |
| #4030 | Task 2 | Post-sign-in redirects: an authenticated Identity root goes to the web app, reset keeps returnUrl, a Google link returns to ExternalLogins |
| #4042 | Task 2 | Default-handler form actions posted to ?handler= after a failed handler post (the 2FA page) |
| #4047 | Task 2 | ID-4: the account is kept after a committed Investigator claim on a first Google sign-in |
| #4098 | Task 3 | Auth0 logout through /oidc/logout; BFF/direct-JWT name overwrite; a refused sign-in shown like direct Auth0; provider-aware live harness |
| #4105 | Task 3 | Per-flow PKCE cookies, so silent and interactive flows no longer clobber each other; /api/auth/callback/{appPath} forwards Auth0 Action links such as /auth/confirmation-email-sent |
| #4106 | Task 3 | End the Auth0 session on the Action email-sent pages through /v2/logout in a hidden iframe |
Related improvements shipped during S09: silent sign-in #4097 (bffSilentSignIn) and flicker-free
hidden-iframe silent sign-in with sliding sessions #4104 (bffSilentSignInFrame, bffSlidingSession,
Identity identitySilentSignInFrame). They are enabled on staging through camaradesuk/cluster-gitops#1602
and #1610.
Production consequences¶
- The production rollback path at S11/S13 needs a new Auth0 application and secret. The checklist is in S11-PLAN.
- The S13 gate (G3) also requires account-portal slices 1-3 (plan). S10-S12 proceed in parallel and are not gated by them.
Next¶
Update 2026-10-07: S27 is done (S27-SUMMARY). Staging runs the BFF on OpenIddict again, and both retained generations were refused. The list below is kept as written at S09.
- S27 (camaradesuk/cluster-gitops#1531): reapply the BFF plus native Identity, generation
s27-staging-openiddict-3. It must refuse both retained generations, so a session cookie from each ofs09-staging-openiddict-1ands09-staging-auth0-rollback-2must be retained for it. It needs Chris's go. - Delete the Auth0 staging test user.
- Decide whether to keep the "SyRF staging BFF" Auth0 application as a standing rollback path.