Email & password management
This pack adds account endpoints where signed-in users manage their own account as JSON. They update their name and email, and change their password, as a signed-in user against your existing authenticated routes.
- A pending email change is resent or reverted through the same profile endpoint, and the account keeps signing in with its confirmed address until then.
- A branded, MJML change-email message that links to your frontend and names both the old and the new address.
This pack extends the auth pack's User model and reuses its email-verification flow. Apply the auth pack first.
Apply the email & password management pack with Flow. Start a new session in your selected coding agent and execute the following slash command inside it.
After applying, Flow will make the following changes to your app.
-
Land the backend as shipped, from the profile and account-password controllers through the validators, the change-email mailer and template, and the resend branch.
-
Add the profile and password endpoints to your existing authenticated account group.
-
Run the tests, then drive the endpoints as a signed-in user and show you the result.
Apply the email & password management pack to your app by hand by working through the steps below in order. It builds on the auth pack, adding profile and password endpoints to your authenticated account group so signed-in users can update their name and email and change their password as JSON.
-
Confirm your auth foundation
This pack builds on the auth pack. Make sure your app already has it before continuing.
- Your User model composes
withAuthFinder(...)andwithManagedEmail(). - The email-verification flow is wired: the
account_activation_provisionmailer and theemailLayoutemail component exist, mail is configured, and the accountemail-verificationsroutes resolve. - The authenticated
accountgroup, undermiddleware.auth(), is registered.
- Your User model composes
Flow adapts the pack to your app, so the exact set of created and edited files depends on what you already have. This is the shape of an apply onto the API starter kit with the auth pack in place.
Configuration
The change-email link is backed by the withManagedEmail() mixin on the User model, the same mixin auth composes. It mints each verification token and stores it in a database table. Pass an options object to the mixin to change its defaults.
expiresIn
How long a change-email link stays valid, as a number of seconds or a time expression. Defaults to 1 day.
table
The table verification tokens are read from and written to. Defaults to email_verification_tokens.
tokenSecretLength
The length of the random secret behind each token. Defaults to 40.
export default class User extends compose(
UserSchema,
withAuthFinder(hash),
withManagedEmail({ expiresIn: '2 hours' })
) {}
Changing an email keeps the old one active
An account carries two addresses. email is the confirmed one that signs the user in, and unverifiedEmail holds a change still waiting on confirmation. Submitting a new email writes it to unverifiedEmail and sends the change-email message there, while the confirmed address goes on working for login until the new one is confirmed.
Submitting the current confirmed email while a change is pending reverts it. The pending address is cleared and no mail is sent, which is what cancelling a pending change relies on. Because the uniqueness check ignores the user's own row, resubmitting an unchanged email is never a duplicate.
A pending change can also be resent, throttled to one message a minute. Inside that window the request does nothing and answers as it would have, so the timing stays invisible.
A password change needs the current password
The change verifies the current password with a constant-time comparison before setting the new one. A wrong current password is rejected and nothing changes. Assigning the new password re-hashes it on save, so only the hash is ever stored.
1.0.0
Initial release.