/**
 * oidcIdentity — verify a PERSON's access token from the company's OpenID
 * Connect identity provider (AD FS 2016+, Entra ID, Keycloak, Okta, Ping).
 *
 * Pattern: Adapter over `jose` + OpenID discovery, behind the vendor-neutral
 *          {@link IdentityVerifier} port. The `oidc-token` strategy's verifier.
 * Role:    everything `jwksIdentity` checks (signature, `iss`, `aud`, `exp`
 *          required, `nbf`), plus what makes a signed token a PERSON:
 *
 *   1. **Discovery, then pinning, with failures classified.** The issuer's
 *      `/.well-known/openid-configuration` is read before any token is
 *      accepted. An unreachable IdP is an OUTAGE (503, retried on later
 *      requests); a 404, a body that is not JSON, a document without
 *      `jwks_uri` or naming another issuer is a CONFIG ERROR (every request
 *      answers 503 "misconfigured", and `identityFromConfig` refuses to boot).
 *   2. **Issuer.** `iss` must equal the configured issuer, or AD FS's
 *      `access_token_issuer` from a document that passed (1).
 *   3. **`exp` is required.** A token with no lifetime is refused.
 *   4. **The stable id claim is required config** — no `sub` default. On both
 *      Microsoft IdPs `sub` is pairwise per application, not the person.
 *   5. **The person test** (`verify/personTest.ts`): the required scope, no
 *      app-only shape, an allowed client. Refusals `not-a-user-token` and
 *      `wrong-client`.
 *   6. **Roles: a string is ONE role**; a path reads nested roles.
 *   7. **Unknown is not none**: a roles claim replaced by an overage pointer
 *      is refused as `roles-unknown`.
 *   8. **Clock tolerance** defaults to 60 s.
 *
 * Emits nothing. The door that called it reports the refusal — by CLASS. The
 * token, its claims and the IdP's own text never appear in an error.
 *
 * @example
 *   const verifier = oidcIdentity({
 *     issuer: 'https://login.microsoftonline.com/<tenant>/v2.0',
 *     audience: '<API client id>',
 *     userIdClaim: 'oid',
 *     requiredScope: 'access_as_user',
 *     allowedClients: ['<web client id>'],
 *   });
 *   standingAgent({ …, identity: { verify: verifier.verify } });
 */
import type { IdentityVerifier } from '../../hosting/identityVerification.js';
import { type ClaimPath } from './verify/claims.js';
import { type DiscoveredIssuer, type DiscoveryFetch } from './verify/discovery.js';
import { type JoseBackend } from './verify/jwtCore.js';
export type { DiscoveryFetch } from './verify/discovery.js';
export interface OidcIdentityOptions {
    /**
     * The issuer, exactly as the discovery document names it (compared
     * exactly, trailing `/` included). With discovery on it must be `https` —
     * it is FETCHED — except `http` on a loopback host when
     * {@link allowLoopbackHttp} is set. With discovery off it is a literal,
     * compared and never fetched.
     */
    readonly issuer: string;
    /** THIS API's name at the IdP — never a client's id. */
    readonly audience: string | readonly string[];
    /**
     * The claim that names the person — REQUIRED, no default. It decides who
     * owns every conversation, forever. `oid` on Entra ID; a custom
     * `objectGUID` claim on AD FS; `sub` is wrong on both. A top-level name (a
     * URI is fine; dots are never split). The value is taken as bytes: never
     * trimmed or case-folded.
     */
    readonly userIdClaim: string;
    /**
     * The OAuth scope only this API's PERSON tokens carry (Entra
     * `access_as_user`, an AD FS scope granted to the web client, a Keycloak
     * client scope). One scope, no spaces, compared exactly.
     */
    readonly requiredScope: string;
    /** Where the scopes are. Default `'scp'` (Entra, AD FS); `'scope'` for RFC 9068 IdPs (Keycloak, Okta). */
    readonly scopeClaim?: string;
    /**
     * The clients allowed to obtain a person token for this API, read from
     * `azp`, else `appid`, else `cid`, else `client_id`. `'any'` turns the check
     * off — the banner says so. A token naming no client is refused.
     *
     * **Every client listed here must have service accounts / the
     * client-credentials grant turned OFF.** On Keycloak and Okta a service
     * account's token carries your API's scope, so this list is the check that
     * refuses it — a listed client that can mint tokens for itself is a daemon
     * that passes as a person. `identityFromConfig` refuses `'any'` in production.
     */
    readonly allowedClients: readonly string[] | 'any';
    /** The roles claim: a top-level name or a path (`['realm_access', 'roles']`). Default `'roles'`. A string is one role. */
    readonly rolesClaim?: ClaimPath;
    /** Overrides the discovery document's `jwks_uri`. REQUIRED when discovery is off. Must be `https`. */
    readonly jwksUrl?: string;
    /** `'on'` (default) reads the issuer's discovery document; `'off'` needs {@link jwksUrl} and treats `issuer` as a literal. */
    readonly discovery?: 'on' | 'off';
    /** Seconds of clock skew tolerated on `exp`/`nbf`. Default 60; at most 300 — skew, not lifetime. */
    readonly clockToleranceSeconds?: number;
    /**
     * Signature algorithms. Default: the RSA and ECDSA families. `HS*` is
     * REFUSED: a key set is public, and an HMAC algorithm over a published key is
     * the classic algorithm-confusion forgery.
     */
    readonly algorithms?: readonly string[];
    /** Accept `http` URLs on a loopback host (development only). Default `false`. */
    readonly allowLoopbackHttp?: boolean;
    /** Timeout for the discovery fetch, in ms. Default 5000; 1 to 60 000. */
    readonly discoveryTimeoutMs?: number;
    /** After an outage, the least time between two discovery attempts, in ms. Default 5000; 0 to 3 600 000. */
    readonly discoveryRetryMs?: number;
    /** The fetch discovery uses. Default: the runtime's `fetch`. */
    readonly fetch?: DiscoveryFetch;
    /**
     * Where a later CHANGE of discovery state is written (outage → misconfigured
     * → ready), with its full reason — the server's log, never a caller's reply.
     * Default `console.warn`.
     */
    readonly log?: (line: string) => void;
    /** An already-imported `jose` (see `jwksIdentity`'s `backend`). */
    readonly backend?: JoseBackend;
}
/**
 * Where discovery stands. `ready` is final for the life of the verifier;
 * `outage` is retried on a later request; `misconfigured` is final.
 */
export type OidcDiscoveryState = {
    readonly kind: 'ready';
    readonly issuer: DiscoveredIssuer;
} | {
    readonly kind: 'outage';
    readonly reason: string;
} | {
    readonly kind: 'misconfigured';
    readonly check: string;
};
/** The verifier, plus its discovery state for a boot check or a banner. */
export interface OidcIdentity extends IdentityVerifier {
    /**
     * Run discovery now (or return the settled answer). Never throws. A second
     * call after an outage retries, no sooner than `discoveryRetryMs`.
     */
    discover(): Promise<OidcDiscoveryState>;
}
export declare function oidcIdentity(options: OidcIdentityOptions): OidcIdentity;
/** The most clock skew tolerated: skew between clocks, never a second token lifetime. */
export declare const MAX_CLOCK_TOLERANCE_SECONDS = 300;
