UNPKG

@cerbos/embedded

Version:

Client library for interacting with embedded Cerbos policy decision points generated by Cerbos Hub from server-side Node.js and browser-based applications

339 lines 12.1 kB
import type { Options as CoreOptions, DecisionLogEntry, JWT, SourceAttributes, Value } from "@cerbos/core"; import { Bundle } from "./bundle"; import { Transport } from "./transport"; type LoadResult = { bundle: Bundle; error?: undefined; } | { bundle?: undefined; error: LoadError; }; /** * Error thrown when a {@link Loader} fails to load an embedded policy decision point bundle. * * @public */ export declare class LoadError extends Error { /** * The error that caused loading the embedded policy decision point bundle to fail. */ readonly cause: unknown; constructor( /** * The error that caused loading the embedded policy decision point bundle to fail. */ cause: unknown); } /** * WebAssembly binary code of an embedded policy decision point bundle, or a URL or HTTP response from which to stream it. * * @public */ export type Source = string | URL | ArrayBufferView | ArrayBuffer | Response | WebAssembly.Module | Promise<ArrayBufferView | ArrayBuffer | Response | WebAssembly.Module>; /** * Options for creating a new {@link Embedded} client or {@link Loader}. * * @public */ export interface Options extends Pick<CoreOptions, "headers" | "userAgent"> { /** * A function to verify and decode JWTs passed as auxiliary data, returning the JWT payload. * * @defaultValue (throw an error when a JWT is passed) */ decodeJWTPayload?: DecodeJWTPayload | undefined; /** * {@link https://docs.cerbos.dev/cerbos/latest/configuration/engine#default_policy_version | Default policy version} to apply to requests that do not specify one. * * @defaultValue `"default"` */ defaultPolicyVersion?: string | undefined; /** * {@link https://docs.cerbos.dev/cerbos/latest/configuration/engine#globals | Global variables} to pass environment-specific information to policy conditions. * * @defaultValue `{}` */ globals?: Record<string, Value> | undefined; /** * Enable {@link https://docs.cerbos.dev/cerbos/latest/configuration/engine#lenient_scopes | lenient scope search}? * * By default, when a request specifies a scope of `a.b.c` then a policy must exist with that exact scope. * If lenient scope search is enabled, then the policy decision point will fall back to trying scopes `a.b`, `a`, and `""` * if a policy with scope `a.b.c` does not exist. * * @defaultValue `false` */ lenientScopeSearch?: boolean | undefined; /** * A function returning the current time, to be used when evaluating policy conditions. * * @remarks * The function can either return a {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date | Date} or a number of milliseconds elapsed since the Unix epoch. * * @defaultValue `Date.now` */ now?: (() => Date | number) | undefined; /** * A callback to invoke when the embedded policy decision point bundle has been loaded. * * @defaultValue (no-op) */ onLoad?: ((metadata: BundleMetadata) => void | Promise<void>) | undefined; /** * A callback to invoke when the embedded policy decision point bundle has failed to load. * * @defaultValue (no-op) */ onError?: ((error: LoadError) => void | Promise<void>) | undefined; /** * A callback to invoke when a decision is made by the embedded policy decision point. * * @defaultValue (no-op) */ onDecision?: ((entry: DecisionLogEntry) => void | Promise<void>) | undefined; } /** * A function to verify and decode a JWT, returning its payload. * * @example * Using {@link https://www.npmjs.com/package/jose | jose}: * * ```typescript * import type { DecodeJWTPayload, DecodedJWTPayload } from "@cerbos/embedded"; * import { JWTVerifyGetKey, createRemoteJWKSet, jwtVerify } from "jose"; * * interface KeySet { * issuer: string; * jwks: JWTVerifyGetKey; * } * * const keySets: Record<string, KeySet> = { * auth0: { * issuer: "https://example.auth0.com/", * jwks: createRemoteJWKSet( * new URL("https://example.auth0.com/.well-known/jwks.json") * ), * }, * okta: { * issuer: "https://example.okta.com/oauth2/default", * jwks: createRemoteJWKSet( * new URL("https://example.okta.com/oauth2/default/v1/keys") * ), * }, * }; * * const decodeJWTPayload: DecodeJWTPayload = async ({ token, keySetId }) => { * if (!keySetId) { * throw new Error("Missing key set ID"); * } * * const keySet = keySets[keySetId]; * * if (!keySet) { * throw new Error(`Unknown key set ID "${keySetId}"`); * } * * const { issuer, jwks } = keySet; * * const { payload } = await jwtVerify(token, jwks, { * issuer, * audience: "https://example.com/", * }); * * return payload as DecodedJWTPayload; * }; * ``` * * @public */ export type DecodeJWTPayload = (jwt: JWT) => DecodedJWTPayload | Promise<DecodedJWTPayload>; /** * The decoded payload of a JWT, containing the claims. * * @public */ export type DecodedJWTPayload = Record<string, Value>; /** * Metadata describing an embedded policy decision point bundle. * * @public */ export interface BundleMetadata { /** * The URL from which the bundle was downloaded. */ url?: string | undefined; /** * The commit SHA from which the bundle was built. */ commit: string; /** * The time at which the bundle was built. */ builtAt: Date; /** * The IDs of the policies included in the bundle. * * @deprecated Use {@link BundleMetadata.sourceAttributes} instead. */ policies: string[]; /** * Map of the IDs of policies included in the bundle to metadata about their sources. */ sourceAttributes: Record<string, SourceAttributes>; } /** * Loads an embedded policy decision point bundle from a given source. * * @public */ export declare class Loader { /** @internal */ _active: LoadResult | Promise<LoadResult>; /** @internal */ readonly _options: Options; /** @internal */ readonly _userAgent: string; private readonly logger; /** * Load an embedded policy decision point (PDP) bundle from a given source. * * @param source - WebAssembly binary code of an embedded PDP bundle, or a URL or HTTP response from which to stream it. * @param options - additional settings. * * @remarks * Bundle download URLs are available in the "Embedded" section of the "Decision points" page of your Cerbos Hub workspace. * * The bundle will be loaded in the background when the loader is created. * If loading fails, then the first request from the client using the loader will throw an error. * To detect failure to load the bundle before making any requests, provide an {@link Options.onError} callback or await the {@link Loader.active} method. * * @example * Fetch an embedded PDP bundle via HTTP in a {@link https://caniuse.com/wasm | supported browser} or Node.js: * * ```typescript * const loader = new Loader("https://lite.cerbos.cloud/bundle?workspace=...&label=..."); * ``` * * @example * Read an embedded PDP bundle from disk in Node.js: * * ```typescript * import { readFile } from "fs/promises"; * * const loader = new Loader(readFile("policies.wasm")); * ``` * * @example * Load an embedded PDP bundle from a precompiled WebAssembly module (requires a bundler): * * ```typescript * import bundle from "bundle.wasm"; * * const loader = new Loader(bundle); * ``` */ constructor(source: Source, options?: Options); /** * Resolves to the metadata of the loaded bundle, or rejects with the error that was encountered when loading the bundle. */ active(): Promise<BundleMetadata>; /** @internal */ readonly _transport: Transport; /** @internal */ protected _load(source: Source, url: string | undefined, initial?: boolean): Promise<LoadResult>; /** @internal */ protected _onLoad(bundle: Bundle, _initial: boolean): Promise<void>; /** @internal */ protected _onError(error: LoadError): Promise<void>; } /** * Options for creating a new {@link AutoUpdatingLoader}. * * @public */ export interface AutoUpdateOptions extends Options { /** * Whether to activate updated embedded policy decision point bundles as soon as they are downloaded. * * @remarks * If `false`, new bundles will be downloaded automatically but not used to evaluate policy decisions until you call {@link AutoUpdatingLoader.activate}. * This might be useful if you want to activate updates only on page transitions to avoid layout shifts in your application. * * To detect whether an update is available to activate, provide an {@link Options.onLoad} callback or check the {@link AutoUpdatingLoader.pending} property. * * @defaultValue `true` */ activateOnLoad?: boolean; /** * The delay (in milliseconds) between successive requests to check for new embedded policy decision point bundles. * * @remarks * The interval will be increased to the minimum of 10 seconds if a smaller value is specified. * * @defaultValue `60_000` (1 minute) */ interval?: number; } /** * Loads an embedded policy decision point bundle from a given URL, and polls for updates. * * @public */ export declare class AutoUpdatingLoader extends Loader { private readonly url; private readonly activateOnLoad; private readonly interval; private _pending; private etag; private running; private abortController?; private timeout?; /** * Load an embedded policy decision point bundle from a given URL. * * @param url - URL from which to stream bundles. * @param options - additional settings. * * @remarks * Bundle download URLs are available in the "Embedded" section of the "Decision points" page of your Cerbos Hub workspace. * * The bundle will be loaded in the background when the loader is created. * If initial loading fails, then the first request from the client using the loader will throw an error. * To detect failure to load the bundle before making any requests, provide an {@link Options.onError} callback or await the {@link Loader.active} method. * * Failure to load updates after the initial load will not cause requests from the client to throw errors, * but errors will be passed to the {@link Options.onError} callback. */ constructor(url: string | URL, { activateOnLoad, interval, ...options }?: AutoUpdateOptions); /** * The metadata of a new embedded policy decision point bundle that has been downloaded but is not yet being used to evaluate policy decisions. * * @remarks * Only set if {@link AutoUpdateOptions.activateOnLoad} is `false` and an update has been downloaded. * * Use {@link AutoUpdatingLoader.activate} to start using the pending bundle to evaluate policy decisions. */ get pending(): BundleMetadata | undefined; /** * Promote the {@link AutoUpdatingLoader.pending | pending} embedded policy decision point bundle (if any) to active, so that it is used to evaluate policy decisions. * * @remarks * This method is a no-op if an update has not been downloaded, or if {@link AutoUpdateOptions.activateOnLoad} is `true` (the default). */ activate(): void; /** * Stops polling for new embedded policy decision point bundles, and aborts any in-flight updates. */ stop(): void; /** @internal */ protected _onLoad(bundle: Bundle, initial: boolean): Promise<void>; /** @internal */ protected _onError(error: LoadError): Promise<void>; private scheduleUpdate; private update; private download; private suppressError; } export {}; //# sourceMappingURL=loader.d.ts.map