handoff.mdunauthorized.md02-routing-layouts.md03-auth-session.mdJourney flow#
Generated from the authored steps below — click a node to jump to that section.
The admin entry is a real, explicit privilege exchange. A protected route sends
an unauthenticated browser to /unauthorized; the public /handoff page
detects the customer cookie; the Admin API forwards that token to the BFF; and
an eligible operator receives a separate scoped admin cookie before returning to
a safe local path. The browser and route tests cover the chain in layers rather
than one customer-cookie click-through E2E.
| Boundary | Responsibility |
|---|---|
| Admin middleware | Request id, production rate limit, admin-cookie shape/expiry, protected-route redirect |
/handoff + launcher |
Customer-cookie presence, explicit request, status and redirect |
POST /api/admin/signin |
Forward customer token, sanitize return path, set admin cookie |
| BFF session route | Authenticate customer, resolve operator eligibility/scopes, mint admin token |
| Proxied Admin BFF requests | Authoritative token signature and scope verification |
Personas#
- Eligible operator — has a customer session and a BFF operator record with admin workspace scopes.
- Ineligible customer — is authenticated as a customer but has no operator record.
- Expired admin operator — has a malformed or expired admin cookie and must exchange again.
- Anonymous visitor — has neither cookie and receives sign-in guidance without an elevation button.
Pre-conditions#
- The customer shell has issued the
oshun-sessioncookie, or a trusted caller suppliesx-oshun-customer-tokento the Admin signin route. - The BFF session handoff route is reachable.
- Eligible users have a seeded or production operator record with roles, scopes, home workspace, and session TTL.
/handoffand/unauthorizedare public Admin paths; workspace paths are protected.
Steps#
1. Enter a protected route without an admin session#
- Middleware generates an
X-Request-Id. - A missing cookie redirects to
/unauthorized?reason=missing-session&returnTo=<path>. - A malformed or expired cookie redirects with
reason=invalid-sessionand is deleted on the redirect response. - The unauthorized page displays the attempted safe local path.
- Its current Return to handoff entry link points to
/, not directly to/handoff; without a cookie that protected root can redirect back to unauthorized.
2. Open the public handoff page#
-
/handoffreads both cookie namespaces without treating the customer cookie as an admin session. - A valid existing admin cookie immediately redirects to the sanitized
returnToor/. - Without an admin cookie, the page renders Privileged handoff, the explicit-elevation explanation, an optional readable reason, and the current request id.
- Without a customer cookie, the launcher instructs the visitor to sign in through the consumer shell and does not render the request button.
3. Request elevation#
- Request admin handoff posts JSON to
/api/admin/signinwith same-origin credentials. - The Admin route reads the customer token from the cookie or trusted header.
- Missing customer state returns 401
customer-session-missing. - The route forwards the customer token as a Bearer credential to
POST /v1/admin/session/handoff. - The launcher sends
x-request-idto the Admin route, but the current BFF session client does not forward that header to the BFF exchange; do not claim one request id spans the full hop.
4. Resolve operator eligibility#
- The BFF authenticates the customer and looks up their operator record.
- An eligible operator receives resolved roles, scopes, home workspace, session id, audience, expiry, and a dev or JWT admin token.
- An authenticated customer without an operator record receives 403
operator_not_eligible. - Missing or rejected customer authentication receives 401.
- This is an explicit first-party HTTP exchange, not an undocumented out-of-band IdP step.
5. Set the separate admin cookie#
- The Admin route stores the BFF token in
oshun-admin-session. - Cookie attributes are
Path=/,HttpOnly,SameSite=Lax, BFF-supplied expiry, andSecurein production. - Customer and admin cookie names remain separate.
- The JSON response returns the operator view and sanitized redirect.
6. Return to a safe local path#
-
returnTomust start with one slash. - Scheme/host targets, protocol-relative paths,
/unauthorized, and/handoffare rejected. - Unsafe or absent values fall back to the operator home workspace.
- The launcher assigns
window.locationto the returned path after a successful exchange. - The sanitizer is a local-path rule, not a complete allowlist of every registered Admin route.
7. Enter and use the Admin shell#
- Middleware decodes token claims for fast routing and expiry checks.
- A signed-in operator can load the dashboard and workspace shell.
- The BFF still performs authoritative signature and scope checks on each proxied Admin API request; middleware claim decoding is not the trust anchor.
- When the admin cookie later expires or becomes malformed, the protected
route closes the loop through
invalid-session.
8. Keep the rate limiter accurately bounded#
- Non-local production requests are limited to 120 requests per minute per
derived IP and return 429 with
Retry-After: 60and a request id. - Local development requests bypass that limiter.
- The middleware limiter uses a module-level Map, so it is per Admin instance and is not a shared multi-replica quota.
- The focused handoff browser coverage does not drive the 121-request production-only boundary.
Post-conditions#
- An eligible operator has a separate, scoped, expiring, HttpOnly admin cookie.
- The browser lands on a sanitized local path or the operator home workspace.
- Ineligible and unauthenticated customer sessions do not receive an admin token.
- A protected request with an invalid admin cookie clears it and returns to the denial flow.
- Cross-hop request-id continuity and distributed rate limiting are not implied.
Failure modes#
- No customer cookie — the handoff page renders guidance only.
- Customer session rejected — the Admin route returns a visible 401 error.
- No operator record — the BFF returns 403 and no admin cookie is set.
- BFF unavailable or malformed payload — the signin route returns a failed exchange rather than inventing a session.
- Unsafe return path — redirect falls back to the home workspace.
- Expired or malformed admin cookie — middleware deletes it and redirects
with
invalid-session. - Non-admin scopes in a parseable token — the server-session gate rejects Admin-shell entry even if middleware accepted the token shape.
- Shared-NAT burst in production — the per-instance IP counter may return 429.
- Unauthorized-page loop — its root link is not currently a direct handoff link.
- Correlation overclaim — the request id entering the launcher is not
forwarded by
requestAdminSessionHandoff.
E2E coverage#
- apps/oshun/admin/e2e/auth-entry-flows.spec.ts
covers protected-route redirects, invalid/expired cookies, non-admin scope
rejection, public routes, dashboard entry, and existing-admin
returnTo. - apps/oshun/admin/src/tests/signin-route.test.ts covers the customer-cookie exchange, HttpOnly cookie, safe/unsafe redirects, and BFF 401/403 mapping.
- apps/oshun/bff/src/tests/admin-session-route.test.ts
covers token issuance, resolved scopes, operator ineligibility, missing auth,
Admin
/session/me, and signout. - apps/oshun/admin/src/tests/session-cookie.test.ts covers dev/JWT-shaped claim parsing and expiry behavior.
Coverage is strong across middleware, Admin route, and BFF layers but not one browser-driven exchange. No current Playwright case seeds a customer cookie, clicks the launcher, observes the BFF handoff, and lands in the target workspace.
Per-view files touched by this journey#
- meta/unauthorized.md — denial reasons and attempted path.
- meta/handoff.md — explicit customer-to-admin exchange.
- shell/03-auth-session.md — cookie and trust boundaries.
- shell/02-routing-layouts.md — public and protected routing.
- Any gated workspace may be the local return target.
Cross-references#
- review-cycle-admin.md — one protected target.
apps/oshun/admin/src/middleware.ts— redirect, request-id, and rate-limit behavior.apps/oshun/admin/src/app/handoff/page.tsxandHandoffLauncher.tsx— entry UI.apps/oshun/admin/src/app/api/admin/signin/route.ts— exchange and cookie boundary.apps/oshun/admin/src/lib/bff-session-client.ts— BFF request and response validation.apps/oshun/admin/src/lib/session-cookie.ts— non-authoritative middleware claim parsing.
Open questions#
- Should the unauthorized page link directly to
/handoffwith its sanitized return path? - Should the Admin signin route forward
x-request-idto the BFF? - Should
returnTobe checked against the registered route model rather than only local-path rules? - Which shared store should enforce the production rate limit across replicas?
- Should Playwright cover the full customer-cookie → click → workspace exchange?
- How are production JWT signing keys, revocation, and session rotation operated?