Context. surface admin · domain meta · route /unauthorized · auth anon (public path) · source apps/oshun/admin/src/app/unauthorized/page.tsx
Last walked. —
Purpose#
Auth-denial UI for the admin app. Reason-based copy from the internal
REASON_COPY table; surfaces the attempted returnTo path so the operator can
resume after handoff. Always renders the same lightweight card with no shell
chrome.
Entry points#
- Middleware bounce —
apps/oshun/admin/src/middleware.tsredirects here when a non-public path is hit without a valid admin session (shell/02-routing-layouts.md). Reasons:missing-session(noOSHUN_ADMIN_SESSION_COOKIE_NAME)invalid-session(parseAdminSessionTokenreturned null — cookie also deleted on the response)
- Page-level / BFF redirect —
forbidden-workspacereason can be surfaced when a page (or downstream) detects an operator with a valid session but missing workspace scope. Middleware itself does not check scope today, so this is dispatched by individual pages or byHandoffLauncherflows - Direct URL —
PUBLIC_PATHSincludes/unauthorized; no session required
Layout regions#
This page does NOT render AdminShell. Renders a plain
<main role="main" className={styles.page}>:
- Card (
section.styles.card):- Eyebrow — "Oshun · Operations"
- Heading — h1 from
REASON_COPY[reasonKey].heading - Body —
<p>fromREASON_COPY[reasonKey].body - Attempted path — when
returnTois non-null and validates viasanitizeReturnTo, renders<p>Attempted path: <code> {returnTo}</code></p> - Action link —
<Link href="/">Return to handoff entry</Link>(anchors to admin root, which middleware will re-bounce to/handoffif there's no admin session)
States#
The reasonKey selection runs before render:
-
searchParams.reasonmatches aREASON_COPYkey (missing-session/invalid-session/forbidden-workspace) → that copy renders -
searchParams.reasonabsent or unrecognized → defaults tomissing-session -
returnToquery missing → "Attempted path" line omitted -
returnToquery starts with/but not//→ "Attempted path:" renders -
returnToquery starts with//or doesn't start with/→sanitizeReturnToreturns null; line omitted - Reason copy variants (from page source):
missing-session— h1 "Admin session required"; body explains privileged-handoff requirement and that customer sessions cannot enter admin routesinvalid-session— h1 "Admin session expired or invalid"; body explains token verification failure and recommends requesting a new privileged handoffforbidden-workspace— h1 "Workspace access denied"; body explains the operator's admin role does not grant the workspace scope and to contact a studio operator
Interactions#
- "Return to handoff entry" (link — primary)
- Function: navigates to
/(admin root). Anonymous operators hit middleware, get bounced to/handoff(the privileged- handoff entry). Operators with a valid admin session will land on the dashboard. - Tab order: the only focusable control on the page
- Function: navigates to
The page exposes no buttons or forms.
Data & contracts#
- Reads:
searchParams(Promise) —reason,returnToREASON_COPY— module-local constant mappingmissing-session/invalid-session/forbidden-workspaceto{ heading, body }
- Writes: None.
- Realtime: None.
- Auth/role check: NO middleware session check —
/unauthorizedis inPUBLIC_PATHS
Cross-references#
- Shell:
shell/02-routing-layouts.md(middleware redirect logic),shell/03-auth-session.md(the three reasons documented there matchREASON_COPYexactly) - Sibling meta route:
handoff.md(the page operators land on after clicking the action link, when no admin session is present)
Open questions / known gaps#
-
forbidden-workspacereason: middleware does NOT check workspace scope; this reason is presumably dispatched by a page-level check or by the BFF/api/admin/signinflow. Locate the actual dispatcher - No "Sign in differently" or "Switch operator" affordance — verify whether a future state should expose operator switching from here
- Action link points to
/not/handoff; this is intentional (it lets the middleware bounce back to/handofffor anonymous traffic) but it adds an extra redirect hop