From 45721beee50f709893b47107fee7d3d7b0112ddc Mon Sep 17 00:00:00 2001 From: Andrew Bernat Date: Fri, 26 Jun 2026 20:33:52 -0700 Subject: [PATCH] rename the test helper registration.ts -> ceremony.ts The rename is necessary b/c authentication operations will be added which is no longer a "registration" type of operation --- .../passkeys-browser/test/ceremonies.test.ts | 18 ++++----- ...{registration.test.ts => ceremony.test.ts} | 40 +++++++++---------- .../helpers/{registration.ts => ceremony.ts} | 17 ++++---- 3 files changed, 39 insertions(+), 36 deletions(-) rename clients/passkeys-browser/test/helpers/{registration.test.ts => ceremony.test.ts} (67%) rename clients/passkeys-browser/test/helpers/{registration.ts => ceremony.ts} (92%) diff --git a/clients/passkeys-browser/test/ceremonies.test.ts b/clients/passkeys-browser/test/ceremonies.test.ts index 6909bad..2ffc738 100644 --- a/clients/passkeys-browser/test/ceremonies.test.ts +++ b/clients/passkeys-browser/test/ceremonies.test.ts @@ -23,7 +23,7 @@ import { newHttpHandler } from "./helpers/httpServer"; import * as http from "node:http"; -import {RegistrationService} from "./helpers/registration"; +import {CeremonyService} from "./helpers/ceremony"; import {FakeAuthenticator} from "./helpers/fakeAuthenticator"; const CREATE_OPTIONS_JSON: PublicKeyCredentialCreationOptionsJson = { @@ -158,11 +158,11 @@ describe("PkAuthCeremonyClient http test", () => { await expect(client.register({ username: "alice", label: "key" })).rejects.toThrow("HTTP 500: unit test error from errorHttpHandler"); - expect(credentialsContainer.create).not.toHaveBeenCalled(); // this proves the client did not attempt to create a credential upon failure to start registration.ts + expect(credentialsContainer.create).not.toHaveBeenCalled(); // this proves the client did not attempt to create a credential upon failure to start ceremony.ts }) it("when start registration succeeds but no credential is created, then the client fails with credential cancellation", async() => { - const registration = new RegistrationService("localhost", "alice", "bob") + const registration = new CeremonyService("localhost", "alice", "bob") using server= await startHttpServer({ [startPath]: newStartHandler(registration), [finishPath]: newFinishHandler(registration) @@ -178,7 +178,7 @@ describe("PkAuthCeremonyClient http test", () => { }) it("when finish registration fails, the credential was still created", async() => { - const registration = new RegistrationService("localhost", "alice", "bob") + const registration = new CeremonyService("localhost", "alice", "bob") using server= await startHttpServer({ [startPath]: newStartHandler(registration), [finishPath]: errorHttpHandler @@ -196,7 +196,7 @@ describe("PkAuthCeremonyClient http test", () => { }) it("when registering a credential, then the server returns a finished response", async() => { - const registration = new RegistrationService("localhost", "alice", "bob") + const registration = new CeremonyService("localhost", "alice", "bob") using server = await startHttpServer({ [startPath]: newStartHandler(registration), [finishPath]: newFinishHandler(registration) @@ -237,17 +237,17 @@ describe("PkAuthCeremonyClient http test", () => { res.end("unit test error from errorHttpHandler") } - function newStartHandler(registration: RegistrationService) : HttpHandler{ + function newStartHandler(registration: CeremonyService) : HttpHandler{ const endpoint = async (req: StartRegistrationRequest) : Promise => { - return registration.start(req); + return registration.startRegistration(req); }; const handler = new Handler(decodePost, endpoint, encodeJson); return newHttpHandler(handler); } - function newFinishHandler(registration: RegistrationService) : HttpHandler{ + function newFinishHandler(registration: CeremonyService) : HttpHandler{ const endpoint = async (req: FinishRegistrationRequest) : Promise => { - return registration.finish(req); + return registration.finishRegistration(req); }; const handler = new Handler(decodePost, endpoint, encodeJson); return newHttpHandler(handler); diff --git a/clients/passkeys-browser/test/helpers/registration.test.ts b/clients/passkeys-browser/test/helpers/ceremony.test.ts similarity index 67% rename from clients/passkeys-browser/test/helpers/registration.test.ts rename to clients/passkeys-browser/test/helpers/ceremony.test.ts index ec86bf8..d502aa1 100644 --- a/clients/passkeys-browser/test/helpers/registration.test.ts +++ b/clients/passkeys-browser/test/helpers/ceremony.test.ts @@ -3,21 +3,21 @@ import { Encoder } from "cbor-x"; import * as b64u from "../../src/base64url"; import { decodeCreationOptions, encodeRegistrationResponse } from "../../src/ceremonies"; import { FakeAuthenticator } from "./fakeAuthenticator"; -import { RegistrationService } from "./registration"; +import { CeremonyService } from "./ceremony"; describe("RegistrationService", () => { describe("start a registration", () => { it("returns 403 when a username is not in the allowlist", async () => { - const service = new RegistrationService("localhost", "alice"); + const service = new CeremonyService("localhost", "alice"); await expect( - service.start({ username: "eve", displayName: null, label: null, challenge: null }), + service.startRegistration({ username: "eve", displayName: null, label: null, challenge: null }), ).rejects.toThrow("HTTP 403"); }); it("returns a StartRegistrationResponse when the request is valid", async () => { - const service = new RegistrationService("localhost", "alice") + const service = new CeremonyService("localhost", "alice") - const response = await service.start({ username: "alice", displayName: null, label: null, challenge: null }) + const response = await service.startRegistration({ username: "alice", displayName: null, label: null, challenge: null }) expect(response.challengeId).toBeTypeOf("string"); expect(response.publicKey.user.id).toBeTypeOf("string"); @@ -33,9 +33,9 @@ describe("RegistrationService", () => { describe("finish a registration", () => { it("returns 400 for an unknown challengeId", async () => { - const service = new RegistrationService("localhost", "alice"); + const service = new CeremonyService("localhost", "alice"); await expect( - service.finish({ + service.finishRegistration({ challengeId: "does-not-exist", username: "alice", label: "key", @@ -50,27 +50,27 @@ describe("RegistrationService", () => { }); it("returns 400 for a username that does not match the started session", async () => { - const service = new RegistrationService("localhost", "alice", "bob"); - const { challengeId, publicKey } = await service.start({ username: "alice", displayName: null, label: null, challenge: null }); + const service = new CeremonyService("localhost", "alice", "bob"); + const { challengeId, publicKey } = await service.startRegistration({ username: "alice", displayName: null, label: null, challenge: null }); const auth = new FakeAuthenticator(); const credential = await auth.create({ publicKey: decodeCreationOptions(publicKey) }); const encoded = encodeRegistrationResponse(credential); await expect( - service.finish({ challengeId, username: "bob", label: null, response: encoded }), + service.finishRegistration({ challengeId, username: "bob", label: null, response: encoded }), ).rejects.toThrow("HTTP 400"); }); it("verifies the attestation and returns credential metadata", async () => { - const service = new RegistrationService("localhost", "alice"); - const { challengeId, publicKey } = await service.start({ username: "alice", displayName: "Alice", label: null, challenge: null }); + const service = new CeremonyService("localhost", "alice"); + const { challengeId, publicKey } = await service.startRegistration({ username: "alice", displayName: "Alice", label: null, challenge: null }); const auth = new FakeAuthenticator(); const credential = await auth.create({ publicKey: decodeCreationOptions(publicKey) }); const encoded = encodeRegistrationResponse(credential); - const { credential: cred } = await service.finish({ challengeId, username: "alice", label: "my-key", response: encoded }); + const { credential: cred } = await service.finishRegistration({ challengeId, username: "alice", label: "my-key", response: encoded }); expect(cred.credentialId).toBeTypeOf("string"); expect(cred.userHandle).toBeTypeOf("string"); @@ -81,8 +81,8 @@ describe("RegistrationService", () => { }); it("returns 400 when the attestation signature is tampered", async () => { - const service = new RegistrationService("localhost", "alice"); - const { challengeId, publicKey } = await service.start({ username: "alice", displayName: null, label: null, challenge: null }); + const service = new CeremonyService("localhost", "alice"); + const { challengeId, publicKey } = await service.startRegistration({ username: "alice", displayName: null, label: null, challenge: null }); const auth = new FakeAuthenticator(); const credential = await auth.create({ publicKey: decodeCreationOptions(publicKey) }); @@ -99,14 +99,14 @@ describe("RegistrationService", () => { const tampered = { ...encoded, response: { ...encoded.response, attestationObject: b64u.encode(cbor.encode(attObj) as Uint8Array) } }; await expect( - service.finish({ challengeId, username: "alice", label: null, response: tampered }), + service.finishRegistration({ challengeId, username: "alice", label: null, response: tampered }), ).rejects.toThrow("HTTP 400"); }); it("returns 400 when the challenge in clientDataJSON belongs to a different session", async () => { - const service = new RegistrationService("localhost", "alice"); - const s1 = await service.start({ username: "alice", displayName: null, label: null, challenge: null }); - const s2 = await service.start({ username: "alice", displayName: null, label: null, challenge: null }); + const service = new CeremonyService("localhost", "alice"); + const s1 = await service.startRegistration({ username: "alice", displayName: null, label: null, challenge: null }); + const s2 = await service.startRegistration({ username: "alice", displayName: null, label: null, challenge: null }); const auth = new FakeAuthenticator(); const credential = await auth.create({ publicKey: decodeCreationOptions(s2.publicKey) }); @@ -114,7 +114,7 @@ describe("RegistrationService", () => { // finish using s1 challengeId but with s2's credential -- this should fail await expect( - service.finish({ challengeId: s1.challengeId, username: "alice", label: null, response: encoded }), + service.finishRegistration({ challengeId: s1.challengeId, username: "alice", label: null, response: encoded }), ).rejects.toThrow("HTTP 400"); }); }); diff --git a/clients/passkeys-browser/test/helpers/registration.ts b/clients/passkeys-browser/test/helpers/ceremony.ts similarity index 92% rename from clients/passkeys-browser/test/helpers/registration.ts rename to clients/passkeys-browser/test/helpers/ceremony.ts index afa71b9..5c60098 100644 --- a/clients/passkeys-browser/test/helpers/registration.ts +++ b/clients/passkeys-browser/test/helpers/ceremony.ts @@ -19,13 +19,16 @@ interface PendingEntry { } /* -RegistrationService is a server side service that can start and finish -client registrations. +CeremonyService is a server side service that can: + - start registration + - finish registration + - start authentication + - finish authentication Instantiate the service with a relying party id [rpId] and a list of allowed users. -A registration is started with a call to start which will produce a challenge that -the client must respond to with a call to finish. +A registration is started with a call to startRegistration which will produce a challenge that +the client must respond to with a call to finishRegistration. */ -export class RegistrationService { +export class CeremonyService { // pending is the map of registrations which have been started; but not yet finished. // Maps challenge id to a PendingEntry structure. private readonly pending : Map; @@ -38,7 +41,7 @@ export class RegistrationService { this.allowedUsernames = allowedUsernames ?? []; } - async start(req : StartRegistrationRequest) : Promise { + async startRegistration(req : StartRegistrationRequest) : Promise { if (!this.allowedUsernames.includes(req.username)) { throw new HttpError(403, { error: `username '${req.username}' not allowed` }); } @@ -60,7 +63,7 @@ export class RegistrationService { return newStartRegistrationResponse(pendingEntry) } - async finish(req: FinishRegistrationRequest): Promise { + async finishRegistration(req: FinishRegistrationRequest): Promise { const entry = this.pending.get(req.challengeId); if (!entry) { throw new HttpError(400, { error: `unknown challengeId: ${req.challengeId}` });