Commit graph

5 commits

Author SHA1 Message Date
64513797ee binarybeachio: per-app edge-identity validation + bundled bridge logout
Marker-cookie pattern per docs/conventions/per-app-edge-identity-validation.md:

- New BbEdgeIdentityMiddleware compares `_bb_edge_sub` cookie to
  `X-Auth-Request-User` header on every authenticated request. On mismatch,
  flushes the Django session and replaces request.user with AnonymousUser
  so DRF returns 401 / browser navigations land at the bridge handoff
  redirect. Lazy-populates the cookie on legacy sessions; passes through
  for anonymous requests and bearer-token-only callers.

- Trusted-JWT view sets `_bb_edge_sub` on the redirect response when
  X-Auth-Request-User is present (single session-mint choke-point — the
  Bucket-4 entry-point is the only path that creates Plane sessions in
  this deployment).

- SignOutAuthEndpoint reads optional BB_LOGOUT_REDIRECT_URL env. When set,
  the SPA's /auth/sign-out/ form-POST is 302'd to the platform bridge's
  synced-logout endpoint (clears edge `_bb_oauth2` + back-channels Zitadel
  end_session). Without this, the user's Zitadel session at the edge
  outlives the Plane logout and silently re-logs them in via bridge handoff
  → trusted sign-in. Vanilla regression-safe: env unset → upstream behavior.

Net surface vs upstream-clean: 1 new middleware file, 1 line in MIDDLEWARE,
~20 lines added to trusted.py and signout.py. No new dependencies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 13:31:02 -10:00
69b499c9ec binarybeachio: trusted view — key User on bb_mailbox (four-layer identity model)
Identity-model rollout T2.4. Trusted view now derives `lookup_email = bb_mailbox or email`
and uses it for both User.objects.filter() and the new-User row's email field. WARN-log
fallback to federation email when the claim is absent (transitional safety; should never
fire once Zitadel `bb-claims` Action + bridge-side userinfo enrichment are live).

Decode-time required-claims unchanged (`bb_mailbox` stays optional) so partial deploys
aren't bricked. Pre-migration SQL rename of operator's existing User row required —
see binarybeachio docs/services/plane/migration-plan.md §9.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 00:56:35 -10:00
c0cfbb2bdc binarybeachio: trusted view — mirror OAuth adapter create-shape (Profile, username, is_email_verified)
Plane's OAuth adapter (apps/api/plane/authentication/adapter/base.py:289-342)
creates User AND Profile when a new identity arrives. My trusted view was
calling User.objects.get_or_create() without the Profile, so the SPA's
/api/users/me/profile/ 404'd and the SPA bounced the user back to /login
in an onboarding loop.

Mirror the adapter's full create-shape: random username (uuid hex),
first/last names from JWT claims, is_password_autoset=True,
is_email_verified=True, random password (so Django's auth hash is non-empty
for break-glass), then Profile.objects.create(user=user). Wrapped in a
transaction so partial creation can't leave the DB inconsistent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 23:55:25 -10:00
13b4de6d82 binarybeachio: trusted view — rename log extra key 'created' to 'is_signup'
LogRecord has a built-in `created` attribute (timestamp) and Python's
Logger.makeRecord raises KeyError("Attempt to overwrite 'created' in
LogRecord") when extra= contains it. The 500 fired AFTER user_login
already set the sessionid, so users were technically signed in but
saw a 500 page on first visit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 21:18:49 -10:00
712612865d binarybeachio: Bucket-4 trusted-JWT auth — replaces in-place github.py patch
Migrates this fork to the binarybeachio platform-architecture pivot:
oauth2-proxy at the edge enforces a Zitadel session, the auth-bridge
mints a short-lived RS256 JWT, and a NEW additive endpoint at
/auth/sign-in-trusted/ verifies the JWT, claims its jti against
shared-redis (single-use replay protection, fail-closed), find-or-creates
the User, and starts a Django session via user_login().

Net surface vs. upstream-clean: 1 new view file + 1 url path + 1
exports __init__ entry + 7 reserved error codes (6000-6099 range).
github.py and the GitHub-button rebrand patch are reverted to upstream
— sign-in entry-point UX is now driven by Traefik redirectregex on
/sign-in* in infrastructure/plane/docker-compose.yml.

Replay protection contract: jti claim minted by bridge, consumed via
Redis SETNX with ttl = exp - now + 30s. Documented at
binarybeachio/docs/architecture/bridge-jwt-replay-protection.md.

Public-key transport: BB_BRIDGE_PUBLIC_KEY_URL env points at the
in-cluster bridge's /.well-known/bb-bridge.pub.pem (avoids the
env-PEM corruption issue Coolify has with backslash-escaped keys).
Endpoint is implicitly disabled (404) when env unset — vanilla
upstream behavior preserved.

Storage patches (Patch 2) unchanged. Brand asset preserved (dormant).
Pre-migration source state preserved on branch pre-migration-2026-05-04.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:24:48 -10:00