feat(auth): real OIDC login + remove agents page

Replaces the dev-bypass-only AuthProvider with a real backend-mediated
OIDC client. Backend ships the OIDC plumbing in
Dialectic.Backend@2463129; this SPA drives it.

Auth flow:
  1. On mount: GET /api/auth/oidc/status (is OIDC configured?) + GET
     /api/auth/me (who am I — reads the dialectic_session HttpOnly
     cookie if present).
  2. Anon + click 'login' → window.location = /api/auth/oidc/start
     (backend redirects to IdP authorize URL with PKCE + state).
  3. IdP returns to backend /api/auth/oidc/callback → backend redirects
     here to /oidc/callback#oidc_ticket=<base64url>.
  4. OidcCallbackPage POSTs the ticket to /api/auth/oidc/exchange →
     backend Set-Cookie's the session JWT → refresh() AuthProvider →
     navigate to /.
  5. Header shows user.name + 'logout' button. Logout → POST
     /api/auth/logout (cookie cleared by backend) → user state cleared.

UI changes:
  - Header gets a Login button (acid primary style) when anon + OIDC
    enabled; logged-in pill (name + logout) when authenticated.
  - 'oidc not configured' message when backend reports
    /api/auth/oidc/status enabled=false (operator needs to run
    dialectic-cli config oidc first).
  - Removed /agents/:id route + AgentActivity page + nav link entirely
    (per user request; admin endpoint stays on backend for curl).
  - Removed Link to /agents/* from TopicDetail + Verdict pages.

api.ts:
  - Adds credentials:'same-origin' on every fetch so the session cookie
    rides along.
  - Drops the admin: opt + x-dialectic-admin-key header path (no more
    AgentActivity consumer).
  - Keeps dev-bypass header support gated on VITE_OIDC_DEV_BYPASS for
    dev convenience — backend's OIDC_ONLY=true env disables it server-
    side anyway.

types.ts: AgentSummary type removed.

Build delta: 178KB → 179KB / gzip 57.12 → 57.70 (oidc-client overhead
is just the AuthProvider polling code; no oidc-client-ts lib needed
since the backend drives the redirect flow).
This commit is contained in:
h z
2026-05-24 01:43:06 +01:00
parent 7dca2c3110
commit d1d2ae2fb1
9 changed files with 248 additions and 232 deletions

View File

@@ -3,12 +3,12 @@ import { useAuth } from './auth';
import { TopicListPage } from './pages/TopicList';
import { TopicDetailPage } from './pages/TopicDetail';
import { VerdictPage } from './pages/Verdict';
import { AgentActivityPage } from './pages/AgentActivity';
import { NotFoundPage } from './pages/NotFound';
import { OidcCallbackPage } from './pages/OidcCallback';
import './styles/app.css';
export function App() {
const { user, devBypass } = useAuth();
const { user, loading, oidcEnabled, login, logout } = useAuth();
return (
<div className="app">
<header className="app-header">
@@ -20,29 +20,37 @@ export function App() {
<NavLink to="/" end>
topics
</NavLink>
<NavLink to="/agents/recruiter">agents</NavLink>
</nav>
<div className="app-user">
{user ? (
<span className="eyebrow">{user.label}</span>
{loading ? (
<span className="eyebrow">loading</span>
) : user ? (
<span className="row" style={{ gap: 12 }}>
<span className="eyebrow" title={user.email}>
{user.name}
</span>
<button onClick={logout} className="auth-btn">
logout
</button>
</span>
) : oidcEnabled ? (
<button onClick={login} className="auth-btn auth-btn-primary">
login
</button>
) : (
<span className="eyebrow">readonly</span>
<span className="eyebrow" title="OIDC not configured — set up via dialectic-cli">
anonymous · oidc not configured
</span>
)}
</div>
</div>
{devBypass && (
<div className="app-dev-banner">
DEV BYPASS ACTIVE auto-attaching <code>x-dev-bypass</code> on
every request; do not run this build in production
</div>
)}
</header>
<main className="app-main">
<Routes>
<Route path="/" element={<TopicListPage />} />
<Route path="/topics/:id" element={<TopicDetailPage />} />
<Route path="/topics/:id/verdict" element={<VerdictPage />} />
<Route path="/agents/:id" element={<AgentActivityPage />} />
<Route path="/oidc/callback" element={<OidcCallbackPage />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</main>