---
path: /identity
surface: tenant-admin
domain: tenant-identity
auth: signed-in + tenant:<tenantId>
source: apps/oshun/tenant-admin/src/app/identity/page.tsx
status: walked
last_walked:
  '2026-06-29 by Codex, real-dev-infra Playwright tenant-admin-identity'
---

# Identity and provisioning

## Purpose

Tenant administrators use this cockpit to inspect the tenant's SSO connection
projection and the sample identity engines for SSO login processing, SCIM sync,
roster diff, auth challenge, and federation. The current shipped page is
read-only: operator/BFF routes own SSO mutation, and tenant-admin metadata
upload, auth-policy editing, sandbox sign-in, and enable controls remain product
gaps.

## Entry Points

- Direct URL `/identity` after a tenant-admin IdP/session handoff sets
  `oshun-tenant-session`.
- Tenant-admin shell navigation once the user has `tenant:admin:<tenantId>`.
- Unauthorized direct access redirects to
  `/unauthorized?reason=missing-session&returnTo=/identity`.

## Layout Regions

- **Tenant shell**: skip link, tenant sidebar, tenant header, and main content.
- **SSO connections**: read-only table backed by the real tenant-console SSO
  read route for the caller's tenant.
- **Policy engine checks (sample inputs)**: four summary tiles for SSO probe,
  roster changes, auth challenge, and federation.
- **Evidence table**: OIDC sample connection, SCIM delete outcome, and device
  posture auth-challenge reason.

## States

- [x] **Loading** — N/A for this server-rendered page; it returns the populated,
      empty, unavailable, or gated state from the request.
- [x] **Empty** — if `/v1/tenant-console/sso` returns no connections for the
      tenant, the page renders
      `No SSO connections are configured for this tenant yet. Connections provisioned through the operator console appear here.`
- [x] **Populated (short)** — verified by
      `apps/oshun/tenant-admin/e2e/tenant-identity.spec.ts`: the test creates a
      real OIDC SSO connection through `POST /v1/admin/sso`, sets a valid tenant
      session cookie, and asserts `/identity` renders only the caller tenant's
      row with protocol, IdP entity, flow flags, JIT state, session minutes, and
      claim count.
- [x] **Populated (long)** — no pagination or virtualization exists; the table
      renders all returned connections in source order.
- [x] **Error (recoverable)** — if the tenant BFF read fails, the page renders
      `The identity backend is unreachable right now...` with `role="alert"`.
- [x] **Error (unrecoverable)** — N/A; unrecoverable app errors use the Next.js
      route error boundary outside this page.
- [x] **Offline** — N/A for first load; this separate tenant-admin app has no
      documented offline service-worker contract.
- [x] **Gated** — verified by `tenant-admin-identity`: no cookie redirects
      before `[data-testid="tenant-identity-panel"]` can render.
- [ ] **Standalone PWA** — no tenant-admin PWA manifest/home-screen path is
      documented yet.

## Interactions

- [x] **Skip to main content** (link)
  - Function: jumps focus to `#tenant-main`.
  - Keyboard: first focusable element; Enter activates.
  - Screen reader: announces `Skip to main content`.
  - Touch target: browser default link target; no custom mobile affordance.
  - Mobile (<= 640 px): follows tenant shell behavior.
  - Disabled when: never.
  - Offline behavior: same-page anchor only.
  - Telemetry: none wired.

- [x] **Tenant sidebar navigation** (links)
  - Function: tenant shell route navigation for workspace sections.
  - Keyboard: standard link activation.
  - Screen reader: current workspace is identified by the shell.
  - Touch target: governed by `TenantSidebar` styles.
  - Mobile (<= 640 px): follows tenant shell responsive layout.
  - Disabled when: shell scope calculation hides or marks inaccessible targets.
  - Offline behavior: navigations require the tenant-admin app to be reachable.
  - Telemetry: none wired on this page.

- [x] **SSO connections table** (read-only table)
  - Function: displays `GET /v1/tenant-console/sso?tenantId=<tenantId>` output.
  - Keyboard: no row action exists.
  - Screen reader: table headers expose connection, protocol, IdP entity, flows,
    JIT, session, and claims.
  - Touch target: no touch controls exist in the table.
  - Mobile (<= 640 px): table uses tenant workspace table styling; no separate
    mobile card layout exists.
  - Disabled when: always read-only.
  - Offline behavior: first-load read fails into the backend-unreachable alert.
  - Telemetry: none wired.

- [x] **Policy engine summary tiles** (read-only summaries)
  - Function: render deterministic sample outputs from `processSsoLogin`,
    `computeRosterDiff`, `evaluateAuthChallenge`, and `canFederateAuth`.
  - Keyboard: table is not interactive.
  - Screen reader: labels and values are text content.
  - Touch target: no touch controls exist.
  - Mobile (<= 640 px): summary grid follows tenant workspace CSS.
  - Disabled when: always read-only.
  - Offline behavior: server-rendered sample values do not require external
    calls after the page loads.
  - Telemetry: none wired.

## Data & Contracts

- **Reads**:
  - `GET /v1/tenant-console/sso?tenantId=<tenantId>` via
    `tenantBffGet<TenantSsoResponse>`; returns a tenant-scoped config-only
    projection of SSO connections.
  - `getTenantServerSession()` reads `oshun-tenant-session`, validates the
    tenant dev-token payload, and requires `tenant:admin:<tenantId>` or an
    equivalent shell-admitted scope.
  - `processSsoLogin`, `applyScimSync`, `computeRosterDiff`,
    `evaluateAuthChallenge`, and `canFederateAuth` from `@oshun/tenant-console`
    drive the read-only evidence section.
- **Writes**: none from this page today.
- **Realtime**: none.
- **Caching**: `dynamic = 'force-dynamic'`; tenant BFF fetch uses
  `cache: 'no-store'`.
- **Auth/role check**: tenant-admin cookie session plus BFF tenant scope gate on
  `/v1/tenant-console/sso`.

## Cross-References

- Feature spec: [`V1/features.md`](../V1/features.md) Identity, SSO, SCIM,
  Provisioning.
- Journey:
  [`tenant-sso-config-saml-oidc-claim-mapping.md`](../WALKTHROUGH/journeys/tenant-sso-config-saml-oidc-claim-mapping.md).
- Operator SSO mutation surface:
  [`operator/operator-sso.md`](../WALKTHROUGH/operator/operator-sso.md).
- Component/source files:
  - `apps/oshun/tenant-admin/src/app/identity/page.tsx`
  - `apps/oshun/tenant-admin/src/lib/server-session.ts`
  - `apps/oshun/tenant-admin/src/lib/tenant-bff.ts`
  - `apps/oshun/bff/src/routes/tenant-console-reads.ts`

## E2E Coverage

- [`apps/oshun/tenant-admin/e2e/tenant-identity.spec.ts`](../apps/oshun/tenant-admin/e2e/tenant-identity.spec.ts)
  starts the tenant-admin app and real BFF, verifies anonymous redirect, creates
  two real SSO connections through the admin SSO BFF, sets a valid tenant
  session cookie, asserts tenant-scoped roster isolation on `/identity`,
  verifies the sample SSO probe/auth-challenge/federation evidence, and runs an
  axe scan on the rendered identity panel.

## Open questions / known gaps

- [ ] Tenant-admin metadata XML upload and OIDC discovery URL controls are not
      present; `/operator/sso` and BFF import routes currently own those flows.
- [ ] Per-tenant auth-policy edit/write controls are not present; the page only
      renders one fixed sample `evaluateAuthChallenge` result.
- [ ] Tenant-admin sandbox sign-in probe action and per-verdict UI are not
      present.
- [ ] `SsoConnection` draft/probed/enabled/disabled state machine is not modeled
      or persisted.
- [ ] Role-template binding for mapped `groups` claims remains outside this
      page's current contract.
