UNPKG

did-jwt

Version:

Library for Signing and Verifying JWTs that use DIDs as issuers and JWEs that use DIDs as recipients

152 lines (132 loc) 5.21 kB
import type { VerificationMethod } from 'did-resolver' import { JWT_ERROR } from './Errors.js' import { type JWTDecoded, type JWTVerifyOptions, resolveAuthenticator, verifyJWT, verifyJWTDecoded } from './JWT.js' export const CONDITIONAL_PROOF_2022 = 'ConditionalProof2022' export async function verifyProof( jwt: string, { header, payload, signature, data }: JWTDecoded, authenticator: VerificationMethod, options: JWTVerifyOptions ): Promise<VerificationMethod> { if (authenticator.type === CONDITIONAL_PROOF_2022) { return verifyConditionalProof(jwt, { payload, header, signature, data }, authenticator, options) } else { return verifyJWTDecoded({ header, payload, data, signature }, [authenticator]) } } export async function verifyConditionalProof( jwt: string, { header, payload, signature, data }: JWTDecoded, authenticator: VerificationMethod, options: JWTVerifyOptions ): Promise<VerificationMethod> { // Validate the condition according to its condition property if (authenticator.conditionWeightedThreshold) { return verifyConditionWeightedThreshold(jwt, { header, payload, data, signature }, authenticator, options) } else if (authenticator.conditionDelegated) { return verifyConditionDelegated(jwt, { header, payload, data, signature }, authenticator, options) } // TODO other conditions throw new Error( `${JWT_ERROR.INVALID_JWT}: conditional proof type did not find condition for authenticator ${authenticator.id}.` ) } async function verifyConditionWeightedThreshold( jwt: string, { header, payload, data, signature }: JWTDecoded, authenticator: VerificationMethod, options: JWTVerifyOptions ): Promise<VerificationMethod> { if (!authenticator.conditionWeightedThreshold || !authenticator.threshold) { throw new Error('Expected conditionWeightedThreshold and threshold') } const issuers: string[] = [] const threshold = authenticator.threshold let weightCount = 0 for (const weightedCondition of authenticator.conditionWeightedThreshold) { const currentCondition = weightedCondition.condition let foundSigner: VerificationMethod | undefined try { if (currentCondition.type === CONDITIONAL_PROOF_2022) { if (!options.didAuthenticator) { throw new Error('Expected didAuthenticator') } const newOptions: JWTVerifyOptions = { ...options, didAuthenticator: { didResolutionResult: options.didAuthenticator?.didResolutionResult, authenticators: [currentCondition], issuer: currentCondition.id, }, } const { verified } = await verifyJWT(jwt, newOptions) if (verified) { foundSigner = currentCondition } } else { foundSigner = await verifyJWTDecoded({ header, payload, data, signature }, currentCondition) } } catch (e) { if (!(e as Error).message.startsWith(JWT_ERROR.INVALID_SIGNATURE)) throw e } if (foundSigner && !issuers.includes(foundSigner.id)) { issuers.push(foundSigner.id) weightCount += weightedCondition.weight if (weightCount >= threshold) { return authenticator } } } throw new Error(`${JWT_ERROR.INVALID_SIGNATURE}: condition for authenticator ${authenticator.id} is not met.`) } async function verifyConditionDelegated( jwt: string, { header, payload, data, signature }: JWTDecoded, authenticator: VerificationMethod, options: JWTVerifyOptions ): Promise<VerificationMethod> { if (!authenticator.conditionDelegated) { throw new Error('Expected conditionDelegated') } if (!options.resolver) { throw new Error('Expected resolver') } let foundSigner: VerificationMethod | undefined const issuer = authenticator.conditionDelegated const didAuthenticator = await resolveAuthenticator(options.resolver, header.alg, issuer, options.proofPurpose) const didResolutionResult = didAuthenticator.didResolutionResult if (!didResolutionResult?.didDocument) { throw new Error(`${JWT_ERROR.RESOLVER_ERROR}: Could not resolve delegated DID ${issuer}.`) } const delegatedAuthenticator = didAuthenticator.authenticators.find((authenticator) => authenticator.id === issuer) if (!delegatedAuthenticator) { throw new Error( `${JWT_ERROR.NO_SUITABLE_KEYS}: Could not find delegated authenticator ${issuer} in it's DID Document` ) } if (delegatedAuthenticator.type === CONDITIONAL_PROOF_2022) { const { verified } = await verifyJWT(jwt, { ...options, ...{ didAuthenticator: { didResolutionResult, authenticators: [delegatedAuthenticator], issuer: delegatedAuthenticator.id, }, }, }) if (verified) { foundSigner = delegatedAuthenticator } } else { try { foundSigner = verifyJWTDecoded({ header, payload, data, signature }, delegatedAuthenticator) } catch (e) { if (!(e as Error).message.startsWith('invalid_signature:')) throw e } } if (foundSigner) { return authenticator } throw new Error(`${JWT_ERROR.INVALID_SIGNATURE}: condition for authenticator ${authenticator.id} is not met.`) }