Enumeration-safe responses
These routes answer an email that has an account and an email that does not with the same status, the same body shape, and the same headers. The behaviour is always on. There is no option to turn it off.
| Route | Same response | Unknown-account work |
|---|---|---|
POST /register | Empty 200 | Notifies the owner when the email already has an account |
POST /forgotPassword | Empty 200 | Issues a password-reset token only when the account exists and the email is confirmed |
POST /resendConfirmationEmail | The signed-in user's resend result | The body email is ignored |
POST /emailCode/send | 200 with expiresInSeconds and resendAfterSeconds | Issues a code only when the account can receive one |
POST /passkeys/register/options | 200 options | Builds creation options |
POST /register does not return DuplicateUserName or DuplicateEmail. A valid password for an existing email does not create a second account. 4.0 sends IAccountSecurityEmailSender<TUser>.SendRegistrationAttemptedAsync to that owner. The attempted password is not in the message. AddPasswordSignUpEndpoints, AddAccountManagementEndpoints, and AddJwtEndpoints register LoggingAccountSecurityEmailSender<TUser> when no sender is registered. That default logs the attempt and does not send mail. Register an implementation to deliver it. Startup logs a warning while the logging sender is still the registration.
Invalid email and password-rule failures stay 400. Those bodies match whether or not the email is registered.
The unknown-account branch runs the same class of work as the account branch, so the two requests take a similar amount of time. Queue confirmation, reset, and registration-attempt mail and return from the sender. A sender that waits on SMTP makes a known address slower than an unknown one.
POST /resendConfirmationEmail still resends for the signed-in user. An anonymous caller cannot use it to ask whether an address is registered.
Clients keep one check-your-email screen for a new registration and a taken email. That empty 200 was already the 3.4.1 register response. See Upgrade to 4.0.
The final passkey register step still reveals a taken email once a valid attestation has been made. POST /passkeys/register/options returns the same options for a known email and an unknown email. POST /passkeys/register checks the email only after attestation succeeds. A taken email then returns 400 with detail Unable to complete registration. A new email continues and creates the user.