agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
90 lines (89 loc) • 4.63 kB
TypeScript
/**
* verify/jwtCore — the one place a signed token is checked, shared by every
* inbound verifier (`jwksIdentity`, `oidcIdentity`).
*
* Pattern: shared kernel. Two verifiers that each spelled "signature, issuer,
* audience, expiry" would be two answers to one question, and the day
* one of them required `exp` and the other did not is the day a token
* with no lifetime got through the lenient one.
* Role: load `jose`, map its failures onto the library's failure words, and
* hand back the verified claim set. Nothing here reads WHO the token
* is about: that is each verifier's own business.
*
* ── Every token expires ─────────────────────────────────────────────────────
* `jose` checks `exp` only when a token carries one (6.2.8,
* `validateClaimsSet`). A signed token WITHOUT `exp` would verify and never
* expire — another token type signed by the same keys, or a proxy that forgot
* the claim. So `exp` is required twice: `requiredClaims: ['exp']` for `jose`,
* and a check of the returned claims for any other backend (a stub, a fork)
* that ignores that option. Refused as `unverifiable`: the token is not one
* this door reads, and `expired` would tell the client to refresh.
*/
/**
* The slice of `jose` the inbound verifiers use — declared STRUCTURALLY, so a
* stub, a pinned fork, or a future major satisfies it without this package
* taking a hard type dependency on an optional peer. (The `UnpdfBackend`
* precedent.)
*/
export interface JoseBackend {
createRemoteJWKSet(url: URL, options?: Record<string, unknown>): unknown;
jwtVerify(token: string, key: unknown, options?: Record<string, unknown>): Promise<{
payload: Record<string, unknown>;
}>;
}
/** Raised when a token must be verified and `jose` is not installed. */
export declare class MissingJwksSupportError extends Error {
readonly code: "ERR_MISSING_JWKS_SUPPORT";
constructor();
}
/**
* The asymmetric families. Symmetric algorithms (`HS*`) are deliberately NOT
* here: with a JWKS the key is public, and a verifier that accepts an HMAC alg
* over a published key is the classic algorithm-confusion forgery.
*/
export declare const DEFAULT_ALGORITHMS: readonly string[];
type JoseFailure = 'expired' | 'not-yet-valid' | 'wrong-audience' | 'wrong-issuer' | 'unverifiable' | 'keys-unavailable';
/**
* `jose`'s stable error codes → this library's failure vocabulary.
*
* Mapped by CODE STRING rather than by `instanceof`, and that is the load-
* bearing choice: the error classes are not on `jose`'s main entry point (they
* live under `jose/errors`), the codes are documented as stable, and a string
* comparison survives a duplicated copy of the library in a `node_modules` tree
* — which `instanceof` famously does not.
*
* Every code is one the verifiers have SEEN the installed library produce; the
* pin test (test/adapters/identity/jwks.test.ts) reproduces each one.
*/
export declare function joseFailureOf(err: unknown): JoseFailure;
/** What {@link verifySignedToken} checks the token against. */
export interface SignedTokenChecks {
/** Accepted `iss` values — compared exactly. */
readonly issuer: string | readonly string[];
/** Accepted `aud` values. */
readonly audience: string | readonly string[];
readonly algorithms: readonly string[];
/** Seconds of clock skew tolerated on `exp`/`nbf`. */
readonly clockToleranceSeconds?: number;
/** The verifier's name, for the 503 — never its configuration. */
readonly verifierName: string;
}
/**
* Check one token's signature and registered claims, and hand back its claim
* set — or throw {@link IdentityNotVerifiedError} (the caller's token) /
* {@link VerifierUnavailableError} (this deployment's key set).
*
* Neither error carries the token, the claims or the library's own text, and
* the original is deliberately NOT attached as `cause`: a cause travels into
* every serializer that walks own properties.
*/
export declare function verifySignedToken(jose: JoseBackend, keys: unknown, token: string, checks: SignedTokenChecks): Promise<Record<string, unknown>>;
/**
* Load `jose`, or refuse by name.
*
* `jose` v6 is ESM-only, so a dynamic import is tried first and the CJS require
* is the fallback — one of the two works on every runtime this package
* supports. (The `unpdf` loader's shape, for the same reason.)
*/
export declare function loadJose(): Promise<JoseBackend>;
export {};