Introducing mailbox challenges
New feature Passlock now supports mailbox challenges, a server-side feature for building email one-time code flows without rebuilding the awkward security and lifecycle pieces yourself.
This is aimed at the cases teams keep needing even when passkeys are the long-term goal:
- verifying email ownership during signup
- passwordless login when passkeys are not available
- confirming a new email address before updating an account
What a mailbox challenge is
Section titled “What a mailbox challenge is”A mailbox challenge is an email verification flow with a little more structure than “generate a six-digit code and hope for the best”.
When you create a challenge, Passlock returns:
challengeIdto identify the challengesecretfor your app to keep server-sidecodefor you to deliver by emailmessage.htmlandmessage.textif you want ready-made email content
The user receives the code, enters it into your app, and your backend verifies the attempt using all three parts: challengeId, secret, and code.
That detail matters. The emailed code alone is not enough.
Why we built it
Section titled “Why we built it”Email one-time codes look simple until you try to ship them properly.
You need challenge expiry, retry handling, invalidation of older outstanding codes, rate limiting, consistent verification, and a way to reduce the risk of treating an intercepted code as sufficient proof on its own.
Mailbox challenges package those moving parts into a small API so you can focus on your product flow instead of rebuilding verification plumbing.
Typical flow
Section titled “Typical flow”The flow is straightforward:
- Your backend creates a mailbox challenge for a purpose like
signup,login, oremail-change. - Your app stores
challengeIdandsecretin a server-side session or HTTP-only cookie. - Passlock sends the email, or your mailer sends the code using Passlock’s rendered message or your own template.
- The user submits the code.
- Your backend verifies the challenge and checks the returned
purpose,email, and any expected local user context before completing the action.
If you need to restore an in-progress flow, you can also read a challenge without exposing the secret or code.
Using @passlock/server
Section titled “Using @passlock/server”If you are already using the Passlock server library, the basic shape looks like this:
import { createMailboxChallenge, verifyMailboxChallenge,} from "@passlock/server/unsafe";
const created = await createMailboxChallenge({ email: "jdoe@example.com", name: "Jane Doe", purpose: "login", invalidateOthers: true,}, { tenancyId: "myTenancyId", apiKey: "myApiKey" });
await savePendingLoginSession({ challengeId: created.challenge.challengeId, secret: created.challenge.secret,});
const pending = await readPendingLoginSession();
const verified = await verifyMailboxChallenge({ challengeId: pending.challengeId, secret: pending.secret, code: form.code,}, { tenancyId: "myTenancyId", apiKey: "myApiKey" });
if (verified.challenge.purpose !== "login") { throw new Error("Unexpected challenge purpose");}
await completeLoginForEmail(verified.challenge.email);The same feature is also available over raw HTTP if you are not using the JS/TS server library.
Want to send the email yourself?
Section titled “Want to send the email yourself?”Passlock will send the rendered email by default, but you can instead send via your own infrastructure.
When you pass sendEmail: false, Passlock creates the challenge and returns the same raw code and rendered message.html / message.text content without sending anything. You can send that rendered content through your own infrastructure, or generate your own template from the raw code.
Where mailbox challenges fit
Section titled “Where mailbox challenges fit”Mailbox challenges are not a replacement for passkeys. They cover a different part of the authentication surface.
Passkeys remain the stronger default for ongoing authentication. Mailbox challenges are useful when you need to prove mailbox ownership, offer a passwordless fallback, or verify account changes that are naturally email-centric.
In practice, many applications will use both:
- passkeys for primary authentication
- mailbox challenges for onboarding, recovery, or account-change verification