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”createMailboxChallenge sends the challenge email by default. Pass sendEmail: false only if you want to send the rendered email yourself.
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 } = result.value.challenge;
// save this in the user's session or a secure HTTP only cookie await savePendingChallenge({ purpose: "email-change", challengeId, secret, });
} 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.value.challenge.email;
// email currentEmail notifying user of the change await sendEmailUpdatedMessage(currentEmail);
// update the account email await updateUserEmail(userId, email);} else { throw new Error(result.error.message);}