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"); }); });