UNPKG

vouchsafe

Version:

Self-verifying identity and offline trust verification for JWTs, including attestations, vouches, revocations, and multi-hop trust chains.

155 lines (134 loc) 5.08 kB
import { validateTrustChain } from './trustchain.mjs'; // adjust path as needed /** * Legacy-style verifyTrustChain wrapper. * * This preserves the older API: * * const result = await verifyTrustChain(subject, trusted, { * tokens: chainTokens, * purposes, * maxDepth, // optional * findAll // optional: true => returnAllValidChains * }); * * Returned shape: * { * valid: true/false, * leaf: { * token: <subjectToken.token>, * payload: <subjectToken.decoded> * }, * trustRoot: <last token object on the first valid chain>, * chain: <array of token objects from subject → trust root>, * purposes: <purposes that survived on that chain> * } */ export async function verifyTrustChain(subjectTokenInput, trustedIssuers, options) { if (!options) { options = {}; } // --------------------------------------------------------------------- // 1. Normalize the token set. // The legacy API passes `options.tokens` as the set of tokens // available to the evaluator. We must ensure the subject token // is also present in that set. // --------------------------------------------------------------------- const tokens = []; if (options.tokens && Array.isArray(options.tokens)) { for (let i = 0; i < options.tokens.length; i++) { tokens.push(options.tokens[i]); } } // NOTE (Mutation): prepend the subject token to the token list. // This guarantees the subject is included even if the caller forgot. // prepareTclean/validateTrustChain will handle deduplication. tokens.unshift(subjectTokenInput); // --------------------------------------------------------------------- // 2. Extract required purposes (legacy name: `purposes`). // --------------------------------------------------------------------- let requiredPurposes = null; if (options.purposes && Array.isArray(options.purposes)) { requiredPurposes = options.purposes; } // --------------------------------------------------------------------- // 3. Map legacy options → new evaluator options. // // - maxDepth → maxDepth // - findAll: true → returnAllValidChains: true // --------------------------------------------------------------------- const evalOptions = {}; if (typeof options.maxDepth === "number") { evalOptions.maxDepth = options.maxDepth; } if (options.findAll === true) { evalOptions.returnAllValidChains = true; } // --------------------------------------------------------------------- // 4. Delegate to validateTrustChain. // This performs: // - token decoding + cleaning // - graph construction // - revocation/burn handling // - BFS traversal and purpose intersection // --------------------------------------------------------------------- const evalResult = await validateTrustChain( tokens, subjectTokenInput, trustedIssuers, requiredPurposes, evalOptions ); // If the evaluation failed, return a legacy-shaped failure result. if (!evalResult || evalResult.valid !== true) { return { valid: false, leaf: null, trustRoot: null, chain: [], purposes: [] }; } // --------------------------------------------------------------------- // 5. Extract the first valid chain and map to the legacy structure. // // - leaf → evalResult.subjectToken // - trustRoot → last token on the first valid chain // - chain → that chain's token list // - purposes → that chain's purposes // --------------------------------------------------------------------- let firstChain = null; if (evalResult.chains && Array.isArray(evalResult.chains) && evalResult.chains.length > 0) { firstChain = evalResult.chains[0]; } const leafToken = evalResult.subjectToken || null; let chainArray = []; let trustRootToken = null; let chainPurposes = []; if (firstChain) { if (firstChain.chain && Array.isArray(firstChain.chain)) { chainArray = firstChain.chain; if (firstChain.chain.length > 0) { // The trust root is the last token on the chain. trustRootToken = firstChain.chain[firstChain.chain.length - 1]; } } if (firstChain.purposes && Array.isArray(firstChain.purposes)) { chainPurposes = firstChain.purposes; } } // Legacy `leaf` shape: { token, payload } let legacyLeaf = null; if (leafToken) { legacyLeaf = { token: leafToken.token, payload: leafToken.decoded }; } return { valid: true, leaf: legacyLeaf, trustRoot: trustRootToken, chain: chainArray, purposes: chainPurposes }; }