agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
124 lines • 6.87 kB
TypeScript
/**
* strategies/choose — `identityFromConfig`: config in, one strategy out, or a
* boot refusal that names the key.
*
* Pattern: Strategy (GoF), chosen by CONFIG at install time — one per
* deployment, behind the existing {@link IdentityVerifier} port.
* Role: the one place a deployment's identity settings are judged. Every
* door (`standingAgent`, an app's own routes) takes what this returns.
*
* ── The laws it enforces at boot (rule 9) ───────────────────────────────────
* • **Production names its strategy**, even `open`. Missing config must not
* mean "no identity". `production` is an input the APP supplies; the
* library never guesses it from `NODE_ENV`.
* • **A typo is not `open`.** An unknown strategy, an unknown key, or a key a
* later release reads is refused by name.
* • **Somebody configured a sign-in and forgot to switch it on**: strategy
* unset while `oidc-token` keys are set is refused, in every environment.
* • **A half-configured verifier is not a smaller verifier**: every required
* key is required; `IDENTITY_USER_ID_CLAIM` has no default.
* • **Config errors refuse; outages answer 503.** Discovery runs at boot. A
* 404, a body that is not JSON, a document without `jwks_uri` or naming
* another issuer refuses to boot. An IdP that cannot be reached lets the
* app start, says so in the banner, and answers 503 until it is reachable.
*
* Asynchronous — a refinement of the design's shape, on purpose: a discovery
* config error must refuse the BOOT, and discovery is a network read.
*/
import type { CrossSiteOptions } from '../../../hosting/doorGuard.js';
import type { DoorIdentity } from '../../../hosting/identityVerification.js';
import type { SignInDoor } from '../../../hosting/signin/door.js';
import type { HostSignInOptions } from '../../../hosting/signin/types.js';
import type { SignInAuditSink } from '../../../hosting/signin/audit.js';
import { type DiscoveryFetch } from '../verify/discovery.js';
import type { JoseBackend } from '../verify/jwtCore.js';
import { oidcIdentity, type OidcIdentityOptions } from '../oidc.js';
import { type IdentityConfig } from './config.js';
import type { LdaptsBackend } from '../directory/ldapDirectory.js';
import type { OpenIdClientBackend } from '../oidcSignIn.js';
import { type IdentityStrategyName } from './vocabulary.js';
/** What the app tells the library about where it is running. */
export interface IdentityBootOptions {
/**
* Is this a production deployment? REQUIRED — the app decides (e.g.
* `NODE_ENV === 'production' || APP_ENV === 'production'`), because only the
* app knows every way it spells it. Production refuses an unset strategy and
* plain-`http` issuers on `localhost`.
*/
readonly production: boolean;
/** The fetch discovery uses. Default: the runtime's `fetch`. */
readonly fetch?: DiscoveryFetch;
/** An already-imported `jose`, for a bundled app. */
readonly backend?: JoseBackend;
/**
* The door hardening's lists — the SAME `allowedHosts` / `allowedOrigins`
* the host is built with. A sign-in strategy's door guard is built from
* them (one source), and the public URL must pass them, or boot refuses.
* Unset: only a loopback public URL (development) can start a sign-in door.
*/
readonly crossSite?: CrossSiteOptions;
/** An already-imported `openid-client`, for a bundled app (browser sign-in). */
readonly openIdClient?: OpenIdClientBackend;
/** An already-imported `ldapts`, for a bundled app (`directory-password`). */
readonly ldapts?: LdaptsBackend;
/**
* The sign-in door's typed audit trail (`signInDoor({ onAudit })`): one
* record per sign-in outcome, for a sign-in strategy (`local-password`,
* `directory-password`, `oidc-token` browser sign-in). Unset: the door
* still writes one log line per outcome through `signInLog`.
*/
readonly onSignInAudit?: SignInAuditSink;
/** Where the sign-in door's audit LINES go (`signInDoor({ log })`). Default `console.info`. */
readonly signInLog?: (line: string) => void;
}
/** What the page's sign-in gate should show — `GET /auth/config` answers it. */
export type IdentityMode = 'open' | 'token-only' | 'password' | 'redirect' | 'proxy';
/** The chosen strategy, ready to hand to every door. */
export interface IdentityChoice {
/** Which strategy this deployment runs. */
readonly strategy: IdentityStrategyName;
/**
* What `standingAgent({ identity })` and every app door take. ABSENT for
* `open`: nothing about a request changes.
*/
readonly identity?: DoorIdentity;
/** What the page's sign-in gate shows: no sign-in, bearer tokens only, or a password form. */
readonly mode: IdentityMode;
/**
* The sign-in door (`/auth/*`), for a sign-in strategy. Mount it in front of
* your routes: `nodeHost({ onUnhandled: (req, res) => door.handle(req, res) … })`.
*/
readonly signInDoor?: SignInDoor;
/**
* What the host takes for a sign-in strategy: `nodeHost({ signIn: choice.hostSignIn })`.
* The transport then strips the sign-in cookie and checks every socket.
*/
readonly hostSignIn?: HostSignInOptions;
/**
* Lines to print at boot: the strategy, what it checks, what it does not,
* and every warning. No secret, token, or claim value is ever in it.
*/
readonly banner: readonly string[];
}
/**
* Judge a deployment's identity config and build its strategy — or refuse to
* boot with an {@link IdentityConfigError} naming the key.
*
* @example
* const choice = await identityFromConfig(identityConfigFromEnv(process.env), {
* production: process.env.NODE_ENV === 'production',
* });
* for (const line of choice.banner) console.log(line);
* const host = await standingAgent({ agent, sessions, host, identity: choice.identity });
*/
export declare function identityFromConfig(config: IdentityConfig, boot: IdentityBootOptions): Promise<IdentityChoice>;
/** The verifier's own construction refusals are boot refusals too. */
export declare function buildVerifier(options: OidcIdentityOptions): ReturnType<typeof oidcIdentity>;
export declare function oidcOptions(config: IdentityConfig, boot: IdentityBootOptions, production: boolean, shape?: {
readonly strategy: 'oidc-token' | 'proxy-token';
readonly literalIssuer: boolean;
}): OidcIdentityOptions;
/** A banner line with any control character escaped — belt and braces for values from a document. */
export declare function bannerSafe(line: string): string;
export declare function checkUrl(url: string, field: string, production: boolean): void;
//# sourceMappingURL=choose.d.ts.map