UNPKG

@sphereon/did-auth-siop

Version:

Self Issued OpenID V2 (SIOPv2) and OpenID 4 Verifiable Presentations (OID4VP)

141 lines (129 loc) 5.51 kB
import { Format } from '@sphereon/pex-models' import { CommonSupportedMetadata, DiscoveryMetadataPayload, RPRegistrationMetadataPayload, SIOPErrors, SubjectSyntaxTypesSupportedValues, } from '../types' export function assertValidMetadata(opMetadata: DiscoveryMetadataPayload, rpMetadata: RPRegistrationMetadataPayload): CommonSupportedMetadata { let subjectSyntaxTypesSupported: string[] = [] const credentials = supportedCredentialsFormats(rpMetadata.vp_formats, opMetadata.vp_formats) const isValidSubjectSyntax = verifySubjectSyntaxes(rpMetadata.subject_syntax_types_supported) if (isValidSubjectSyntax && rpMetadata.subject_syntax_types_supported) { subjectSyntaxTypesSupported = supportedSubjectSyntaxTypes( rpMetadata.subject_syntax_types_supported, opMetadata.subject_syntax_types_supported as string[], ) } else if (isValidSubjectSyntax && (!rpMetadata.subject_syntax_types_supported || !rpMetadata.subject_syntax_types_supported.length)) { if (opMetadata.subject_syntax_types_supported) { subjectSyntaxTypesSupported = [...opMetadata.subject_syntax_types_supported] } } return { vp_formats: credentials, subject_syntax_types_supported: subjectSyntaxTypesSupported } } function getIntersection<T>(rpMetadata: Array<T> | T, opMetadata: Array<T> | T): Array<T> { let arrayA, arrayB if (!Array.isArray(rpMetadata)) { arrayA = [rpMetadata] } else { arrayA = rpMetadata } if (!Array.isArray(opMetadata)) { arrayB = [opMetadata] } else { arrayB = opMetadata } return arrayA.filter((value) => arrayB.includes(value)) } function verifySubjectSyntaxes(subjectSyntaxTypesSupported: string[] | undefined): boolean { if (subjectSyntaxTypesSupported?.length) { if (Array.isArray(subjectSyntaxTypesSupported)) { if ( subjectSyntaxTypesSupported.length === subjectSyntaxTypesSupported.filter( (sst) => sst.includes(SubjectSyntaxTypesSupportedValues.DID.valueOf()) || sst === SubjectSyntaxTypesSupportedValues.JWK_THUMBPRINT.valueOf(), ).length ) { return true } } } return false } function supportedSubjectSyntaxTypes(rpMethods: string[] | string, opMethods: string[] | string): Array<string> { const rpMethodsList = Array.isArray(rpMethods) ? rpMethods : [rpMethods] const opMethodsList = Array.isArray(opMethods) ? opMethods : [opMethods] const supportedSubjectSyntaxTypes = getIntersection(rpMethodsList, opMethodsList) if (supportedSubjectSyntaxTypes.indexOf(SubjectSyntaxTypesSupportedValues.DID.valueOf()) !== -1) { return [SubjectSyntaxTypesSupportedValues.DID.valueOf()] } if (rpMethodsList.includes(SubjectSyntaxTypesSupportedValues.DID.valueOf())) { const supportedExtendedDids: string[] = opMethodsList.filter((method) => method.startsWith('did:')) if (supportedExtendedDids.length) { return supportedExtendedDids } } if (opMethodsList.includes(SubjectSyntaxTypesSupportedValues.DID.valueOf())) { const supportedExtendedDids: string[] = rpMethodsList.filter((method) => method.startsWith('did:')) if (supportedExtendedDids.length) { return supportedExtendedDids } } if (!supportedSubjectSyntaxTypes.length) { throw Error(SIOPErrors.DID_METHODS_NOT_SUPORTED) } const supportedDidMethods = supportedSubjectSyntaxTypes.filter((sst) => sst.includes('did:')) if (supportedDidMethods.length) { return supportedDidMethods } return supportedSubjectSyntaxTypes } function collectAlgValues(algTableObject: any): string[] { const algValues: string[] = [] for (const key of Object.keys(algTableObject)) { algValues.push(...algTableObject[key]) } return algValues } const isJwtFormat = (crFormat: string) => crFormat.includes('jwt') || crFormat.includes('mdoc') function getFormatIntersection(rpFormat: Format, opFormat: Format): Format { const intersectionFormat: Record<string, any> = {} const supportedCredentials = getIntersection(Object.keys(rpFormat), Object.keys(opFormat)) if (!supportedCredentials.length) { throw new Error(SIOPErrors.CREDENTIAL_FORMATS_NOT_SUPPORTED) } supportedCredentials.forEach(function (crFormat: string) { const rpFormatElement = rpFormat[crFormat as keyof Format] const opFormatElement = opFormat[crFormat as keyof Format] const rpAlgs = collectAlgValues(rpFormatElement) const opAlgs = collectAlgValues(opFormatElement) let methodKeyRP = undefined let methodKeyOP = undefined if (rpFormatElement !== undefined) { Object.keys(rpFormatElement).forEach((k) => (methodKeyRP = k)) } if (opFormatElement !== undefined) { Object.keys(opFormatElement).forEach((k) => (methodKeyOP = k)) } if (methodKeyRP !== methodKeyOP) { throw new Error(SIOPErrors.CREDENTIAL_FORMATS_NOT_SUPPORTED) } const algs = getIntersection(rpAlgs, opAlgs) if (!algs.length && isJwtFormat(crFormat)) { throw new Error(SIOPErrors.CREDENTIAL_FORMATS_NOT_SUPPORTED) } intersectionFormat[crFormat] = {} if (methodKeyOP !== undefined) { intersectionFormat[crFormat][methodKeyOP] = algs } }) return intersectionFormat } export function supportedCredentialsFormats(rpFormat: Format, opFormat: Format): Format { if (!rpFormat || !opFormat || !Object.keys(rpFormat).length || !Object.keys(opFormat).length) { throw new Error(SIOPErrors.CREDENTIALS_FORMATS_NOT_PROVIDED) } return getFormatIntersection(rpFormat, opFormat) }