Context. surface tenant-admin · domain meta · route /unauthorized · auth anon (public deny page) · source apps/oshun/tenant-admin/src/app/unauthorized/page.tsx
Last walked. —
Purpose#
Public deny page shown when a request to a gated tenant-admin route has no valid
session. It explains why access was refused and links back to the handoff so the
identity provider can issue a session. One of two public paths (/unauthorized,
/handoff) in src/middleware.ts.
Entry points#
- Middleware redirect (
src/middleware.ts) when a non-public path has no/invalid session token:/unauthorized?reason=<missing-session|invalid-session>&returnTo=<path> - Server-component redirect from every gated page when
getTenantServerSession()returns null:/unauthorized?reason=missing-session&returnTo=<route> - Direct URL
/unauthorized(public; no auth required)
Layout regions#
No shell chrome (TenantShell is not used — this is a bare page so no session
leaks into the layout):
- Main:
<main data-testid="tenant-unauthorized-page"><h1>Tenant session required</h1>- reason copy paragraph (from
REASON_COPY[reason]) - paragraph with the return to the handoff link
States#
- Loading — N/A; static server component, no data fetch
- Empty — N/A; always renders heading + copy + link
- Populated (short) —
reason=missing-session(default) → "No tenant-admin session was found. Sign in through your identity provider and try again." - Populated (long) — N/A
- Error (recoverable) — N/A; no query
- Error (unrecoverable) — unknown
reasonvalue falls back to themissing-sessioncopy;invalid-session→ "Your tenant-admin session is no longer valid. Sign in again to refresh your scope claims." - Offline — static page; renders from cache if SW present (verify)
- Gated — N/A; this IS the deny page (public path in middleware). No information leak — copy is generic and never echoes session data
- Standalone PWA — verify; no manifest/SW confirmed in source
Interactions#
- return to the handoff (link,
next/link)- Function: navigates to
/handoff?returnTo=<encoded returnTo>(encodeURIComponent);returnTodefaults to/when absent - Keyboard: only focusable element; Enter activates
- Screen reader: announces "return to the handoff"
- Touch target: standard inline link — verify ≥ 44×44 px hit area on coarse pointers
- Mobile (≤ 640 px): no layout delta (no shell)
- Offline behavior: navigation only;
/handoffis a public path - Telemetry: None observed in source.
- Function: navigates to
Data & contracts#
- Reads:
searchParams(reason,returnTo) from the URL only — no BFF/network read - Writes: None. (middleware separately deletes the
oshun-tenant-sessioncookie when it redirected here with an invalid token) - Realtime: None.
- Caching: static / SSR; depends only on query params
- Auth/role check: none —
/unauthorizedis in middlewarePUBLIC_PATHS
Cross-references#
- Middleware:
apps/oshun/tenant-admin/src/middleware.ts(PUBLIC_PATHS, redirect construction, cookie delete on invalid token) - Session:
apps/oshun/tenant-admin/src/lib/session-cookie.ts(parseTenantSessionToken),src/lib/server-session.ts(getTenantServerSession) - Handoff target:
/handoff(other public path; no walkthrough file in scope) - Gated routes that redirect here: integrations.md, members.md, notifications.md, policy.md, roles.md, status.md
- Conventions: ../WALKTHROUGH/00-conventions.md
Open questions / known gaps#
-
/handoff(the link target and other public path) has no walkthrough file in this scope; document it separately. - No telemetry/event is fired on this deny page; confirm whether a denied- access event is expected.