diff --git a/packages/traceability-tests/examples/v2.json b/packages/traceability-tests/examples/v2.json index d745f4ffe..45d4d6552 100644 --- a/packages/traceability-tests/examples/v2.json +++ b/packages/traceability-tests/examples/v2.json @@ -9,7 +9,7 @@ }, "examples": [ { - "issued": "eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKRlF5SXNJbU55ZGlJNklsQXRNemcwSWl3aVlXeG5Jam9pUlZNek9EUWlMQ0o0SWpvaU9XcHpSbEpXVm1weFNsazFTa2w1U25JMU9HZEZRblZ5YW10emFreHdUVUp2V1ZaRFpUWmxNRUo2VXpoQlV6Tk1RWGx5TjNoTGVGTm5VRTlwVUdOSFRTSXNJbmtpT2lKaWRIUk1YMlpWYTNSa2NpMU1hSGhOWTNwSmFtbDBaRnBXY0c5cGFYcHBVR2xuT0cxbk9ERmlkR1ZVVW0welFtcGFMV3c0ZFU1cWRrcExZV3RuVWpOQ0luMCIsImtpZCI6IiMwIiwiYWxnIjoiRVMzODQiLCJjdHkiOiJ2YytsZCtqc29uIn0.eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vd3d3LnczLm9yZy9ucy9hY3Rpdml0eXN0cmVhbXMiXSwiaWQiOiJ1cm46dXVpZDoxODViNzM5Yy0yZDBkLTQxYzAtYmE5NC1kNDdjMTk4ODUwYTAiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiQWN0aXZpdHlQdWJBY3RvckNhcmQiXSwiY3JlZGVudGlhbFNjaGVtYSI6eyJpZCI6Imh0dHBzOi8vdzNjLWNjZy5naXRodWIuaW8vdHJhY2VhYmlsaXR5LXZvY2FiL29wZW5hcGkvY29tcG9uZW50cy9zY2hlbWFzL2NyZWRlbnRpYWxzL0FjdGl2aXR5UHViQWN0b3JDYXJkLnltbCIsInR5cGUiOiJKc29uU2NoZW1hIn0sImlzc3VhbmNlRGF0ZSI6IjIwMjMtMDUtMjFUMTU6Mzk6NTcuOTQ0WiIsImlzc3VlciI6eyJpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpGUXlJc0ltTnlkaUk2SWxBdE16ZzBJaXdpWVd4bklqb2lSVk16T0RRaUxDSjRJam9pT1dwelJsSldWbXB4U2xrMVNrbDVTbkkxT0dkRlFuVnlhbXR6YWt4d1RVSnZXVlpEWlRabE1FSjZVemhCVXpOTVFYbHlOM2hMZUZOblVFOXBVR05IVFNJc0lua2lPaUppZEhSTVgyWlZhM1JrY2kxTWFIaE5ZM3BKYW1sMFpGcFdjRzlwYVhwcFVHbG5PRzFuT0RGaWRHVlVVbTB6UW1wYUxXdzRkVTVxZGtwTFlXdG5Vak5DSW4wIn0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InR5cGUiOlsiUGVyc29uIl0sImlkIjoiaHR0cHM6Ly9rZW56b2lzaGlpLmV4YW1wbGUuY29tLyIsImZvbGxvd2luZyI6Imh0dHBzOi8va2Vuem9pc2hpaS5leGFtcGxlLmNvbS9mb2xsb3dpbmcuanNvbiIsImZvbGxvd2VycyI6Imh0dHBzOi8va2Vuem9pc2hpaS5leGFtcGxlLmNvbS9mb2xsb3dlcnMuanNvbiIsImxpa2VkIjoiaHR0cHM6Ly9rZW56b2lzaGlpLmV4YW1wbGUuY29tL2xpa2VkLmpzb24iLCJpbmJveCI6Imh0dHBzOi8va2Vuem9pc2hpaS5leGFtcGxlLmNvbS9pbmJveC5qc29uIiwib3V0Ym94IjoiaHR0cHM6Ly9rZW56b2lzaGlpLmV4YW1wbGUuY29tL2ZlZWQuanNvbiIsInByZWZlcnJlZFVzZXJuYW1lIjoia2Vuem9pc2hpaSIsIm5hbWUiOiLnn7PkupXlgaXolLUiLCJzdW1tYXJ5Ijoi44GT44Gu5pa544Gv44Gf44Gg44Gu5L6L44Gn44GZIiwiaWNvbiI6WyJodHRwczovL2tlbnpvaXNoaWkuZXhhbXBsZS5jb20vaW1hZ2UvMTY1OTg3YWtscmU0Il19fQ.6JVcaWvSZZL0EYu_25D4vOBi5K1kvAEt5xRYS4uefNwqkmK-fMUzxJGAW_YZ8KiUNbas9IpHDS60mjCKE_NirBwHYPbe7njqpsHycB3jtforszSB0d9HE5Q-u-SQ92gK", + "issued": "eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKRlF5SXNJbU55ZGlJNklsQXRNemcwSWl3aVlXeG5Jam9pUlZNek9EUWlMQ0o0SWpvaU9XcHpSbEpXVm1weFNsazFTa2w1U25JMU9HZEZRblZ5YW10emFreHdUVUp2V1ZaRFpUWmxNRUo2VXpoQlV6Tk1RWGx5TjNoTGVGTm5VRTlwVUdOSFRTSXNJbmtpT2lKaWRIUk1YMlpWYTNSa2NpMU1hSGhOWTNwSmFtbDBaRnBXY0c5cGFYcHBVR2xuT0cxbk9ERmlkR1ZVVW0welFtcGFMV3c0ZFU1cWRrcExZV3RuVWpOQ0luMCIsImtpZCI6IiMwIiwiYWxnIjoiRVMzODQiLCJjdHkiOiJ2YytsZCtqc29uIn0.eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vd3d3LnczLm9yZy9ucy9hY3Rpdml0eXN0cmVhbXMiXSwiaWQiOiJ1cm46dXVpZDphMGU4OTE1ZS1hMWE5LTQ0YzctYmMzMC00YjI4NTdjNTA5MjkiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiQWN0aXZpdHlQdWJBY3RvckNhcmQiXSwiY3JlZGVudGlhbFNjaGVtYSI6eyJpZCI6Imh0dHBzOi8vdzNjLWNjZy5naXRodWIuaW8vdHJhY2VhYmlsaXR5LXZvY2FiL29wZW5hcGkvY29tcG9uZW50cy9zY2hlbWFzL2NyZWRlbnRpYWxzL0FjdGl2aXR5UHViQWN0b3JDYXJkLnltbCIsInR5cGUiOiJKc29uU2NoZW1hIn0sImlzc3VhbmNlRGF0ZSI6IjIwMjMtMDUtMjFUMTU6Mzk6NTcuOTQ0WiIsImlzc3VlciI6eyJpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpGUXlJc0ltTnlkaUk2SWxBdE16ZzBJaXdpWVd4bklqb2lSVk16T0RRaUxDSjRJam9pT1dwelJsSldWbXB4U2xrMVNrbDVTbkkxT0dkRlFuVnlhbXR6YWt4d1RVSnZXVlpEWlRabE1FSjZVemhCVXpOTVFYbHlOM2hMZUZOblVFOXBVR05IVFNJc0lua2lPaUppZEhSTVgyWlZhM1JrY2kxTWFIaE5ZM3BKYW1sMFpGcFdjRzlwYVhwcFVHbG5PRzFuT0RGaWRHVlVVbTB6UW1wYUxXdzRkVTVxZGtwTFlXdG5Vak5DSW4wIn0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InR5cGUiOlsiUGVyc29uIl0sImlkIjoiaHR0cHM6Ly9rZW56b2lzaGlpLmV4YW1wbGUuY29tLyIsImZvbGxvd2luZyI6Imh0dHBzOi8va2Vuem9pc2hpaS5leGFtcGxlLmNvbS9mb2xsb3dpbmcuanNvbiIsImZvbGxvd2VycyI6Imh0dHBzOi8va2Vuem9pc2hpaS5leGFtcGxlLmNvbS9mb2xsb3dlcnMuanNvbiIsImxpa2VkIjoiaHR0cHM6Ly9rZW56b2lzaGlpLmV4YW1wbGUuY29tL2xpa2VkLmpzb24iLCJpbmJveCI6Imh0dHBzOi8va2Vuem9pc2hpaS5leGFtcGxlLmNvbS9pbmJveC5qc29uIiwib3V0Ym94IjoiaHR0cHM6Ly9rZW56b2lzaGlpLmV4YW1wbGUuY29tL2ZlZWQuanNvbiIsInByZWZlcnJlZFVzZXJuYW1lIjoia2Vuem9pc2hpaSIsIm5hbWUiOiLnn7PkupXlgaXolLUiLCJzdW1tYXJ5Ijoi44GT44Gu5pa544Gv44Gf44Gg44Gu5L6L44Gn44GZIiwiaWNvbiI6WyJodHRwczovL2tlbnpvaXNoaWkuZXhhbXBsZS5jb20vaW1hZ2UvMTY1OTg3YWtscmU0Il19fQ.MzFHvlUA0NDuTqWy8usYWkw74XmKFMRJMtlp6ukYcpwTVnCjWjdXMIVUGGwAxDQgpbzl-UvV1_asvBXp4ZxZqL-WjEayz19qSx23-L3Dz--aPMk_B0MGhXK59gig_cqi", "verified": { "protectedHeader": { "iss": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMzg0IiwiYWxnIjoiRVMzODQiLCJ4IjoiOWpzRlJWVmpxSlk1Skl5SnI1OGdFQnVyamtzakxwTUJvWVZDZTZlMEJ6UzhBUzNMQXlyN3hLeFNnUE9pUGNHTSIsInkiOiJidHRMX2ZVa3Rkci1MaHhNY3pJaml0ZFpWcG9paXppUGlnOG1nODFidGVUUm0zQmpaLWw4dU5qdkpLYWtnUjNCIn0", @@ -22,7 +22,7 @@ "https://www.w3.org/2018/credentials/v1", "https://www.w3.org/ns/activitystreams" ], - "id": "urn:uuid:185b739c-2d0d-41c0-ba94-d47c198850a0", + "id": "urn:uuid:a0e8915e-a1a9-44c7-bc30-4b2857c50929", "type": [ "VerifiableCredential", "ActivityPubActorCard" diff --git a/packages/traceability-tests/tests/regression.test.js b/packages/traceability-tests/tests/regression.test.js new file mode 100644 index 000000000..78cc1db1c --- /dev/null +++ b/packages/traceability-tests/tests/regression.test.js @@ -0,0 +1,56 @@ +const fs = require('fs'); +const { v4: uuidv4 } = require('uuid'); +const { schemas } = require('traceability-schemas'); +const { issuer, verifier } = require('../utils'); + +const upgradeClaimset = (example) => { + const claimset = JSON.parse(JSON.stringify(example)); + // claimset['@context'][0] = 'https://www.w3.org/ns/credentials/v2'; + claimset.id = `urn:uuid:${uuidv4()}`; + if (claimset.issuer.id) { + claimset.issuer.id = issuer.id; + } else { + claimset.issuer = issuer.id; + } + // TODO: https://github.com/w3c-ccg/traceability-vocab/issues/787 + // claimset.validFrom = claimset.issuanceDate; + // delete claimset.issuanceDate; + delete claimset.proof; + return claimset; +}; + +describe('regression tests', () => { + const fixture = { privateKeyJwk: issuer.privateKeyJwk, examples: [] }; + schemas.forEach(async (schema) => { + // only apply the upgrade to tagged schemas + if (schema.tags && schema.tags.includes('VCDMv2')) { + it(schema.title, async () => { + const claimset = upgradeClaimset(JSON.parse(schema.example), { issuer }); + // issuer validates against a schema before signing... + const issuerValidation = await issuer + .validator(claimset.credentialSchema.id) + .validate(claimset); + expect(issuerValidation).toBe(true); + // credential issuance + const vc = await issuer.signer.sign(claimset); + // credential verification + const { payload, protectedHeader } = await verifier.verify(vc); + // verifier validates against a schema after verifying... + const verifierValidation = await verifier + .validator(claimset.credentialSchema.id) + .validate(claimset); + expect(verifierValidation).toBe(true); + expect(protectedHeader.iss).toBe(issuer.id); + expect(protectedHeader.cty).toBe('vc+ld+json'); + fixture.examples.push({ issued: vc, verified: { protectedHeader, payload } }); + }); + } + }); + + afterAll(() => { + // all these examples will be broken because of the unresolved issue here: + // https://github.com/w3c-ccg/traceability-vocab/issues/786 + // except for the schemas that are tagged VCDMv2 + fs.writeFileSync('./examples/v2.json', JSON.stringify(fixture, null, 2)); + }); +}); diff --git a/packages/traceability-tests/tests/sanity/context-validation.test.js b/packages/traceability-tests/tests/sanity/context-validation.test.js new file mode 100644 index 000000000..bc7143332 --- /dev/null +++ b/packages/traceability-tests/tests/sanity/context-validation.test.js @@ -0,0 +1,102 @@ +const Ajv = require('ajv'); +const yaml = require('js-yaml'); + +const ajv = new Ajv({ + strict: false, + // In order to get strict mode back on, and warnings off, + // We may need these if statements in the future + // https://github.com/ajv-validator/ajv/issues/1417#issuecomment-1431435887 +}); + +describe('v2', () => { + const validate = ajv.compile( + yaml.load(` +title: Example Title +description: Example Description +type: object +properties: + '@context': + type: array + readOnly: true + default: + - https://www.w3.org/2018/credentials/v2 + - https://w3id.org/traceability/v1 + items: + - type: string + const: https://www.w3.org/2018/credentials/v2 + additionalItems: + type: string + enum: + - https://w3id.org/traceability/v1 + - https://w3id.org/vc/status-list/2021/v1 + - https://ref.gs1.org/gs1/vc/licence-context/ + - https://ref.gs1.org/gs1/vc/trade-item-context/ + - https://ref.gs1.org/gs1/vc/declaration-context/ +`) + ); + + it('minimal context', async () => { + const instance1 = { + '@context': ['https://www.w3.org/2018/credentials/v2'], + }; + const validation1 = validate(instance1); + expect(validation1).toBe(true); + }); + + describe('extended contexts', () => { + describe('valid', () => { + it('https://w3id.org/traceability/v1', async () => { + const instance1 = { + '@context': [ + 'https://www.w3.org/2018/credentials/v2', + 'https://w3id.org/traceability/v1', + ], + }; + const validation1 = validate(instance1); + expect(validation1).toBe(true); + }); + it('https://w3id.org/vc/status-list/2021/v1', async () => { + const instance1 = { + '@context': [ + 'https://www.w3.org/2018/credentials/v2', + 'https://w3id.org/vc/status-list/2021/v1', + ], + }; + const validation1 = validate(instance1); + expect(validation1).toBe(true); + }); + it('https://ref.gs1.org/gs1/vc/licence-context/', async () => { + const instance1 = { + '@context': [ + 'https://www.w3.org/2018/credentials/v2', + 'https://ref.gs1.org/gs1/vc/licence-context/', + ], + }; + const validation1 = validate(instance1); + expect(validation1).toBe(true); + }); + }); + describe('invalid', () => { + it('unknown url', async () => { + const instance1 = { + '@context': [ + 'https://www.w3.org/2018/credentials/v2', + 'https://extension.example/v42', // not in allow list + ], + }; + const validation1 = validate(instance1); + expect(validation1).toBe(false); + }); + it('unknown object', async () => { + const instance2 = { + '@context': [ + 'https://www.w3.org/2018/credentials/v2', + { '@vocab': 'https://extension.example/#' }, // not in allow list + ], + }; + const validation2 = validate(instance2); + expect(validation2).toBe(false); + }); + }); + }); +}); diff --git a/packages/traceability-tests/tests/sanity/jsonld.test.js b/packages/traceability-tests/tests/sanity/jsonld.test.js new file mode 100644 index 000000000..c1ebc1ef1 --- /dev/null +++ b/packages/traceability-tests/tests/sanity/jsonld.test.js @@ -0,0 +1,33 @@ +// https://github.com/digitalbazaar/jsonld.js/issues/516#issuecomment-1485959372 +const { kyPromise } = require('@digitalbazaar/http-client'); +const { canonize } = require('jsonld'); + +beforeAll(async () => { + // https://github.com/digitalbazaar/jsonld.js/issues/516#issuecomment-1485959372 + await kyPromise; +}); + +it('canonize', async () => { + expect( + await canonize( + { + '@context': { '@vocab': 'https://www.w3.org/2018/credentials#' }, + type: ['VerifiableCredential'], + issuer: 'https://example.org/issuers/565049', + issuanceDate: '2010-01-01T19:23:24Z', + credentialSubject: { + id: 'did:example:ebfeb1f712ebc6f1c276e12ec21', + }, + }, + { + format: 'application/n-quads', + } + ) + ) + .toBe(`_:c14n0 "did:example:ebfeb1f712ebc6f1c276e12ec21" . +_:c14n1 _:c14n0 . +_:c14n1 "2010-01-01T19:23:24Z" . +_:c14n1 "https://example.org/issuers/565049" . +_:c14n1 "VerifiableCredential" . +`); +}); diff --git a/packages/traceability-tests/tests/sanity/traceable-presentation.test.js b/packages/traceability-tests/tests/sanity/traceable-presentation.test.js new file mode 100644 index 000000000..0b8cc9660 --- /dev/null +++ b/packages/traceability-tests/tests/sanity/traceable-presentation.test.js @@ -0,0 +1,49 @@ +// https://github.com/digitalbazaar/jsonld.js/issues/516#issuecomment-1485959372 +const { kyPromise } = require('@digitalbazaar/http-client'); +const { canonize } = require('jsonld'); + +beforeAll(async () => { + // https://github.com/digitalbazaar/jsonld.js/issues/516#issuecomment-1485959372 + await kyPromise; +}); + +it('canonize', async () => { + const data = await canonize( + { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + { + '@vocab': 'https://w3id.org/traceability#', + TraceablePresentation: { + '@id': 'https://w3id.org/traceability#TraceablePresentation', + '@context': { + '@vocab': 'https://w3id.org/traceability#' + }, + }, + }, + ], + id: 'urn:uuid:00000000-8596-4c3a-a978-8fcaba3903c5', + type: ['VerifiablePresentation', 'TraceablePresentation'], + workflow: { + definition: ['urn:uuid:11111111-cc91-4bb3-91f1-5466a0be084e'], + instance: ['urn:uuid:22222222-b0b1-41b8-89b0-331ni58b7ee0'], + }, + holder: { + id: 'did:web:sender.example', + type: 'Organization', + }, + }, + { + format: 'application/n-quads', + } + ) + expect(data) + .toBe(` . + . + . + _:c14n0 . + . +_:c14n0 "urn:uuid:11111111-cc91-4bb3-91f1-5466a0be084e" . +_:c14n0 "urn:uuid:22222222-b0b1-41b8-89b0-331ni58b7ee0" . +`); +}); diff --git a/packages/traceability-tests/utils/index.js b/packages/traceability-tests/utils/index.js new file mode 100644 index 000000000..2c7a657ec --- /dev/null +++ b/packages/traceability-tests/utils/index.js @@ -0,0 +1,6 @@ +const issuer = require('./issuer'); +const verifier = require('./verifier'); + +const api = { issuer, verifier }; + +module.exports = api; diff --git a/packages/traceability-tests/utils/issuer.js b/packages/traceability-tests/utils/issuer.js new file mode 100644 index 000000000..e6f0f3651 --- /dev/null +++ b/packages/traceability-tests/utils/issuer.js @@ -0,0 +1,36 @@ +const jose = require('jose'); +const privateKeyJwk = require('./privateKeyJwk'); +const validator = require('./validator'); + +const { d, ...publicKeyJwk } = privateKeyJwk; + +const id = `did:jwk:${jose.base64url.encode(JSON.stringify(publicKeyJwk))}`; + +const signer = (privateKey) => ({ + sign: async ( + claimset, + header = { + iss: id, + kid: '#0', + alg: publicKeyJwk.alg, + cty: 'vc+ld+json', + } + ) => { + const jwt = await new jose.CompactSign(Buffer.from(JSON.stringify(claimset))) + .setProtectedHeader(header) + .sign(await jose.importJWK(privateKey)); + return jwt; + }, +}); + +const api = { + id, + alg: publicKeyJwk.alg, + signer: signer(privateKeyJwk), + // exported here for test vectors, normally not exposed. + privateKeyJwk, + // normally this would be applied before sign above, exported here for test readability. + ...validator +}; + +module.exports = api; diff --git a/packages/traceability-tests/utils/privateKeyJwk.js b/packages/traceability-tests/utils/privateKeyJwk.js new file mode 100644 index 000000000..7c16a64d0 --- /dev/null +++ b/packages/traceability-tests/utils/privateKeyJwk.js @@ -0,0 +1,10 @@ +const privateKeyJwk = { + kty: 'EC', + crv: 'P-384', + alg: 'ES384', + d: 'yJ8crrNOPXauJl2uHneamQp33w_iWW3pso3MGcpPvPioe5P5qnmywOBBkRMwhQFM', + x: '9jsFRVVjqJY5JIyJr58gEBurjksjLpMBoYVCe6e0BzS8AS3LAyr7xKxSgPOiPcGM', + y: 'bttL_fUktdr-LhxMczIjitdZVpoiiziPig8mg81bteTRm3BjZ-l8uNjvJKakgR3B', +}; + +module.exports = privateKeyJwk; diff --git a/packages/traceability-tests/utils/validator.js b/packages/traceability-tests/utils/validator.js new file mode 100644 index 000000000..619167d31 --- /dev/null +++ b/packages/traceability-tests/utils/validator.js @@ -0,0 +1,56 @@ +const Ajv = require('ajv'); +const addFormats = require('ajv-formats').default; +const jose = require('jose'); +const axios = require('axios'); +const yaml = require('js-yaml'); +const fs = require('fs'); +const path = require('path'); + +const dereferenceSchema = async (id) => { + // For example: https://w3c-ccg.github.io/traceability-vocab/openapi/components/schemas/credentials/ActivityPubActorCard.yml + if (id.endsWith('/ActivityPubActorCard.yml')) { + const schemaYml = fs + .readFileSync( + path.resolve( + __dirname, + '../../../docs/openapi/components/schemas/credentials/ActivityPubActorCard.yml' + ) + ) + .toString(); + const schemaJson = JSON.parse(JSON.stringify(yaml.load(schemaYml))); + return schemaJson; + } + // handle relative refs... (they don't start with https) + const base = 'https://w3id.org/traceability/openapi/components/schemas/'; + if (!id.startsWith('https')) { + const { data } = await axios.get(`${base}${id}`); + const loaded = yaml.load(data); + const json = JSON.parse(JSON.stringify(loaded)); + return json; + } + throw new Error(`Unresolvable schema: ${id}`); +}; + +const ajv = new Ajv({ + strict: false, // see https://github.com/w3c-ccg/traceability-vocab/issues/786 + loadSchema: dereferenceSchema, +}); +addFormats(ajv); + +const validator = (id) => ({ + validate: async (instance) => { + const schema = await dereferenceSchema(id); + const validate = await ajv.compileAsync(schema); + const valid = validate(instance); + if (validate.errors) { + console.error(validate.errors); + } + return valid; + }, + }); + +const api = { + validator, +}; + +module.exports = api; diff --git a/packages/traceability-tests/utils/verifier.js b/packages/traceability-tests/utils/verifier.js new file mode 100644 index 000000000..1e5258206 --- /dev/null +++ b/packages/traceability-tests/utils/verifier.js @@ -0,0 +1,22 @@ +const jose = require('jose'); +const validator = require('./validator'); + +// normally, you only accept certain issuers... +// this is for testing purposes... +const getPublicKey = async (didJwkUrl) => jose.importJWK(JSON.parse(jose.base64url.decode(didJwkUrl.split(':')[2].split('#')[0]))); + +const verifier = { + verify: async (token) => { + // decode header first, + // don't look at payload until after verify. + const { iss, kid } = jose.decodeProtectedHeader(token); + const publicKey = await getPublicKey(`${iss}#${kid}`); + const { payload, protectedHeader } = await jose.jwtVerify(token, publicKey); + return { payload, protectedHeader }; + }, + // normally this would be applied before after verify above, exported here for test readability. + ...validator, +}; + +const api = verifier; +module.exports = api;