binarybeachio: SPA 401 interceptor hard-nav via /sign-in/ + cache-bust
Vanilla 401 handler did `window.location.replace('/?next_path=<currentPath>')`.
That IS a hard nav, but the browser's HTTP cache returns the cached SPA
bundle for `/` — the SPA boots, re-fetches the same /api endpoint, gets 401
again, and loops without ever hitting Traefik at the document level. Diagnosed
2026-05-05 via HAR analysis: 9 history entries bouncing `/` ↔ `/?next_path=/`
at ~780ms intervals; zero requests to bridge or oauth2-proxy during the
loop; first bridge.binarybeach.io/handoff request only after Ctrl+Shift+R.
Trigger on the platform side: oauth2-proxy refresh fails for cross-org
gmail-federated users (separate root cause — disabled platform-wide via
OAUTH2_PROXY_OIDC_GROUPS_CLAIM=). The hard-nav fix here is the safety net
that handles that and any other future 401-causing scenario.
Replace with `window.location.replace('/sign-in/?_bb_reauth=<Date.now()>')`:
- /sign-in/ matches Plane's priority-200 plane-signin-redirect Traefik
router (matched on PathRegexp `^/(sign-in|sign-up|signin|login|register|
accounts/sign-in)(/.*)?$$`), which 302s to the bridge handoff regardless
of cookie state.
- _bb_reauth=<ts> cache-busts so even a previously-cached /sign-in/
response can't short-circuit the request.
Vanilla Plane regression-safe: /sign-in/ is also a known SPA route in
upstream that bounces to /, so non-platform deployments see the same
behavior they'd get without this patch (modulo a single extra navigation).
Also fixes BINARYBEACHIO.md frontend build instructions: Dockerfile.web
needs the monorepo root as build context (turbo prune scope), opposite of
Dockerfile.api which needs apps/api/ as context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
64513797ee
commit
a9b4921973
2 changed files with 30 additions and 9 deletions
|
|
@ -174,17 +174,24 @@ Per binarybeachio architecture doc §7.4 ("only rebuild what we touched"), this
|
|||
Tag scheme per architecture §6 #7: `<upstream-version>-mine.<n>`. Push immutable tag + `:latest`:
|
||||
|
||||
```bash
|
||||
# from C:\Users\maxwe\GitHubRepos\bb-plane-fork
|
||||
docker build -t git.binarybeach.io/binarybeach/plane-backend:v1.3.0-mine.2 \
|
||||
# Backend — Dockerfile.api COPY paths are relative to apps/api/, so the
|
||||
# build context must be apps/api/, NOT the repo root. Building from the
|
||||
# repo root (`-f apps/api/Dockerfile.api .`) fails: "/plane: not found".
|
||||
cd C:\Users\maxwe\GitHubRepos\bb-plane-fork\apps\api
|
||||
docker build -t git.binarybeach.io/binarybeach/plane-backend:v1.3.0-mine.<n> \
|
||||
-t git.binarybeach.io/binarybeach/plane-backend:latest \
|
||||
-f apps/api/Dockerfile.api .
|
||||
docker push git.binarybeach.io/binarybeach/plane-backend:v1.3.0-mine.2
|
||||
-f Dockerfile.api .
|
||||
docker push git.binarybeach.io/binarybeach/plane-backend:v1.3.0-mine.<n>
|
||||
docker push git.binarybeach.io/binarybeach/plane-backend:latest
|
||||
|
||||
docker build -t git.binarybeach.io/binarybeach/plane-frontend:v1.3.0-mine.2 \
|
||||
# Frontend — OPPOSITE convention: Dockerfile.web does `COPY . .` then
|
||||
# `turbo prune --scope=web --docker`, so the context must be the monorepo
|
||||
# ROOT (turbo needs to see all workspaces to prune correctly).
|
||||
cd C:\Users\maxwe\GitHubRepos\bb-plane-fork
|
||||
docker build -t git.binarybeach.io/binarybeach/plane-frontend:v1.3.0-mine.<n> \
|
||||
-t git.binarybeach.io/binarybeach/plane-frontend:latest \
|
||||
-f apps/web/Dockerfile.web .
|
||||
docker push git.binarybeach.io/binarybeach/plane-frontend:v1.3.0-mine.2
|
||||
docker push git.binarybeach.io/binarybeach/plane-frontend:v1.3.0-mine.<n>
|
||||
docker push git.binarybeach.io/binarybeach/plane-frontend:latest
|
||||
```
|
||||
|
||||
|
|
@ -196,6 +203,7 @@ Plane runs as two patched images (`plane-backend`, `plane-frontend`); they bump
|
|||
|
||||
| Tag | Upstream | Date | What changed |
|
||||
|---|---|---|---|
|
||||
| `plane-frontend:v1.3.0-mine.4` | v1.3.0 | 2026-05-05 | SPA 401 interceptor hard-navs to `/sign-in/?_bb_reauth=<ts>` (with cache-bust ts) instead of `/?next_path=...`. Vanilla path was a hard nav too, but the browser HTTP cache returned the cached SPA bundle for `/`, looping on every XHR 401 without ever hitting Traefik. Routing through `/sign-in/` matches the priority-200 plane-signin-redirect Traefik router → bridge handoff. Bundled with the mine.7 backend edge-identity work. |
|
||||
| `plane-backend:v1.3.0-mine.7` | v1.3.0 | 2026-05-05 | Per-app edge-identity validation (`_bb_edge_sub` cookie + `BbEdgeIdentityMiddleware`); `BB_LOGOUT_REDIRECT_URL` env re-points sign-out to platform bridge `/logout`. Per `binarybeachio/docs/conventions/per-app-edge-identity-validation.md`. |
|
||||
| `plane-backend:v1.3.0-mine.6` | v1.3.0 | 2026-05-05 | Trusted view keys User on `bb_mailbox` (four-layer identity model T2.4); WARN-log fallback to federation email when claim absent. |
|
||||
| `plane-backend:v1.3.0-mine.5` | v1.3.0 | 2026-05-04 | Trusted view mirrors OauthAdapter user-create shape (Profile, username uuid hex, is_email_verified, is_password_autoset, transaction). |
|
||||
|
|
@ -204,7 +212,7 @@ Plane runs as two patched images (`plane-backend`, `plane-frontend`); they bump
|
|||
| `plane-backend:v1.3.0-mine.2` | v1.3.0 | 2026-05-01 | Presigned-PUT signature mismatch fix on empty Content-Type. |
|
||||
| `plane-backend:v1.3.0-mine.1` | v1.3.0 | 2026-04-30 | Initial fork — presigned PUT for uploads (R2/B2 don't implement PostObject), GitHub-as-OIDC, brand asset. |
|
||||
|
||||
`plane-frontend` follows independently; it currently sits at `v1.3.0-mine.3` (Patch 2 frontend bits — presigned-PUT plumbing). The mine.7 backend bump above does NOT require a frontend rebuild — the SPA's existing `signOut()` form-POST goes to the same `/auth/sign-out/` URL; only the backend's response 302 target changed.
|
||||
`plane-frontend` follows independently. mine.3 was Patch 2 frontend bits (presigned-PUT plumbing). mine.4 (2026-05-05) is the 401-interceptor hard-nav fix bundled with the edge-identity rollout.
|
||||
|
||||
## License compliance
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,21 @@ export abstract class APIService {
|
|||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response && error.response.status === 401) {
|
||||
const currentPath = window.location.pathname;
|
||||
window.location.replace(`/${currentPath ? `?next_path=${currentPath}` : ``}`);
|
||||
// binarybeachio fork — re-auth must miss the browser HTTP cache.
|
||||
// Vanilla Plane navigated to `/?next_path=<currentPath>` which is the
|
||||
// SPA root route; the browser served the cached SPA bundle, the SPA
|
||||
// re-fetched the same /api endpoint, the call 401'd again, and the
|
||||
// page looped without ever hitting the network at the document
|
||||
// level. Plane's Traefik plane-signin-redirect router (priority 200,
|
||||
// matching `^/(sign-in|...)`) catches `/sign-in/` regardless of
|
||||
// cookie state and 302s to the bridge handoff — but only if the
|
||||
// browser actually fetches it. Append a ts param so the URL is
|
||||
// never in cache, and target /sign-in/ so the priority-200 router
|
||||
// wins. Vanilla Plane handles /sign-in/ in its SPA too (the SPA
|
||||
// bounces it to /), so the patch is also benign in non-platform
|
||||
// deployments. See binarybeachio/docs/conventions/per-app-edge-
|
||||
// identity-validation.md and feedback_plane_spa_cached_loop.
|
||||
window.location.replace(`/sign-in/?_bb_reauth=${Date.now()}`);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue