Account management
Account management flows often need mailbox verification too. A common example is changing the email address on an existing account: create a challenge for the new mailbox, verify the code, then update the account.
Generate and email the challenge
Section titled “Generate and email the challenge”import { Passlock, isChallengeRateLimitedError } from "@passlock/server";
const passlock = new Passlock({ ... });
const { userId } = await getLoggedInUser();
const email = "..."; // new email address
const result = await passlock.createMailboxChallenge({ email, userId, purpose: "email-change", // any pending "email-change" challenges for this userId will be nuked invalidateOthers: true,});
if (result.success) { const { challengeId, secret, message } = result.value.challenge;
// save this in the user's session or a secure HTTP only cookie await savePendingChallenge({ purpose: "email-change", challengeId, secret, });
await emailCodeToUser({ email, message });} else if (isChallengeRateLimitedError(result.error)) { return showRateLimit(result.error.retryAfterSeconds);} else { throw new Error(result.error.message);}Verify the code and create a session
Section titled “Verify the code and create a session”import { Passlock } from "@passlock/server";
const passlock = new Passlock({... });
const { userId, email: currentEmail } = await getLoggedInUser();
// fetch from the user's session or secure cookieconst pendingChallenge = await loadPendingChallenge({ purpose: "email-change" });
const result = await passlock.verifyMailboxChallenge({ challengeId: pendingChallenge.challengeId, secret: pendingChallenge.secret, code: form.code, // the user submitted this});
if (result.success) { const email = result.challenge.email;
// email currentEmail notifying user of the change await sendEmailUpdatedMessage(currentEmail);
// update the account email await updateUserEmail(userId, email);} else { return throw new Error(result.error.message);}