@idos-network/credentials 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["v1","idosCredentialsV1","ed25519Signature2020V1","idosCredentialSubjectV1","idosCredentialSubjectV2","idosCredentialSubjectFaceIdV1"],"sources":["../../assets/ed25519-signature-2020-v1.json","../../assets/idos-credential-subject-face-id-v1.json","../../assets/idos-credential-subject-v1.json","../../assets/idos-credential-subject-v2.json","../../assets/idos-credentials-v1.json","../../assets/v1.json","../../src/builder/loader.ts","../../src/builder/verifier.ts","../../src/builder/index.ts"],"sourcesContent":["","","","","","","import { type JsonLDDocumentLoaderInstance, JsonLdDocumentLoader } from \"jsonld-document-loader\";\n\nimport ed25519Signature2020V1 from \"../../assets/ed25519-signature-2020-v1.json\";\nimport idosCredentialSubjectFaceIdV1 from \"../../assets/idos-credential-subject-face-id-v1.json\";\nimport idosCredentialSubjectV1 from \"../../assets/idos-credential-subject-v1.json\";\nimport idosCredentialSubjectV2 from \"../../assets/idos-credential-subject-v2.json\";\nimport idosCredentialsV1 from \"../../assets/idos-credentials-v1.json\";\nimport v1 from \"../../assets/v1.json\";\n\nexport const CONTEXT_V1 = \"https://www.w3.org/2018/credentials/v1\";\nexport const CONTEXT_ED25519_SIGNATURE_2020_V1 = \"https://w3id.org/security/suites/ed25519-2020/v1\";\n\n// Check .github/workflow/pages.yml how those are deployed\nexport const CONTEXT_IDOS_CREDENTIAL_V1 =\n \"https://idos-network.github.io/idos-sdk-js/credentials/idos-credentials-v1.json\";\nexport const CONTEXT_IDOS_CREDENTIAL_V1_SUBJECT =\n \"https://idos-network.github.io/idos-sdk-js/credentials/idos-credential-subject-v1.json\";\nexport const CONTEXT_IDOS_CREDENTIAL_V2_SUBJECT =\n \"https://idos-network.github.io/idos-sdk-js/credentials/idos-credential-subject-v2.json\";\n\nexport const CONTEXT_IDOS_CREDENTIAL_V1_FACE_ID =\n \"https://idos-network.github.io/idos-sdk-js/credentials/idos-credential-subject-face-id-v1.json\";\n\n// Latest contexts for builder\nexport const CONTEXT_IDOS_CREDENTIAL: string = CONTEXT_IDOS_CREDENTIAL_V1;\nexport const CONTEXT_IDOS_CREDENTIAL_SUBJECT: string = CONTEXT_IDOS_CREDENTIAL_V2_SUBJECT;\nexport const CONTEXT_IDOS_SIGNATURE: string = CONTEXT_ED25519_SIGNATURE_2020_V1;\nexport const CONTEXT_IDOS_CREDENTIAL_FACE_ID: string = CONTEXT_IDOS_CREDENTIAL_V1_FACE_ID;\n\nexport function buildDocumentLoader(): JsonLDDocumentLoaderInstance {\n const loader = new JsonLdDocumentLoader();\n\n loader.addStatic(CONTEXT_V1, v1);\n loader.addStatic(CONTEXT_IDOS_CREDENTIAL_V1, idosCredentialsV1);\n loader.addStatic(CONTEXT_ED25519_SIGNATURE_2020_V1, ed25519Signature2020V1);\n\n // plus / basic + liveness credentials\n loader.addStatic(CONTEXT_IDOS_CREDENTIAL_V1_SUBJECT, idosCredentialSubjectV1);\n loader.addStatic(CONTEXT_IDOS_CREDENTIAL_V2_SUBJECT, idosCredentialSubjectV2);\n\n // PoP\n loader.addStatic(CONTEXT_IDOS_CREDENTIAL_V1_FACE_ID, idosCredentialSubjectFaceIdV1);\n\n // DEPRECATED: Those should be removed\n [\n \"1356fea6672d22deb07fedc5fa427478b2e1654c\",\n \"346f14468348e4f3dd00039c89ce9bb49d88777c\",\n \"1e8421744e037dfd7b0289cb39261c73ab6d643c\",\n \"0049d2e93f8913c42398372e2bb65d92bed38ac0\",\n ].forEach((hash) => {\n loader.addStatic(\n `https://raw.githubusercontent.com/idos-network/idos-sdk-js/${hash}/packages/%40credentials/assets/idos-credentials-v1.json`,\n idosCredentialsV1,\n );\n loader.addStatic(\n `https://raw.githubusercontent.com/idos-network/idos-sdk-js/${hash}/packages/%40credentials/assets/idos-credential-subject-v1.json`,\n idosCredentialSubjectV1,\n );\n });\n\n // DEPRECATED: Those should be removed\n [\"1bc3503f5302a7e42777076445d5b05fec8db429\"].forEach((hash) => {\n loader.addStatic(\n `https://raw.githubusercontent.com/idos-network/idos-sdk-js/${hash}/packages/issuer-sdk-js/assets/idos-credentials-v1.json`,\n idosCredentialsV1,\n );\n loader.addStatic(\n `https://raw.githubusercontent.com/idos-network/idos-sdk-js/${hash}/packages/issuer-sdk-js/assets/idos-credential-subject-v1.json`,\n idosCredentialSubjectV1,\n );\n });\n\n return loader.build();\n}\n\nexport const defaultDocumentLoader: JsonLDDocumentLoaderInstance = buildDocumentLoader();\n","import type { JsonLDDocumentLoaderInstance } from \"jsonld-document-loader\";\n\nimport { Ed25519Signature2020 } from \"@digitalbazaar/ed25519-signature-2020\";\nimport * as vc from \"@digitalbazaar/vc\";\n\nimport type {\n AvailableIssuerType,\n VerifiableCredential,\n VerifiableCredentialSubject,\n} from \"../types\";\n\nimport { issuerToKey } from \"../utils\";\nimport { defaultDocumentLoader } from \"./loader\";\n\nexport type VerifyCredentialResult = [boolean, Map<AvailableIssuerType, vc.VerifyCredentialResult>];\n\nexport async function verifyCredential<K = VerifiableCredentialSubject>(\n credential: VerifiableCredential<K>,\n issuers: AvailableIssuerType[],\n customDocumentLoader?: JsonLDDocumentLoaderInstance,\n): Promise<VerifyCredentialResult> {\n const resultsByIssuer: Map<AvailableIssuerType, vc.VerifyCredentialResult> = new Map();\n\n for (const issuer of issuers) {\n const publicKey = await issuerToKey(issuer);\n\n const vcVerifyingSuite = new Ed25519Signature2020({\n key: publicKey,\n verificationMethod: publicKey.id,\n });\n\n const controller = {\n \"@context\": \"https://w3id.org/security/v2\",\n id: publicKey.controller,\n assertionMethod: [publicKey.id],\n authentication: [publicKey.id],\n };\n\n // Verify the signature\n const verifyCredentialResult = await vc.verifyCredential<K>({\n credential: credential,\n suite: vcVerifyingSuite,\n controller,\n documentLoader: customDocumentLoader ?? defaultDocumentLoader,\n });\n\n resultsByIssuer.set(issuer, verifyCredentialResult);\n\n if (verifyCredentialResult.verified) {\n return [true, resultsByIssuer];\n }\n }\n\n return [false, resultsByIssuer];\n}\n","import { Ed25519Signature2020 } from \"@digitalbazaar/ed25519-signature-2020\";\nimport * as vc from \"@digitalbazaar/vc\";\n\nimport type { VerifyCredentialResult } from \"./verifier\";\n\nimport {\n type AvailableIssuerType,\n type CredentialFields,\n CredentialFieldsSchema,\n type CredentialSubject,\n type CredentialSubjectFaceId,\n CredentialSubjectFaceIdSchema,\n CredentialSubjectSchema,\n type VerifiableCredential,\n type VerifiableCredentialSubject,\n} from \"../types\";\nimport { convertValues, issuerToKey } from \"../utils\";\nimport {\n CONTEXT_IDOS_CREDENTIAL,\n CONTEXT_IDOS_CREDENTIAL_FACE_ID,\n CONTEXT_IDOS_CREDENTIAL_SUBJECT,\n CONTEXT_IDOS_SIGNATURE,\n CONTEXT_V1,\n defaultDocumentLoader,\n} from \"./loader\";\nimport { verifyCredential } from \"./verifier\";\n\nexport type Credential = VerifiableCredential<VerifiableCredentialSubject>;\nexport type FaceIdCredential = VerifiableCredential<CredentialSubjectFaceId>;\n\nexport type CredentialBuilder<TInput, TOutput = TInput> = (\n fields: CredentialFields,\n subject: TInput,\n issuer: AvailableIssuerType,\n validate?: boolean,\n) => Promise<VerifiableCredential<TOutput>>;\n\nexport type CredentialConverter<TInput> = (\n subject: TInput,\n validate: boolean,\n // oxlint-disable-next-line typescript/no-explicit-any -- any is needed here\n) => Record<string, any>;\n\nfunction genericCredentialBuilder<TInput, TOutput = TInput>(\n credentialConverter: CredentialConverter<TInput>,\n): CredentialBuilder<TInput, TOutput> {\n async function builder(\n fields: CredentialFields,\n subject: TInput,\n issuer: AvailableIssuerType,\n validate = true,\n ) {\n if (validate) {\n // This raises an z.ZodError exception if the fields are invalid\n CredentialFieldsSchema.parse(fields);\n }\n\n const credentialSubject = credentialConverter(subject, validate);\n\n const key = await issuerToKey(issuer);\n\n // Create credentials container\n const credential = {\n \"@context\": [CONTEXT_V1, CONTEXT_IDOS_CREDENTIAL, CONTEXT_IDOS_SIGNATURE],\n type: [\"VerifiableCredential\"],\n issuer: key.controller,\n ...convertValues(fields),\n credentialSubject,\n };\n\n const suite = new Ed25519Signature2020({ key });\n\n return vc.issue<TOutput>({\n credential,\n suite,\n documentLoader: defaultDocumentLoader,\n });\n }\n\n return builder;\n}\n\nexport const credentialSubjectConverter: CredentialConverter<CredentialSubject> = (\n subject,\n validate,\n) => {\n if (validate) {\n CredentialSubjectSchema.parse(subject);\n }\n\n const { residentialAddress, ...subjectData } = subject;\n return {\n \"@context\": CONTEXT_IDOS_CREDENTIAL_SUBJECT,\n ...convertValues(subjectData),\n ...(residentialAddress ? convertValues(residentialAddress, \"residentialAddress\") : {}),\n };\n};\n\nexport const credentialFaceIdSubjectConverter: CredentialConverter<CredentialSubjectFaceId> = (\n subject,\n validate,\n) => {\n if (validate) {\n CredentialSubjectFaceIdSchema.parse(subject);\n }\n\n return {\n \"@context\": CONTEXT_IDOS_CREDENTIAL_FACE_ID,\n ...convertValues(subject),\n };\n};\n\nexport const buildCredential: CredentialBuilder<CredentialSubject, VerifiableCredentialSubject> =\n genericCredentialBuilder<CredentialSubject, VerifiableCredentialSubject>(\n credentialSubjectConverter,\n );\n\nexport const buildFaceIdCredential: CredentialBuilder<CredentialSubjectFaceId> =\n genericCredentialBuilder<CredentialSubjectFaceId>(credentialFaceIdSubjectConverter);\n\nexport type { VerifyCredentialResult };\nexport { verifyCredential };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AMSA,MAAa,aAAa;AAC1B,MAAa,oCAAoC;AAGjD,MAAa,6BACX;AACF,MAAa,qCACX;AACF,MAAa,qCACX;AAEF,MAAa,qCACX;AAGF,MAAa,0BAAkC;AAC/C,MAAa,kCAA0C;AACvD,MAAa,yBAAiC;AAC9C,MAAa,kCAA0C;AAEvD,SAAgB,sBAAoD;CAClE,MAAM,SAAS,IAAI,sBAAsB;AAEzC,QAAO,UAAU,YAAYA,WAAG;AAChC,QAAO,UAAU,4BAA4BC,4BAAkB;AAC/D,QAAO,UAAU,mCAAmCC,kCAAuB;AAG3E,QAAO,UAAU,oCAAoCC,mCAAwB;AAC7E,QAAO,UAAU,oCAAoCC,mCAAwB;AAG7E,QAAO,UAAU,oCAAoCC,2CAA8B;AAGnF;EACE;EACA;EACA;EACA;EACD,CAAC,SAAS,SAAS;AAClB,SAAO,UACL,8DAA8D,KAAK,2DACnEJ,4BACD;AACD,SAAO,UACL,8DAA8D,KAAK,kEACnEE,mCACD;GACD;AAGF,EAAC,2CAA2C,CAAC,SAAS,SAAS;AAC7D,SAAO,UACL,8DAA8D,KAAK,0DACnEF,4BACD;AACD,SAAO,UACL,8DAA8D,KAAK,iEACnEE,mCACD;GACD;AAEF,QAAO,OAAO,OAAO;;AAGvB,MAAa,wBAAsD,qBAAqB;;;AC3DxF,eAAsB,iBACpB,YACA,SACA,sBACiC;CACjC,MAAM,kCAAuE,IAAI,KAAK;AAEtF,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,YAAY,MAAM,YAAY,OAAO;EAE3C,MAAM,mBAAmB,IAAI,qBAAqB;GAChD,KAAK;GACL,oBAAoB,UAAU;GAC/B,CAAC;EAEF,MAAM,aAAa;GACjB,YAAY;GACZ,IAAI,UAAU;GACd,iBAAiB,CAAC,UAAU,GAAG;GAC/B,gBAAgB,CAAC,UAAU,GAAG;GAC/B;EAGD,MAAM,yBAAyB,MAAM,GAAG,iBAAoB;GAC9C;GACZ,OAAO;GACP;GACA,gBAAgB,wBAAwB;GACzC,CAAC;AAEF,kBAAgB,IAAI,QAAQ,uBAAuB;AAEnD,MAAI,uBAAuB,SACzB,QAAO,CAAC,MAAM,gBAAgB;;AAIlC,QAAO,CAAC,OAAO,gBAAgB;;;;ACVjC,SAAS,yBACP,qBACoC;CACpC,eAAe,QACb,QACA,SACA,QACA,WAAW,MACX;AACA,MAAI,SAEF,wBAAuB,MAAM,OAAO;EAGtC,MAAM,oBAAoB,oBAAoB,SAAS,SAAS;EAEhE,MAAM,MAAM,MAAM,YAAY,OAAO;EAGrC,MAAM,aAAa;GACjB,YAAY;IAAC;IAAY;IAAyB;IAAuB;GACzE,MAAM,CAAC,uBAAuB;GAC9B,QAAQ,IAAI;GACZ,GAAG,cAAc,OAAO;GACxB;GACD;EAED,MAAM,QAAQ,IAAI,qBAAqB,EAAE,KAAK,CAAC;AAE/C,SAAO,GAAG,MAAe;GACvB;GACA;GACA,gBAAgB;GACjB,CAAC;;AAGJ,QAAO;;AAGT,MAAa,8BACX,SACA,aACG;AACH,KAAI,SACF,yBAAwB,MAAM,QAAQ;CAGxC,MAAM,EAAE,oBAAoB,GAAG,gBAAgB;AAC/C,QAAO;EACL,YAAY;EACZ,GAAG,cAAc,YAAY;EAC7B,GAAI,qBAAqB,cAAc,oBAAoB,qBAAqB,GAAG,EAAE;EACtF;;AAGH,MAAa,oCACX,SACA,aACG;AACH,KAAI,SACF,+BAA8B,MAAM,QAAQ;AAG9C,QAAO;EACL,YAAY;EACZ,GAAG,cAAc,QAAQ;EAC1B;;AAGH,MAAa,kBACX,yBACE,2BACD;AAEH,MAAa,wBACX,yBAAkD,iCAAiC"}
1
+ {"version":3,"file":"index.mjs","names":["v1","idosCredentialsV1","ed25519Signature2020V1","idosCredentialSubjectV1","idosCredentialSubjectV2","idosCredentialSubjectFaceIdV1"],"sources":["../../assets/ed25519-signature-2020-v1.json","../../assets/idos-credential-subject-face-id-v1.json","../../assets/idos-credential-subject-v1.json","../../assets/idos-credential-subject-v2.json","../../assets/idos-credentials-v1.json","../../assets/v1.json","../../src/builder/loader.ts","../../src/builder/verifier.ts","../../src/builder/index.ts"],"sourcesContent":["","","","","","","import { type JsonLDDocumentLoaderInstance, JsonLdDocumentLoader } from \"jsonld-document-loader\";\n\nimport ed25519Signature2020V1 from \"../../assets/ed25519-signature-2020-v1.json\";\nimport idosCredentialSubjectFaceIdV1 from \"../../assets/idos-credential-subject-face-id-v1.json\";\nimport idosCredentialSubjectV1 from \"../../assets/idos-credential-subject-v1.json\";\nimport idosCredentialSubjectV2 from \"../../assets/idos-credential-subject-v2.json\";\nimport idosCredentialsV1 from \"../../assets/idos-credentials-v1.json\";\nimport v1 from \"../../assets/v1.json\";\n\nexport const CONTEXT_V1 = \"https://www.w3.org/2018/credentials/v1\";\nexport const CONTEXT_ED25519_SIGNATURE_2020_V1 = \"https://w3id.org/security/suites/ed25519-2020/v1\";\n\n// Check .github/workflow/pages.yml how those are deployed\nexport const CONTEXT_IDOS_CREDENTIAL_V1 =\n \"https://idos-network.github.io/idos-sdk-js/credentials/idos-credentials-v1.json\";\nexport const CONTEXT_IDOS_CREDENTIAL_V1_SUBJECT =\n \"https://idos-network.github.io/idos-sdk-js/credentials/idos-credential-subject-v1.json\";\nexport const CONTEXT_IDOS_CREDENTIAL_V2_SUBJECT =\n \"https://idos-network.github.io/idos-sdk-js/credentials/idos-credential-subject-v2.json\";\n\nexport const CONTEXT_IDOS_CREDENTIAL_V1_FACE_ID =\n \"https://idos-network.github.io/idos-sdk-js/credentials/idos-credential-subject-face-id-v1.json\";\n\n// Latest contexts for builder\nexport const CONTEXT_IDOS_CREDENTIAL: string = CONTEXT_IDOS_CREDENTIAL_V1;\nexport const CONTEXT_IDOS_CREDENTIAL_SUBJECT: string = CONTEXT_IDOS_CREDENTIAL_V2_SUBJECT;\nexport const CONTEXT_IDOS_SIGNATURE: string = CONTEXT_ED25519_SIGNATURE_2020_V1;\nexport const CONTEXT_IDOS_CREDENTIAL_FACE_ID: string = CONTEXT_IDOS_CREDENTIAL_V1_FACE_ID;\n\nexport function buildDocumentLoader(): JsonLDDocumentLoaderInstance {\n const loader = new JsonLdDocumentLoader();\n\n loader.addStatic(CONTEXT_V1, v1);\n loader.addStatic(CONTEXT_IDOS_CREDENTIAL_V1, idosCredentialsV1);\n loader.addStatic(CONTEXT_ED25519_SIGNATURE_2020_V1, ed25519Signature2020V1);\n\n // plus / basic + liveness credentials\n loader.addStatic(CONTEXT_IDOS_CREDENTIAL_V1_SUBJECT, idosCredentialSubjectV1);\n loader.addStatic(CONTEXT_IDOS_CREDENTIAL_V2_SUBJECT, idosCredentialSubjectV2);\n\n // PoP\n loader.addStatic(CONTEXT_IDOS_CREDENTIAL_V1_FACE_ID, idosCredentialSubjectFaceIdV1);\n\n // DEPRECATED: Those should be removed\n [\n \"1356fea6672d22deb07fedc5fa427478b2e1654c\",\n \"346f14468348e4f3dd00039c89ce9bb49d88777c\",\n \"1e8421744e037dfd7b0289cb39261c73ab6d643c\",\n \"0049d2e93f8913c42398372e2bb65d92bed38ac0\",\n ].forEach((hash) => {\n loader.addStatic(\n `https://raw.githubusercontent.com/idos-network/idos-sdk-js/${hash}/packages/%40credentials/assets/idos-credentials-v1.json`,\n idosCredentialsV1,\n );\n loader.addStatic(\n `https://raw.githubusercontent.com/idos-network/idos-sdk-js/${hash}/packages/%40credentials/assets/idos-credential-subject-v1.json`,\n idosCredentialSubjectV1,\n );\n });\n\n // DEPRECATED: Those should be removed\n [\"1bc3503f5302a7e42777076445d5b05fec8db429\"].forEach((hash) => {\n loader.addStatic(\n `https://raw.githubusercontent.com/idos-network/idos-sdk-js/${hash}/packages/issuer-sdk-js/assets/idos-credentials-v1.json`,\n idosCredentialsV1,\n );\n loader.addStatic(\n `https://raw.githubusercontent.com/idos-network/idos-sdk-js/${hash}/packages/issuer-sdk-js/assets/idos-credential-subject-v1.json`,\n idosCredentialSubjectV1,\n );\n });\n\n return loader.build();\n}\n\nexport const defaultDocumentLoader: JsonLDDocumentLoaderInstance = buildDocumentLoader();\n","import type { JsonLDDocumentLoaderInstance } from \"jsonld-document-loader\";\n\nimport { Ed25519Signature2020 } from \"@digitalbazaar/ed25519-signature-2020\";\nimport * as vc from \"@digitalbazaar/vc\";\n\nimport type {\n AvailableIssuerType,\n VerifiableCredential,\n VerifiableCredentialSubject,\n} from \"../types\";\n\nimport { issuerToKey } from \"../utils\";\nimport { defaultDocumentLoader } from \"./loader\";\n\nexport type VerifyCredentialResult = [boolean, Map<AvailableIssuerType, vc.VerifyCredentialResult>];\n\nexport async function verifyCredential<K = VerifiableCredentialSubject>(\n credential: VerifiableCredential<K>,\n issuers: AvailableIssuerType[],\n customDocumentLoader?: JsonLDDocumentLoaderInstance,\n): Promise<VerifyCredentialResult> {\n const resultsByIssuer: Map<AvailableIssuerType, vc.VerifyCredentialResult> = new Map();\n\n for (const issuer of issuers) {\n const publicKey = await issuerToKey(issuer);\n\n const vcVerifyingSuite = new Ed25519Signature2020({\n key: publicKey,\n verificationMethod: publicKey.id,\n });\n\n const controller = {\n \"@context\": \"https://w3id.org/security/v2\",\n id: publicKey.controller,\n assertionMethod: [publicKey.id],\n authentication: [publicKey.id],\n };\n\n // Verify the signature\n const verifyCredentialResult = await vc.verifyCredential<K>({\n credential: credential,\n suite: vcVerifyingSuite,\n controller,\n documentLoader: customDocumentLoader ?? defaultDocumentLoader,\n });\n\n resultsByIssuer.set(issuer, verifyCredentialResult);\n\n if (verifyCredentialResult.verified) {\n return [true, resultsByIssuer];\n }\n }\n\n return [false, resultsByIssuer];\n}\n","import { Ed25519Signature2020 } from \"@digitalbazaar/ed25519-signature-2020\";\nimport * as vc from \"@digitalbazaar/vc\";\n\nimport type { VerifyCredentialResult } from \"./verifier\";\n\nimport {\n type AvailableIssuerType,\n type CredentialFields,\n CredentialFieldsSchema,\n type CredentialSubject,\n type CredentialSubjectFaceId,\n CredentialSubjectFaceIdSchema,\n CredentialSubjectSchema,\n type VerifiableCredential,\n type VerifiableCredentialSubject,\n} from \"../types\";\nimport { convertValues, issuerToKey } from \"../utils\";\nimport {\n CONTEXT_IDOS_CREDENTIAL,\n CONTEXT_IDOS_CREDENTIAL_FACE_ID,\n CONTEXT_IDOS_CREDENTIAL_SUBJECT,\n CONTEXT_IDOS_SIGNATURE,\n CONTEXT_V1,\n defaultDocumentLoader,\n} from \"./loader\";\nimport { verifyCredential } from \"./verifier\";\n\nexport type Credential = VerifiableCredential<VerifiableCredentialSubject>;\nexport type FaceIdCredential = VerifiableCredential<CredentialSubjectFaceId>;\n\nexport type CredentialBuilder<TInput, TOutput = TInput> = (\n fields: CredentialFields,\n subject: TInput,\n issuer: AvailableIssuerType,\n validate?: boolean,\n) => Promise<VerifiableCredential<TOutput>>;\n\nexport type CredentialConverter<TInput> = (\n subject: TInput,\n validate: boolean,\n // oxlint-disable-next-line typescript/no-explicit-any -- any is needed here\n) => Record<string, any>;\n\nfunction genericCredentialBuilder<TInput, TOutput = TInput>(\n credentialConverter: CredentialConverter<TInput>,\n): CredentialBuilder<TInput, TOutput> {\n async function builder(\n fields: CredentialFields,\n subject: TInput,\n issuer: AvailableIssuerType,\n validate = true,\n ) {\n if (validate) {\n // This raises an z.ZodError exception if the fields are invalid\n CredentialFieldsSchema.parse(fields);\n }\n\n const credentialSubject = credentialConverter(subject, validate);\n\n const key = await issuerToKey(issuer);\n\n // Create credentials container\n const credential = {\n \"@context\": [CONTEXT_V1, CONTEXT_IDOS_CREDENTIAL, CONTEXT_IDOS_SIGNATURE],\n type: [\"VerifiableCredential\"],\n issuer: key.controller,\n ...convertValues(fields),\n credentialSubject,\n };\n\n const suite = new Ed25519Signature2020({ key });\n\n return vc.issue<TOutput>({\n credential,\n suite,\n documentLoader: defaultDocumentLoader,\n });\n }\n\n return builder;\n}\n\nexport const credentialSubjectConverter: CredentialConverter<CredentialSubject> = (\n subject,\n validate,\n) => {\n if (validate) {\n CredentialSubjectSchema.parse(subject);\n }\n\n const { residentialAddress, ...subjectData } = subject;\n return {\n \"@context\": CONTEXT_IDOS_CREDENTIAL_SUBJECT,\n ...convertValues(subjectData),\n ...(residentialAddress ? convertValues(residentialAddress, \"residentialAddress\") : {}),\n };\n};\n\nexport const credentialFaceIdSubjectConverter: CredentialConverter<CredentialSubjectFaceId> = (\n subject,\n validate,\n) => {\n if (validate) {\n CredentialSubjectFaceIdSchema.parse(subject);\n }\n\n return {\n \"@context\": CONTEXT_IDOS_CREDENTIAL_FACE_ID,\n ...convertValues(subject),\n };\n};\n\nexport const buildCredential: CredentialBuilder<CredentialSubject, VerifiableCredentialSubject> =\n genericCredentialBuilder<CredentialSubject, VerifiableCredentialSubject>(\n credentialSubjectConverter,\n );\n\nexport const buildFaceIdCredential: CredentialBuilder<CredentialSubjectFaceId> =\n genericCredentialBuilder<CredentialSubjectFaceId>(credentialFaceIdSubjectConverter);\n\nexport type { VerifyCredentialResult };\nexport { verifyCredential };\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/utils/index.ts"],"sourcesContent":["import { Ed25519VerificationKey2020 } from \"@digitalbazaar/ed25519-verification-key-2020\";\nimport { base64Decode, base64Encode, hexEncode, utf8Encode } from \"@idos-network/utils/codecs\";\nimport * as base85 from \"base85\";\nimport { every, get } from \"es-toolkit/compat\";\nimport invariant from \"tiny-invariant\";\nimport nacl from \"tweetnacl\";\n\nimport type {\n AvailableIssuerType,\n CredentialFields,\n CredentialResidentialAddress,\n CredentialSubject,\n CredentialSubjectFaceId,\n CustomIssuerType,\n InsertableIDOSCredential,\n} from \"../types\";\n\nexport function fileToBase85(file: Buffer): string {\n return base85.encode(file, \"ascii85\");\n}\n\nexport function base85ToFile(data: string): Buffer | false {\n return base85.decode(data);\n}\n\nexport function capitalizeFirstLetter(str: string): string {\n return str[0].toUpperCase() + str.slice(1);\n}\n\nfunction isIssuerKey(issuer: AvailableIssuerType): issuer is Ed25519VerificationKey2020 {\n return (\n typeof issuer === \"object\" &&\n issuer !== null &&\n \"type\" in issuer &&\n \"id\" in issuer &&\n \"controller\" in issuer\n );\n}\n\nfunction isCustomIssuerType(issuer: AvailableIssuerType): issuer is CustomIssuerType {\n return (\n typeof issuer === \"object\" &&\n issuer !== null &&\n \"issuer\" in issuer &&\n \"publicKeyMultibase\" in issuer\n );\n}\n\nexport async function issuerToKey(\n issuer: AvailableIssuerType,\n): Promise<Ed25519VerificationKey2020> {\n if (isIssuerKey(issuer)) {\n return issuer;\n }\n\n if (isCustomIssuerType(issuer)) {\n return await Ed25519VerificationKey2020.from({\n id: `${issuer.issuer}/keys/1`,\n controller: `${issuer.issuer}/issuers/1`,\n publicKeyMultibase: issuer.publicKeyMultibase,\n privateKeyMultibase: issuer.privateKeyMultibase,\n type: \"Ed25519VerificationKey2020\",\n });\n }\n\n return await Ed25519VerificationKey2020.from({ ...issuer, type: \"Ed25519VerificationKey2020\" });\n}\n\nexport function convertValues<\n K extends\n | CredentialFields\n | CredentialSubject\n | CredentialResidentialAddress\n | CredentialSubjectFaceId,\n>(fields: K, prefix?: string): Record<string, unknown> {\n const acc: Record<string, unknown> = {};\n\n for (const key in fields) {\n if (Object.hasOwn(fields, key)) {\n const value = fields[key];\n const name = prefix ? `${prefix}${capitalizeFirstLetter(key)}` : key;\n if (value instanceof Date) {\n acc[name] = value.toISOString();\n } else if (value instanceof Buffer) {\n // Convert file to base85\n acc[name] = fileToBase85(value);\n } else {\n acc[name] = value;\n }\n }\n }\n\n return acc;\n}\n\ntype BaseLevel = \"basic\" | \"plus\";\ntype Addon = \"liveness\" | \"email\" | \"phoneNumber\";\n\nexport function deriveLevel(credential: CredentialSubject): string {\n let level: BaseLevel = \"basic\";\n\n // Address is a sign for plus+\n const address = credential.residentialAddress;\n if (address?.proofFile && address?.city && address?.proofCategory) {\n level = \"plus\";\n }\n\n const addons: Addon[] = [];\n if (credential.selfieFile) {\n addons.push(\"liveness\");\n }\n\n if (credential.email) {\n addons.push(\"email\");\n }\n\n if (credential.phoneNumber) {\n addons.push(\"phoneNumber\");\n }\n\n return [level, ...addons].join(\"+\");\n}\n\nexport function parseLevel(level: string): {\n base: BaseLevel;\n addons: Addon[];\n} {\n const [base, ...addons] = level.split(\"+\") as [BaseLevel, ...Addon[]];\n return { base, addons };\n}\n\nexport function matchLevelOrHigher(\n level: BaseLevel,\n requiredAddons: Addon[],\n currentLevel: string,\n): boolean {\n const { base: currentBaseLevel, addons: currentAddons } = parseLevel(currentLevel);\n\n // TODO: Consider pop+ or uniqueness+ scenarios\n if (level === \"plus\" && currentBaseLevel !== \"plus\") {\n return false;\n }\n\n return requiredAddons.every((addon) => currentAddons.includes(addon));\n}\n\nexport function levelScore(level: string): number {\n const { base, addons } = parseLevel(level);\n let score = 0;\n\n if (base === \"plus\") {\n score += 100;\n }\n\n score += addons.length * 10;\n\n return score;\n}\n\nexport function pickHighestMatchingLevel(\n levels: string[],\n requiredLevel: BaseLevel,\n requiredAddons: Addon[],\n): string | null {\n return (\n levels\n .filter((currentLevel) => matchLevelOrHigher(requiredLevel, requiredAddons, currentLevel))\n .sort((a, b) => {\n const aAddons = levelScore(a);\n const bAddons = levelScore(b);\n return bAddons - aAddons; // descending\n })[0] ?? null\n );\n}\n\nexport function highestMatchingCredential<K extends { public_notes: string }>(\n credentials: K[],\n requiredLevel: BaseLevel,\n {\n addons: requiredAddons = [],\n publicNotesConstraint = {},\n }: {\n addons?: Addon[];\n publicNotesConstraint?: Record<string, number | string>;\n },\n): K | undefined {\n const matchingCredentials = credentials\n .map((credential) => {\n const publicNotes = JSON.parse(credential.public_notes || \"{}\");\n return {\n credential,\n publicNotes,\n };\n })\n .filter(({ publicNotes }) => {\n const level = publicNotes.level;\n\n if (!level) {\n return false;\n }\n\n if (!matchLevelOrHigher(requiredLevel, requiredAddons, level)) {\n return false;\n }\n\n for (const key in publicNotesConstraint) {\n if (publicNotes[key] !== publicNotesConstraint[key]) {\n return false;\n }\n }\n\n return true;\n })\n .sort((a, b) => {\n const aLevel = a.publicNotes.level;\n const bLevel = b.publicNotes.level;\n return levelScore(bLevel) - levelScore(aLevel); // descending\n })\n .map(({ credential }) => credential);\n\n return matchingCredentials[0];\n}\n\nexport function recordFilter(\n rec: Record<string, unknown>,\n pick: Record<string, unknown[]>,\n omit: Record<string, unknown[]>,\n): boolean {\n const matchCriteria = (content: Record<string, unknown>, criteria: Record<string, unknown[]>) =>\n every(Object.entries(criteria), ([path, targetSet]) => targetSet.includes(get(content, path)));\n\n if (Object.keys(pick).length > 0 && !matchCriteria(rec, pick)) {\n // Fast fail on pick criteria\n return false;\n }\n\n if (Object.keys(omit).length > 0 && matchCriteria(rec, omit)) {\n // Fast fail on omit criteria\n return false;\n }\n\n return true;\n}\n\nexport function buildInsertableIDOSCredential(\n userId: string,\n publicNotes: string,\n content: string,\n encryptorPublicKey: string,\n): InsertableIDOSCredential {\n invariant(encryptorPublicKey, \"Missing `encryptorPublicKey`\");\n\n const ephemeralAuthenticationKeyPair = nacl.sign.keyPair();\n\n const publicNotesSignature = nacl.sign.detached(\n utf8Encode(publicNotes),\n ephemeralAuthenticationKeyPair.secretKey,\n );\n\n return {\n user_id: userId,\n content,\n\n public_notes: publicNotes,\n public_notes_signature: base64Encode(publicNotesSignature),\n\n broader_signature: base64Encode(\n nacl.sign.detached(\n Uint8Array.from([...publicNotesSignature, ...base64Decode(content)]),\n ephemeralAuthenticationKeyPair.secretKey,\n ),\n ),\n\n issuer_auth_public_key: hexEncode(ephemeralAuthenticationKeyPair.publicKey, true),\n encryptor_public_key: encryptorPublicKey,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/utils/index.ts"],"sourcesContent":["import { Ed25519VerificationKey2020 } from \"@digitalbazaar/ed25519-verification-key-2020\";\nimport { base64Decode, base64Encode, hexEncode, utf8Encode } from \"@idos-network/utils/codecs\";\nimport * as base85 from \"base85\";\nimport { every, get } from \"es-toolkit/compat\";\nimport invariant from \"tiny-invariant\";\nimport nacl from \"tweetnacl\";\n\nimport type {\n AvailableIssuerType,\n CredentialFields,\n CredentialResidentialAddress,\n CredentialSubject,\n CredentialSubjectFaceId,\n CustomIssuerType,\n InsertableIDOSCredential,\n} from \"../types\";\n\nexport function fileToBase85(file: Buffer): string {\n return base85.encode(file, \"ascii85\");\n}\n\nexport function base85ToFile(data: string): Buffer | false {\n return base85.decode(data);\n}\n\nexport function capitalizeFirstLetter(str: string): string {\n return str[0].toUpperCase() + str.slice(1);\n}\n\nfunction isIssuerKey(issuer: AvailableIssuerType): issuer is Ed25519VerificationKey2020 {\n return (\n typeof issuer === \"object\" &&\n issuer !== null &&\n \"type\" in issuer &&\n \"id\" in issuer &&\n \"controller\" in issuer\n );\n}\n\nfunction isCustomIssuerType(issuer: AvailableIssuerType): issuer is CustomIssuerType {\n return (\n typeof issuer === \"object\" &&\n issuer !== null &&\n \"issuer\" in issuer &&\n \"publicKeyMultibase\" in issuer\n );\n}\n\nexport async function issuerToKey(\n issuer: AvailableIssuerType,\n): Promise<Ed25519VerificationKey2020> {\n if (isIssuerKey(issuer)) {\n return issuer;\n }\n\n if (isCustomIssuerType(issuer)) {\n return await Ed25519VerificationKey2020.from({\n id: `${issuer.issuer}/keys/1`,\n controller: `${issuer.issuer}/issuers/1`,\n publicKeyMultibase: issuer.publicKeyMultibase,\n privateKeyMultibase: issuer.privateKeyMultibase,\n type: \"Ed25519VerificationKey2020\",\n });\n }\n\n return await Ed25519VerificationKey2020.from({ ...issuer, type: \"Ed25519VerificationKey2020\" });\n}\n\nexport function convertValues<\n K extends\n | CredentialFields\n | CredentialSubject\n | CredentialResidentialAddress\n | CredentialSubjectFaceId,\n>(fields: K, prefix?: string): Record<string, unknown> {\n const acc: Record<string, unknown> = {};\n\n for (const key in fields) {\n if (Object.hasOwn(fields, key)) {\n const value = fields[key];\n const name = prefix ? `${prefix}${capitalizeFirstLetter(key)}` : key;\n if (value instanceof Date) {\n acc[name] = value.toISOString();\n } else if (value instanceof Buffer) {\n // Convert file to base85\n acc[name] = fileToBase85(value);\n } else {\n acc[name] = value;\n }\n }\n }\n\n return acc;\n}\n\ntype BaseLevel = \"basic\" | \"plus\";\ntype Addon = \"liveness\" | \"email\" | \"phoneNumber\";\n\nexport function deriveLevel(credential: CredentialSubject): string {\n let level: BaseLevel = \"basic\";\n\n // Address is a sign for plus+\n const address = credential.residentialAddress;\n if (address?.proofFile && address?.city && address?.proofCategory) {\n level = \"plus\";\n }\n\n const addons: Addon[] = [];\n if (credential.selfieFile) {\n addons.push(\"liveness\");\n }\n\n if (credential.email) {\n addons.push(\"email\");\n }\n\n if (credential.phoneNumber) {\n addons.push(\"phoneNumber\");\n }\n\n return [level, ...addons].join(\"+\");\n}\n\nexport function parseLevel(level: string): {\n base: BaseLevel;\n addons: Addon[];\n} {\n const [base, ...addons] = level.split(\"+\") as [BaseLevel, ...Addon[]];\n return { base, addons };\n}\n\nexport function matchLevelOrHigher(\n level: BaseLevel,\n requiredAddons: Addon[],\n currentLevel: string,\n): boolean {\n const { base: currentBaseLevel, addons: currentAddons } = parseLevel(currentLevel);\n\n // TODO: Consider pop+ or uniqueness+ scenarios\n if (level === \"plus\" && currentBaseLevel !== \"plus\") {\n return false;\n }\n\n return requiredAddons.every((addon) => currentAddons.includes(addon));\n}\n\nexport function levelScore(level: string): number {\n const { base, addons } = parseLevel(level);\n let score = 0;\n\n if (base === \"plus\") {\n score += 100;\n }\n\n score += addons.length * 10;\n\n return score;\n}\n\nexport function pickHighestMatchingLevel(\n levels: string[],\n requiredLevel: BaseLevel,\n requiredAddons: Addon[],\n): string | null {\n return (\n levels\n .filter((currentLevel) => matchLevelOrHigher(requiredLevel, requiredAddons, currentLevel))\n .sort((a, b) => {\n const aAddons = levelScore(a);\n const bAddons = levelScore(b);\n return bAddons - aAddons; // descending\n })[0] ?? null\n );\n}\n\nexport function highestMatchingCredential<K extends { public_notes: string }>(\n credentials: K[],\n requiredLevel: BaseLevel,\n {\n addons: requiredAddons = [],\n publicNotesConstraint = {},\n }: {\n addons?: Addon[];\n publicNotesConstraint?: Record<string, number | string>;\n },\n): K | undefined {\n const matchingCredentials = credentials\n .map((credential) => {\n const publicNotes = JSON.parse(credential.public_notes || \"{}\");\n return {\n credential,\n publicNotes,\n };\n })\n .filter(({ publicNotes }) => {\n const level = publicNotes.level;\n\n if (!level) {\n return false;\n }\n\n if (!matchLevelOrHigher(requiredLevel, requiredAddons, level)) {\n return false;\n }\n\n for (const key in publicNotesConstraint) {\n if (publicNotes[key] !== publicNotesConstraint[key]) {\n return false;\n }\n }\n\n return true;\n })\n .sort((a, b) => {\n const aLevel = a.publicNotes.level;\n const bLevel = b.publicNotes.level;\n return levelScore(bLevel) - levelScore(aLevel); // descending\n })\n .map(({ credential }) => credential);\n\n return matchingCredentials[0];\n}\n\nexport function recordFilter(\n rec: Record<string, unknown>,\n pick: Record<string, unknown[]>,\n omit: Record<string, unknown[]>,\n): boolean {\n const matchCriteria = (content: Record<string, unknown>, criteria: Record<string, unknown[]>) =>\n every(Object.entries(criteria), ([path, targetSet]) => targetSet.includes(get(content, path)));\n\n if (Object.keys(pick).length > 0 && !matchCriteria(rec, pick)) {\n // Fast fail on pick criteria\n return false;\n }\n\n if (Object.keys(omit).length > 0 && matchCriteria(rec, omit)) {\n // Fast fail on omit criteria\n return false;\n }\n\n return true;\n}\n\nexport function buildInsertableIDOSCredential(\n userId: string,\n publicNotes: string,\n content: string,\n encryptorPublicKey: string,\n): InsertableIDOSCredential {\n invariant(encryptorPublicKey, \"Missing `encryptorPublicKey`\");\n\n const ephemeralAuthenticationKeyPair = nacl.sign.keyPair();\n\n const publicNotesSignature = nacl.sign.detached(\n utf8Encode(publicNotes),\n ephemeralAuthenticationKeyPair.secretKey,\n );\n\n return {\n user_id: userId,\n content,\n\n public_notes: publicNotes,\n public_notes_signature: base64Encode(publicNotesSignature),\n\n broader_signature: base64Encode(\n nacl.sign.detached(\n Uint8Array.from([...publicNotesSignature, ...base64Decode(content)]),\n ephemeralAuthenticationKeyPair.secretKey,\n ),\n ),\n\n issuer_auth_public_key: hexEncode(ephemeralAuthenticationKeyPair.publicKey, true),\n encryptor_public_key: encryptorPublicKey,\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idos-network/credentials",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "idOS Credentials JavaScript SDK",
5
5
  "homepage": "https://idos.network",
6
6
  "license": "MIT",
@@ -46,14 +46,14 @@
46
46
  "jsonld-document-loader": "^2.3.0",
47
47
  "tiny-invariant": "^1.3.3",
48
48
  "tweetnacl": "^1.0.3",
49
- "zod": "4.3.6",
50
- "@idos-network/utils": "1.1.0"
49
+ "zod": "4.4.3",
50
+ "@idos-network/utils": "1.2.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@types/node": "25.5.2",
54
- "tsdown": "^0.21.0",
53
+ "@types/node": "25.6.2",
54
+ "tsdown": "^0.22.0",
55
55
  "typescript": "^5.8.3",
56
- "vitest": "4.1.4",
56
+ "vitest": "4.1.5",
57
57
  "web-streams-polyfill": "3.3.3"
58
58
  },
59
59
  "peerDependencies": {