From f43d4edd6bedcdfe69d89e57bd621389bccdfbf3 Mon Sep 17 00:00:00 2001 From: Rob Knight Date: Tue, 16 Jul 2024 22:15:30 +0200 Subject: [PATCH] Throw exceptions when event ID doesn't match configuration (#1802) Closes https://linear.app/0xparc-pcd/issue/0XP-974/zuauth-doesnt-check-event-id-when-revealed Fixes `authenticate` so that, if the event ID is revealed and event IDs are configured as filters, the revealed event ID must match one of the filtered values. Also updates the tests to be more careful in identifying the kind of exception thrown. --- .../src/pages/examples/zuauth/index.tsx | 4 +- .../src/pages/examples/zuauth/utils.ts | 6 +- .../src/routing/routes/zuauth/login.ts | 10 +- examples/zuauth/src/app/api/login/route.ts | 11 +- examples/zuauth/src/app/home.tsx | 4 +- packages/lib/zuauth/package.json | 2 + packages/lib/zuauth/src/server.ts | 152 ++++++-- packages/lib/zuauth/src/zuauth.ts | 15 +- packages/lib/zuauth/test/zuauth.spec.ts | 362 +++++++++++++----- 9 files changed, 428 insertions(+), 138 deletions(-) diff --git a/apps/consumer-client/src/pages/examples/zuauth/index.tsx b/apps/consumer-client/src/pages/examples/zuauth/index.tsx index 10244981fa..26fd3f50d3 100644 --- a/apps/consumer-client/src/pages/examples/zuauth/index.tsx +++ b/apps/consumer-client/src/pages/examples/zuauth/index.tsx @@ -70,7 +70,9 @@ export default function ZuAuth(): JSX.Element { }); if (result.type === "pcd") { - setAuthenticated(await serverLogin(result.pcdStr, config)); + setAuthenticated( + await serverLogin(result.pcdStr, config, fieldsToReveal) + ); } })(); }, [fieldsToReveal, configString]); diff --git a/apps/consumer-client/src/pages/examples/zuauth/utils.ts b/apps/consumer-client/src/pages/examples/zuauth/utils.ts index 0f1ae2d9bc..eca9fa7b56 100644 --- a/apps/consumer-client/src/pages/examples/zuauth/utils.ts +++ b/apps/consumer-client/src/pages/examples/zuauth/utils.ts @@ -1,5 +1,6 @@ import { ITicketData } from "@pcd/eddsa-ticket-pcd"; import { PipelineEdDSATicketZuAuthConfig } from "@pcd/passport-interface"; +import { EdDSATicketFieldsToReveal } from "@pcd/zk-eddsa-event-ticket-pcd"; import urlJoin from "url-join"; import { CONSUMER_SERVER_URL } from "../../../constants"; @@ -33,7 +34,8 @@ export async function logout(): Promise { */ export async function serverLogin( serialized: string, - config: PipelineEdDSATicketZuAuthConfig[] + config: PipelineEdDSATicketZuAuthConfig[], + fieldsToReveal: EdDSATicketFieldsToReveal ): Promise> { const response = await fetch(urlJoin(CONSUMER_SERVER_URL, `auth/login`), { method: "POST", @@ -43,7 +45,7 @@ export async function serverLogin( "Access-Control-Allow-Origin": "*", "Content-Type": "application/json" }, - body: JSON.stringify({ pcd: serialized, config }) + body: JSON.stringify({ pcd: serialized, config, fieldsToReveal }) }); return await response.json(); diff --git a/apps/consumer-server/src/routing/routes/zuauth/login.ts b/apps/consumer-server/src/routing/routes/zuauth/login.ts index 9eab3d3ee3..6eaef0baac 100644 --- a/apps/consumer-server/src/routing/routes/zuauth/login.ts +++ b/apps/consumer-server/src/routing/routes/zuauth/login.ts @@ -27,11 +27,11 @@ export function login( return; } - const pcd = await authenticate( - req.body.pcd, - session.watermark, - req.body.config - ); + const pcd = await authenticate(req.body.pcd, { + watermark: session.watermark, + config: req.body.config, + fieldsToReveal: req.body.fieldsToReveal + }); session.ticket = pcd.claim.partialTicket; diff --git a/examples/zuauth/src/app/api/login/route.ts b/examples/zuauth/src/app/api/login/route.ts index 31b6f40e8b..6851fcbbfa 100644 --- a/examples/zuauth/src/app/api/login/route.ts +++ b/examples/zuauth/src/app/api/login/route.ts @@ -23,7 +23,16 @@ export async function POST(req: NextRequest) { try { const session = await getIronSession(cookieStore, ironOptions); - const pcd = await authenticate(body.pcd, session.watermark ?? "", config); + const pcd = await authenticate(body.pcd, { + watermark: session.watermark ?? "", + config, + fieldsToReveal: { + revealAttendeeEmail: true, + revealAttendeeName: true, + revealEventId: true, + revealProductId: true + } + }); session.user = pcd.claim.partialTicket; await session.save(); diff --git a/examples/zuauth/src/app/home.tsx b/examples/zuauth/src/app/home.tsx index 0ff4109cad..60782e55cd 100644 --- a/examples/zuauth/src/app/home.tsx +++ b/examples/zuauth/src/app/home.tsx @@ -32,7 +32,9 @@ export default function Home() { zupassUrl: process.env.NEXT_PUBLIC_ZUPASS_SERVER_URL as string, fieldsToReveal: { revealAttendeeEmail: true, - revealAttendeeName: true + revealAttendeeName: true, + revealEventId: true, + revealProductId: true }, watermark, config: config diff --git a/packages/lib/zuauth/package.json b/packages/lib/zuauth/package.json index 40dd406214..a2a5a15f20 100644 --- a/packages/lib/zuauth/package.json +++ b/packages/lib/zuauth/package.json @@ -64,8 +64,10 @@ "@pcd/tsconfig": "0.11.1", "@semaphore-protocol/identity": "^3.15.2", "@types/chai": "^4.3.5", + "@types/chai-as-promised": "^7.1.5", "@types/mocha": "^10.0.1", "@types/react": "^18.2.0", + "chai-as-promised": "^7.1.1", "eslint": "^8.57.0", "mocha": "^10.2.0", "ts-mocha": "^10.0.0", diff --git a/packages/lib/zuauth/src/server.ts b/packages/lib/zuauth/src/server.ts index 4dfe67b018..48bc9c88f0 100644 --- a/packages/lib/zuauth/src/server.ts +++ b/packages/lib/zuauth/src/server.ts @@ -1,10 +1,73 @@ import { isEqualEdDSAPublicKey } from "@pcd/eddsa-pcd"; -import { PipelineEdDSATicketZuAuthConfig } from "@pcd/passport-interface"; +import { ITicketData } from "@pcd/eddsa-ticket-pcd"; +import { PipelineZuAuthConfig } from "@pcd/passport-interface"; import { + EdDSATicketFieldsToReveal, ZKEdDSAEventTicketPCD, + ZKEdDSAEventTicketPCDClaim, ZKEdDSAEventTicketPCDPackage, ZKEdDSAEventTicketPCDTypeName } from "@pcd/zk-eddsa-event-ticket-pcd"; +import { ZuAuthArgs } from "."; + +/** + * Check if a given field is defined. + */ +function checkIsDefined( + field: T | undefined, + fieldName: string +): field is T { + if (field === undefined || field === null) { + throw new Error( + `Field "${fieldName}" is undefined but should have a value` + ); + } + return true; +} + +/** + * Check if a given field is undefined. + */ +function checkIsUndefined(field: unknown, fieldName: string): boolean { + if (field !== undefined) { + throw new Error( + `Field "${fieldName}" is defined but should not have a value` + ); + } + return true; +} + +/** + * Check if an individual configuration matches the claim from the PCD. + */ +function claimMatchesConfiguration( + claim: ZKEdDSAEventTicketPCDClaim, + config: PipelineZuAuthConfig +): boolean { + return ( + isEqualEdDSAPublicKey(claim.signer, config.publicKey) && + claim.partialTicket.eventId === config.eventId && + (config.productId === undefined || + claim.partialTicket.productId === config.productId) + ); +} + +const revealedFields: Record< + keyof EdDSATicketFieldsToReveal, + keyof ITicketData +> = { + revealAttendeeEmail: "attendeeEmail", + revealAttendeeName: "attendeeName", + revealAttendeeSemaphoreId: "attendeeSemaphoreId", + revealEventId: "eventId", + revealIsConsumed: "isConsumed", + revealIsRevoked: "isRevoked", + revealProductId: "productId", + revealTicketCategory: "ticketCategory", + revealTicketId: "ticketId", + revealTimestampConsumed: "timestampConsumed", + revealTimestampSigned: "timestampSigned" +} as const; /** * Authenticates a ticket PCD. @@ -20,9 +83,11 @@ import { */ export async function authenticate( pcdStr: string, - watermark: string, - config: PipelineEdDSATicketZuAuthConfig[] + { watermark, config, fieldsToReveal, externalNullifier }: ZuAuthArgs ): Promise { + /** + * Check to see if our inputs are valid, beginning with the PCD. + */ const serializedPCD = JSON.parse(pcdStr); if (serializedPCD.type !== ZKEdDSAEventTicketPCDTypeName) { throw new Error("PCD is malformed or of the incorrect type"); @@ -34,37 +99,68 @@ export async function authenticate( throw new Error("ZK ticket PCD is not valid"); } - if (pcd.claim.watermark.toString() !== watermark) { - throw new Error("PCD watermark doesn't match"); + /** + * The configuration array must not be empty. + */ + if (config.length === 0) { + throw new Error("Configuration is empty"); } - const publicKeys = config.map((em) => em.publicKey); - const productIds = new Set( - // Product ID is optional, so it's important to filter out undefined values - config - .map((em) => em.productId) - .filter((productId) => productId !== undefined) - ); - - if ( - publicKeys.length > 0 && - !publicKeys.find((pubKey) => - isEqualEdDSAPublicKey(pubKey, pcd.claim.signer) - ) - ) { + /** + * Check if the external nullifier matches the configuration. + */ + if (externalNullifier !== undefined) { + if (pcd.claim.externalNullifier === undefined) { + throw new Error( + "PCD is missing external nullifier when one was provided" + ); + } + if ( + pcd.claim.externalNullifier.toString() !== externalNullifier.toString() + ) { + throw new Error("External nullifier does not match the provided value"); + } + } else if (pcd.claim.externalNullifier !== undefined) { throw new Error( - "Signing key does not match any of the configured public keys" + "PCD contains an external nullifier when none was provided" ); } - if ( - productIds.size > 0 && - pcd.claim.partialTicket.productId && - !productIds.has(pcd.claim.partialTicket.productId) - ) { - throw new Error( - "Product ID does not match any of the configured product IDs" - ); + if (pcd.claim.watermark !== watermark.toString()) { + throw new Error("PCD watermark does not match"); + } + + checkIsUndefined(pcd.claim.validEventIds, "validEventIds"); + + /** + * Check that the revealed fields in the PCD match the expectations set out + * in {@link revealedFields}. This is to ensure the consistency between the + * configuration passed to this function, and the configuration used on the + * client-side when generating the PCD. + */ + for (const [revealedField, fieldName] of Object.entries(revealedFields)) { + if (fieldsToReveal[revealedField as keyof EdDSATicketFieldsToReveal]) { + checkIsDefined(pcd.claim.partialTicket[fieldName], fieldName); + } else { + checkIsUndefined(pcd.claim.partialTicket[fieldName], fieldName); + } + } + + /** + * Our inputs are formally valid. Now we check to see if any of the + * configuration patterns match the claim in the PCD. + */ + let match = false; + + for (const em of config) { + if (claimMatchesConfiguration(pcd.claim, em)) { + match = true; + break; + } + } + + if (!match) { + throw new Error("PCD does not match any of the configured patterns"); } return pcd; diff --git a/packages/lib/zuauth/src/zuauth.ts b/packages/lib/zuauth/src/zuauth.ts index 691009d771..f39309917e 100644 --- a/packages/lib/zuauth/src/zuauth.ts +++ b/packages/lib/zuauth/src/zuauth.ts @@ -65,7 +65,7 @@ export function zuAuthRedirect(args: ZuAuthRedirectArgs): void { */ export function constructZkTicketProofUrl(zuAuthArgs: ZuAuthArgs): string { const { - zupassUrl = "https://zupass.org", + zupassUrl = "https://zupass.org/", returnUrl, fieldsToReveal, watermark, @@ -114,6 +114,16 @@ export function constructZkTicketProofUrl(zuAuthArgs: ZuAuthArgs): string { publicKeys.push(em.publicKey); } + if (!fieldsToReveal.revealEventId) { + throw new Error("The event ID must be revealed for authentication"); + } + + if (productIds.length > 0 && !fieldsToReveal.revealProductId) { + throw new Error( + "When product IDs are specified for authentication, the product ID field must be revealed" + ); + } + const args: ZKEdDSAEventTicketPCDArgs = { ticket: { argumentType: ArgumentTypeName.PCD, @@ -135,8 +145,7 @@ export function constructZkTicketProofUrl(zuAuthArgs: ZuAuthArgs): string { }, validEventIds: { argumentType: ArgumentTypeName.StringArray, - value: - eventIds.length !== 0 && eventIds.length <= 20 ? eventIds : undefined, + value: undefined, userProvided: false }, fieldsToReveal: { diff --git a/packages/lib/zuauth/test/zuauth.spec.ts b/packages/lib/zuauth/test/zuauth.spec.ts index 6c217cac25..51bc85b551 100644 --- a/packages/lib/zuauth/test/zuauth.spec.ts +++ b/packages/lib/zuauth/test/zuauth.spec.ts @@ -9,12 +9,14 @@ import { import { ArgumentTypeName, SerializedPCD } from "@pcd/pcd-types"; import { SemaphoreIdentityPCDPackage } from "@pcd/semaphore-identity-pcd"; import { + EdDSATicketFieldsToReveal, ZKEdDSAEventTicketPCD, ZKEdDSAEventTicketPCDPackage, ZKEdDSAEventTicketPCDTypeName } from "@pcd/zk-eddsa-event-ticket-pcd"; import { Identity } from "@semaphore-protocol/identity"; -import { assert, expect } from "chai"; +import { expect, use } from "chai"; +import chaiAsPromised from "chai-as-promised"; import "mocha"; import * as path from "path"; import { v4 as uuid } from "uuid"; @@ -45,7 +47,9 @@ async function makeTestTicket( async function makeZKTicketPCD( ticketPCD: EdDSATicketPCD, identity: Identity, - watermark: string + watermark: string, + fieldsToReveal: EdDSATicketFieldsToReveal, + validEventIds: string[] | undefined ): Promise { const serializedTicketPCD = await EdDSATicketPCDPackage.serialize(ticketPCD); const serializedIdentityPCD = await SemaphoreIdentityPCDPackage.serialize( @@ -64,14 +68,11 @@ async function makeZKTicketPCD( argumentType: ArgumentTypeName.PCD }, fieldsToReveal: { - value: { - revealEventId: true, - revealProductId: true - }, + value: fieldsToReveal, argumentType: ArgumentTypeName.ToggleList }, validEventIds: { - value: [ticketPCD.claim.ticket.eventId], + value: validEventIds, argumentType: ArgumentTypeName.StringArray }, externalNullifier: { @@ -119,27 +120,41 @@ describe("zuauth should work", async function () { const watermark = generateSnarkMessageHash("watermark").toString(); let zkPCD: ZKEdDSAEventTicketPCD; let serializedZKPCD: SerializedPCD; + let ticketPCD: EdDSATicketPCD; this.beforeAll(async () => { + use(chaiAsPromised); await EdDSATicketPCDPackage.init?.({}); await ZKEdDSAEventTicketPCDPackage.init?.({ zkeyFilePath, wasmFilePath }); - const ticketPCD = await makeTestTicket(privKey, testTicket); + ticketPCD = await makeTestTicket(privKey, testTicket); - zkPCD = await makeZKTicketPCD(ticketPCD, identity, watermark); + zkPCD = await makeZKTicketPCD( + ticketPCD, + identity, + watermark, + { + revealEventId: true, + revealProductId: true + }, + undefined + ); serializedZKPCD = await ZKEdDSAEventTicketPCDPackage.serialize(zkPCD); }); it("should authenticate PCDs with correct settings", async function () { const publicKey = await getEdDSAPublicKey(privKey); - const resultPCD = await authenticate( - JSON.stringify(serializedZKPCD), + const resultPCD = await authenticate(JSON.stringify(serializedZKPCD), { watermark, - [ + fieldsToReveal: { + revealEventId: true, + revealProductId: true + }, + config: [ { eventId: testTicket.eventId, eventName: testTicket.eventName, @@ -149,121 +164,276 @@ describe("zuauth should work", async function () { publicKey } ] - ); + }); expect(resultPCD.type).to.eq(ZKEdDSAEventTicketPCDTypeName); expect(resultPCD.claim.partialTicket.eventId).to.eq(testTicket.eventId); expect(resultPCD.claim.partialTicket.productId).to.eq(testTicket.productId); }); - it("should not authenticate PCDs with the wrong public key", async function () { - const newPrivKey = newEdDSAPrivateKey(); - const publicKey = await getEdDSAPublicKey(newPrivKey); + it("should authenticate PCDs without product ID in configuration", async function () { + const publicKey = await getEdDSAPublicKey(privKey); - try { - await authenticate(JSON.stringify(serializedZKPCD), watermark, [ + const resultPCD = await authenticate(JSON.stringify(serializedZKPCD), { + watermark, + fieldsToReveal: { + revealEventId: true, + revealProductId: true + }, + config: [ { eventId: testTicket.eventId, eventName: testTicket.eventName, - productId: testTicket.productId, - productName: testTicket.ticketName, pcdType: EdDSATicketPCDTypeName, publicKey } - ]); - assert(false, "Should not reach this point due to exception"); - } catch (e) { - expect(e).to.exist; - } + ] + }); + + expect(resultPCD.type).to.eq(ZKEdDSAEventTicketPCDTypeName); + expect(resultPCD.claim.partialTicket.eventId).to.eq(testTicket.eventId); + expect(resultPCD.claim.partialTicket.productId).to.eq(testTicket.productId); + }); + + it("should not authenticate PCD where fields are unexpectedly revealed", async function () { + const publicKey = await getEdDSAPublicKey(privKey); + + await expect( + authenticate(JSON.stringify(serializedZKPCD), { + watermark, + fieldsToReveal: { + revealEventId: true, + // Product ID is revealed in the PCD but this is not expected + revealProductId: false + }, + config: [ + { + eventId: testTicket.eventId, + eventName: testTicket.eventName, + productId: testTicket.productId, + productName: testTicket.ticketName, + pcdType: EdDSATicketPCDTypeName, + publicKey + } + ] + }) + ).to.be.rejectedWith( + 'Field "productId" is defined but should not have a value' + ); + }); + + it("should not authenticate PCDs with the wrong public key", async function () { + const newPrivKey = newEdDSAPrivateKey(); + const publicKey = await getEdDSAPublicKey(newPrivKey); + + await expect( + authenticate(JSON.stringify(serializedZKPCD), { + watermark, + fieldsToReveal: { + revealEventId: true, + revealProductId: true + }, + config: [ + { + eventId: testTicket.eventId, + eventName: testTicket.eventName, + productId: testTicket.productId, + productName: testTicket.ticketName, + pcdType: EdDSATicketPCDTypeName, + publicKey + } + ] + }) + ).to.be.rejectedWith("PCD does not match any of the configured patterns"); }); it("should not authenticate PCDs with the wrong watermark", async function () { const publicKey = await getEdDSAPublicKey(privKey); const newWatermark = generateSnarkMessageHash("new watermark").toString(); - try { - await authenticate(JSON.stringify(serializedZKPCD), newWatermark, [ - { - eventId: testTicket.eventId, - eventName: testTicket.eventName, - productId: testTicket.productId, - productName: testTicket.ticketName, - pcdType: EdDSATicketPCDTypeName, - publicKey - } - ]); - assert(false, "Should not reach this point due to exception"); - } catch (e) { - expect(e).to.exist; - } + await expect( + authenticate(JSON.stringify(serializedZKPCD), { + watermark: newWatermark, + fieldsToReveal: { + revealEventId: true, + revealProductId: true + }, + config: [ + { + eventId: testTicket.eventId, + eventName: testTicket.eventName, + productId: testTicket.productId, + productName: testTicket.ticketName, + pcdType: EdDSATicketPCDTypeName, + publicKey + } + ] + }) + ).to.be.rejectedWith("PCD watermark does not match"); }); it("should not authenticate PCDs with the wrong event ID", async function () { const publicKey = await getEdDSAPublicKey(privKey); - try { - await authenticate(JSON.stringify(serializedZKPCD), watermark, [ - { - eventId: uuid(), - eventName: testTicket.eventName, - productId: testTicket.productId, - productName: testTicket.ticketName, - pcdType: EdDSATicketPCDTypeName, - publicKey - } - ]); - assert(false, "Should not reach this point due to exception"); - } catch (e) { - expect(e).to.exist; - } + await expect( + authenticate(JSON.stringify(serializedZKPCD), { + watermark, + fieldsToReveal: { + revealEventId: true, + revealProductId: true + }, + config: [ + { + eventId: uuid(), + eventName: testTicket.eventName, + productId: testTicket.productId, + productName: testTicket.ticketName, + pcdType: EdDSATicketPCDTypeName, + publicKey + } + ] + }) + ).to.be.rejectedWith("PCD does not match any of the configured patterns"); }); it("should not authenticate PCDs with the wrong product ID", async function () { const publicKey = await getEdDSAPublicKey(privKey); - try { - await authenticate(JSON.stringify(serializedZKPCD), watermark, [ - { - eventId: testTicket.eventId, - eventName: testTicket.eventName, - productId: uuid(), - productName: testTicket.ticketName, - pcdType: EdDSATicketPCDTypeName, - publicKey - } - ]); - assert(false, "Should not reach this point due to exception"); - } catch (e) { - expect(e).to.exist; - } + await expect( + authenticate(JSON.stringify(serializedZKPCD), { + watermark, + fieldsToReveal: { + revealEventId: true, + revealProductId: true + }, + config: [ + { + eventId: testTicket.eventId, + eventName: testTicket.eventName, + productId: uuid(), + productName: testTicket.ticketName, + pcdType: EdDSATicketPCDTypeName, + publicKey + } + ] + }) + ).to.be.rejectedWith("PCD does not match any of the configured patterns"); + }); + + it("should not authenticate a PCD which should have a revealed event ID but does not", async function () { + const publicKey = await getEdDSAPublicKey(privKey); + + // Proving operation which happens on the client-side. + const zkPCDWithoutRevealedEventId = await makeZKTicketPCD( + ticketPCD, + identity, + watermark, + { revealEventId: false, revealProductId: true }, + undefined + ); + + const serializedZKPCD = await ZKEdDSAEventTicketPCDPackage.serialize( + zkPCDWithoutRevealedEventId + ); + + await expect( + authenticate(JSON.stringify(serializedZKPCD), { + watermark, + fieldsToReveal: { + // Here the "server-side" config does not match the client. + revealEventId: true, + revealProductId: true + }, + config: [ + { + eventId: uuid(), + eventName: testTicket.eventName, + productId: testTicket.productId, + productName: testTicket.ticketName, + pcdType: EdDSATicketPCDTypeName, + publicKey + } + ] + }) + ).to.be.rejectedWith( + `Field "eventId" is undefined but should have a value` + ); + }); + + it("should not authenticate a PCD which should have a revealed product ID but does not", async function () { + const publicKey = await getEdDSAPublicKey(privKey); + + // Proving operation which happens on the client-side. + const zkPCDWithoutRevealedProductId = await makeZKTicketPCD( + ticketPCD, + identity, + watermark, + { revealEventId: true, revealProductId: false }, + undefined + ); + + const serializedZKPCD = await ZKEdDSAEventTicketPCDPackage.serialize( + zkPCDWithoutRevealedProductId + ); + + await expect( + authenticate(JSON.stringify(serializedZKPCD), { + watermark, + fieldsToReveal: { + // Here the "server-side" config does not match the client. + revealEventId: true, + revealProductId: true + }, + config: [ + { + eventId: uuid(), + eventName: testTicket.eventName, + productId: testTicket.productId, + productName: testTicket.ticketName, + pcdType: EdDSATicketPCDTypeName, + publicKey + } + ] + }) + ).to.be.rejectedWith( + 'Field "productId" is undefined but should have a value' + ); }); it("should construct URLs for popup windows", async function () { - const url = constructZkTicketProofUrl({ - config: [ - { - pcdType: "eddsa-ticket-pcd", - publicKey: [ - "1d47687549cb273b6fed3493de5a954920dd0403f8c7eb67c2ff72a26fa4ab62", - "1144ef5d44e2d8972d7ade8138629ebefb094025ebb4df00ed02e22d9b68e665" + let url = ""; + + expect( + () => + (url = constructZkTicketProofUrl({ + config: [ + { + pcdType: "eddsa-ticket-pcd", + publicKey: [ + "1d47687549cb273b6fed3493de5a954920dd0403f8c7eb67c2ff72a26fa4ab62", + "1144ef5d44e2d8972d7ade8138629ebefb094025ebb4df00ed02e22d9b68e665" + ], + eventId: "536c96f5-feb8-4938-bcac-47d4e13847c6", + eventName: "Test event", + productId: "9e39949c-b468-4c7e-a6a2-7735521f0bda", + productName: "GA" + } ], - eventId: "536c96f5-feb8-4938-bcac-47d4e13847c6", - eventName: "Test event", - productId: "9e39949c-b468-4c7e-a6a2-7735521f0bda", - productName: "GA" - } - ], - fieldsToReveal: {}, - watermark: "12345" - }); + fieldsToReveal: { + revealEventId: true, + revealProductId: true + }, + watermark: "12345" + })) + ).to.not.throw(); expect(url).to.eq( - "https://zupass.org#/prove?request=%7B%22type%22%3A%22Get%22%2C%22returnUrl%22%3A%22%22%2C%22args%22%3A%7B%22ticket%22%3A%7B%22argumentType%22%3A%22PCD%22%2C%22pcdType%22%3A%22eddsa-ticket-pcd%22%2C%22userProvided%22%3Atrue%2C%22validatorParams%22%3A%7B%22eventIds%22%3A%5B%22536c96f5-feb8-4938-bcac-47d4e13847c6%22%5D%2C%22productIds%22%3A%5B%229e39949c-b468-4c7e-a6a2-7735521f0bda%22%5D%2C%22publicKeys%22%3A%5B%5B%221d47687549cb273b6fed3493de5a954920dd0403f8c7eb67c2ff72a26fa4ab62%22%2C%221144ef5d44e2d8972d7ade8138629ebefb094025ebb4df00ed02e22d9b68e665%22%5D%5D%2C%22notFoundMessage%22%3A%22No%20eligible%20PCDs%20found%22%7D%7D%2C%22identity%22%3A%7B%22argumentType%22%3A%22PCD%22%2C%22pcdType%22%3A%22semaphore-identity-pcd%22%2C%22userProvided%22%3Atrue%7D%2C%22validEventIds%22%3A%7B%22argumentType%22%3A%22StringArray%22%2C%22value%22%3A%5B%22536c96f5-feb8-4938-bcac-47d4e13847c6%22%5D%2C%22userProvided%22%3Afalse%7D%2C%22fieldsToReveal%22%3A%7B%22argumentType%22%3A%22ToggleList%22%2C%22value%22%3A%7B%7D%2C%22userProvided%22%3Afalse%7D%2C%22watermark%22%3A%7B%22argumentType%22%3A%22BigInt%22%2C%22value%22%3A%2212345%22%2C%22userProvided%22%3Afalse%7D%2C%22externalNullifier%22%3A%7B%22argumentType%22%3A%22BigInt%22%2C%22value%22%3A%2212345%22%2C%22userProvided%22%3Afalse%7D%7D%2C%22pcdType%22%3A%22zk-eddsa-event-ticket-pcd%22%2C%22options%22%3A%7B%22genericProveScreen%22%3Atrue%7D%2C%22postMessage%22%3Atrue%7D" + "https://zupass.org/#/prove?request=%7B%22type%22%3A%22Get%22%2C%22returnUrl%22%3A%22%22%2C%22args%22%3A%7B%22ticket%22%3A%7B%22argumentType%22%3A%22PCD%22%2C%22pcdType%22%3A%22eddsa-ticket-pcd%22%2C%22userProvided%22%3Atrue%2C%22validatorParams%22%3A%7B%22eventIds%22%3A%5B%22536c96f5-feb8-4938-bcac-47d4e13847c6%22%5D%2C%22productIds%22%3A%5B%229e39949c-b468-4c7e-a6a2-7735521f0bda%22%5D%2C%22publicKeys%22%3A%5B%5B%221d47687549cb273b6fed3493de5a954920dd0403f8c7eb67c2ff72a26fa4ab62%22%2C%221144ef5d44e2d8972d7ade8138629ebefb094025ebb4df00ed02e22d9b68e665%22%5D%5D%2C%22notFoundMessage%22%3A%22No%20eligible%20PCDs%20found%22%7D%7D%2C%22identity%22%3A%7B%22argumentType%22%3A%22PCD%22%2C%22pcdType%22%3A%22semaphore-identity-pcd%22%2C%22userProvided%22%3Atrue%7D%2C%22validEventIds%22%3A%7B%22argumentType%22%3A%22StringArray%22%2C%22userProvided%22%3Afalse%7D%2C%22fieldsToReveal%22%3A%7B%22argumentType%22%3A%22ToggleList%22%2C%22value%22%3A%7B%22revealEventId%22%3Atrue%2C%22revealProductId%22%3Atrue%7D%2C%22userProvided%22%3Afalse%7D%2C%22watermark%22%3A%7B%22argumentType%22%3A%22BigInt%22%2C%22value%22%3A%2212345%22%2C%22userProvided%22%3Afalse%7D%2C%22externalNullifier%22%3A%7B%22argumentType%22%3A%22BigInt%22%2C%22value%22%3A%2212345%22%2C%22userProvided%22%3Afalse%7D%7D%2C%22pcdType%22%3A%22zk-eddsa-event-ticket-pcd%22%2C%22options%22%3A%7B%22genericProveScreen%22%3Atrue%7D%2C%22postMessage%22%3Atrue%7D" ); }); it("should fail to construct URL with bad watermark", async function () { - try { + expect(() => constructZkTicketProofUrl({ config: [ { @@ -273,17 +443,15 @@ describe("zuauth should work", async function () { "1144ef5d44e2d8972d7ade8138629ebefb094025ebb4df00ed02e22d9b68e665" ], eventId: "536c96f5-feb8-4938-bcac-47d4e13847c6", - eventName: "Test event", - productId: "9e39949c-b468-4c7e-a6a2-7735521f0bda", - productName: "GA" + eventName: "Test event" } ], - fieldsToReveal: {}, + fieldsToReveal: { + revealEventId: true, + revealProductId: true + }, watermark: "bad watermark" - }); - assert(false, "Should not reach here due to thrown exception"); - } catch (e) { - // Empty - } + }) + ).to.throw("Cannot convert bad watermark to a BigInt"); }); });