UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

54 lines (53 loc) • 2.9 kB
/** * verify/claims — reading a verified claim set, one rule per kind of claim. * * Three readings, and the difference between them is the whole file: * * • **A roles claim is a LIST.** An array contributes each string entry as * one role; a single string is ONE role. `"Not bot-users"` is the role * `Not bot-users` — never the two roles `Not` and `bot-users`, which is how * a space-splitting reader lets a group name satisfy a role gate it does * not name. (`'space-delimited'` keeps the old reading, by request only.) * • **A scope claim is space-delimited** — RFC 8693 §4.2 / RFC 9068 §2.2.3 * define `scope` that way, and Entra's `scp` follows it. An array is also * read, entry by entry. * • **A claim NAME is never split.** `urn:bot:objectguid` and * `http://schemas.microsoft.com/…/role` contain dots and colons and are one * name each. A nested claim is named by a PATH — an array of names * (`['realm_access', 'roles']` for Keycloak) — never by a dotted string. * * Comparison is always exact, and nothing here trims, case-folds or tidies a * value: ids are opaque bytes (rule 7). */ /** A top-level claim name, or a path of names into nested objects. */ export type ClaimPath = string | readonly string[]; /** How a roles claim's single STRING is read. */ export type RolesFormat = 'list' | 'space-delimited'; /** The value at `path`, or `undefined`. A string path is ONE name. */ export declare function claimAt(payload: Readonly<Record<string, unknown>>, path: ClaimPath): unknown; /** A path, spelled for a banner or a refusal — never a value. */ export declare function pathLabel(path: ClaimPath): string; /** * Roles out of a claim value, never invented: ABSENT when the claim is absent, * empty, or holds no string. */ export declare function rolesOf(value: unknown, format: RolesFormat): readonly string[] | undefined; /** * Scopes out of a scope claim: a space-delimited string or an array of * strings. ABSENT when the claim is absent or holds nothing — which is not the * same fact as "holds no matching scope", and the person test tells them apart. */ export declare function scopesOf(value: unknown): readonly string[] | undefined; /** * The claims that name the client which OBTAINED a token, in the order they * are read: `azp` (OIDC, Entra v2, Keycloak), `appid` (Entra v1, AD FS), `cid` * (Okta), `client_id` (RFC 9068). */ export declare const CLIENT_CLAIMS: readonly string[]; /** * The client that obtained the token: the FIRST of {@link CLIENT_CLAIMS} that * is present. A present claim that is not a non-empty string names nobody, and * the answer is `undefined` — never a fall-through to the next claim, which * would let a token choose which of its claims is read. */ export declare function clientOf(payload: Readonly<Record<string, unknown>>): string | undefined;