/** * In-app auth routes — expose the CLI's browser OAuth flow over HTTP so the * Studio web app can trigger sign-in/sign-out without restarting the server. * * Reuses `performBrowserAuth` from `@sapiom/mcp/auth` (the SAME function the * CLI's `ensureAuthenticated` calls at boot) — no custom OAuth implementation, * no changes to any Sapiom cloud endpoint. The flow: * * POST /api/auth/start * ↳ performBrowserAuth(appURL, apiURL) ← existing CLI flow, unchanged * opens browser → localhost callback → token exchange * ↳ writeCredentials(...) ← writes ~/.sapiom/credentials.json * ↳ apiKeyProvider.refresh() ← adopts the new key in-process * ↳ bus.publish({ type: "auth.changed" }) ← notifies open WS connections * * POST /api/auth/disconnect * ↳ clearCredentials(...) * ↳ provider.clear() * ↳ bus.publish({ type: "auth.changed", authenticated: false }) * * GET /api/auth/status * ↳ { authenticated, organizationName } * * All three routes sit behind the existing `X-Harness-Token` middleware applied * at the /api level in server/index.ts — no extra auth layer needed here. */ import { Router } from "express"; import { performBrowserAuth } from "@sapiom/mcp/auth"; import type { EventBus } from "../core/event-bus.js"; export interface AuthState { authenticated: boolean; organizationName: string | null; } /** * A mutable holder for the harness's current auth identity — updated on * sign-in/sign-out and queried by `GET /api/auth/status`. Shared between the * auth router and any other consumer (e.g. `GET /api/state` in rest.ts) that * wants live auth state rather than the boot-time snapshot. */ export interface MutableAuthState { get(): AuthState; set(state: AuthState): void; } export declare function createMutableAuthState(initial: AuthState): MutableAuthState; /** * The minimal slice of {@link ApiKeyProvider} the auth router needs: a way * to push a new key in after a successful browser auth (or clear it on * disconnect), without importing the full provider shape. */ export interface AuthKeyTarget { /** * Re-reads `~/.sapiom/credentials.json` and adopts the key found there. * Called after `performBrowserAuth` + `writeCredentials` complete so the * provider's in-memory key matches the freshly written credential. */ refresh(): Promise; /** * Unconditionally zero the in-memory key (sets it to null). Called on * disconnect so that the provider returns null immediately, without waiting * for another credential-store read. */ clear(): void; } export interface AuthRoutesOptions { /** Mutable auth state shared with `GET /api/state` and `GET /api/auth/status`. */ authState: MutableAuthState; /** * The live API key provider — `refresh()` is called after sign-in writes * `~/.sapiom/credentials.json` so the in-process key adopts the new value. */ apiKeyProvider: AuthKeyTarget; /** The server event bus — broadcasts `auth.changed` after every state transition. */ bus: EventBus; /** Whether this Studio process may read or mutate shared authentication. */ authEnabled?: boolean; /** * Overrides SAPIOM_ENVIRONMENT for the OAuth flow. Defaults to * `process.env.SAPIOM_ENVIRONMENT` (the same override the CLI uses). */ environment?: string; /** * Injectable seam for `performBrowserAuth` — lets tests mock the full OAuth * round-trip without opening a real browser or binding a local port. */ performBrowserAuthImpl?: typeof performBrowserAuth; /** Server-private identity projection for authorization consumers. It is * intentionally not added to AuthState or any browser response. */ onProjectUserChanged?: (userId: string | null) => void; /** Awaited after local auth is cleared and before disconnect reports success. */ onCredentialRemoved?: () => Promise; /** @deprecated Use onProjectUserChanged; remove in SAP-3152. */ onPlanningUserChanged?: (userId: string | null) => void; } /** * Mount the in-app auth routes. The caller is responsible for gating these * behind the boot-token middleware (applied at the /api level in * server/index.ts before any router) — these routes do NOT add a second layer. */ export declare function createAuthRouter(opts: AuthRoutesOptions): Router; //# sourceMappingURL=auth-routes.d.ts.map