Implement Email/Password Authentication in Meteor with accounts‑password
Step‑by‑step guide to add secure email/password login to a Meteor app, covering package setup, UI integration, server configuration, testing, and common pitfalls.
04 Mar 2026, 16:30 UTC

Desired Outcome
By the end of this guide you will have a fully functional email/password authentication system in your Meteor application. Users can register, verify their email, log in, and log out, with session persistence across page reloads. The system uses Meteor’s built‑in bcrypt hashing and optional UI components from accounts‑ui.
Prerequisites
- Node.js 18.x or newer (Meteor 2.x+ requires Node 18).
- Meteor 2.x or later installed globally:
curl https://install.meteor.com | sh. - Basic familiarity with Meteor project structure and Blaze templates.
- Optional: an SMTP service such as Mailgun, SendGrid, or Gmail for email verification.
Setup Steps
- Create or navigate to your Meteor project
# If you have no project yet meteor create myapp cd myapp # If you already have a project cd path/to/your/appRun
meteor listto confirm the version. The guide assumes Meteor 2.8 or newer. - Add authentication packages
meteor add accounts-password accounts-uiThese packages provide server‑side account handling and optional login UI helpers.
- Add a login UI (optional)
Insert
{{> loginButtons}}into a Blaze template, e.g.,client/main.html:<body> <div class="container"> {{> loginButtons}} </div> </body>accounts‑ui will automatically render registration and login forms.
- Custom login form (optional)
If you prefer a bespoke form, create
client/login.htmlandclient/login.js:<template name="loginForm"> <form id="loginForm"> <input type="email" name="email" placeholder="Email" required> <input type="password" name="password" placeholder="Password" required> <button type="submit">Login</button> </form> </template>And in
client/login.js:Template.loginForm.events({ 'submit #loginForm'(event) { event.preventDefault(); const email = event.target.email.value; const password = event.target.password.value; Meteor.loginWithPassword(email, password, (err) => { if (err) alert(err.reason); }); } }); - Protect routes
In
client/main.jsor a router file, guard pages that require authentication:if (!Meteor.userId()) { // redirect to login or show a message } - Server‑side configuration
Create
server/main.js(or any file inside a server folder) with the following:import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; Meteor.startup(() => { // Email templates Accounts.emailTemplates.siteName = "My Meteor App"; Accounts.emailTemplates.from = "[contact removed]"; // URL for password reset Accounts.urls.resetPassword = (token) => { return `https://myapp.com/reset-password?token=${token}`; }; // Optional: enforce email verification before login Accounts.config({ sendVerificationEmail: true, forbidClientAccountCreation: false }); });Replace placeholders with your domain and email address.
- Configure SMTP for email verification
Set Meteor settings (JSON file) or environment variables. Example
settings.json:{ "mailUrl": "smtp://username:[contact removed]:587" }Run the app with:
meteor --settings settings.jsonAlternatively, set
MAIL_URLas an environment variable.
Testing the Flow
- Register a new user
Navigate to
http://localhost:3000, use the login UI to sign up. Verify that the console logs a line likeCreated user #1234. - Email verification
Check the inbox of the address used. The verification link should point to
https://myapp.com/verify-email?token=.... - Login
After verifying, log in again. In the browser console, run
Meteor.userId()– it should return a non‑null string. - Session persistence
Reload the page;
Meteor.userId()should still return the same ID. - Logout
Execute
Meteor.logout()or click a logout button. Verify thatMeteor.userId()becomesnull. - API testing
Using curl:
curl -X POST http://localhost:3000/login -d "username=[contact removed]" -d "password=secret" -c cookies.txtA successful response contains a JSON object with a
tokenfield.
Common Pitfalls
- Missing SMTP configuration
Without
MAIL_URL, verification emails will not be sent. Check server logs for errors likeCannot send email – no mailUrl set. - Wrong domain in URL templates
Ensure
Accounts.urls.resetPasswordpoints to your actual public domain; otherwise, users will receive broken links. - Client‑side account creation disabled
If you set
forbidClientAccountCreation: true, users cannot register via the UI. Use server‑sideAccounts.createUserinstead. - Using outdated Meteor version
Accounts‑password relies on Meteor 2.x features. Running Meteor 1.4 or older will cause package conflicts.
Recovery Options
- Reset a user’s password
On the server console, run:
Meteor.users.update({ _id: "userId" }, { $set: { "services.password.bcrypt": "" } });Generate a bcrypt hash with a tool like
bcryptjsor an online generator. - Delete a user
Use the
Meteor.users.removemethod:Meteor.users.remove({ _id: "userId" });Only run this on the server or via a protected admin route.
- Re‑enable client account creation
Set
Accounts.config({ forbidClientAccountCreation: false });inserver/main.jsand restart the app.
Wrap‑Up
With the steps above you have a secure, email/password authentication system in place. Remember to keep your SMTP credentials out of version control, monitor server logs for authentication events, and test both the UI flow and the underlying API endpoints. For production deployments, consider adding rate limiting, CAPTCHAs, and multi‑factor authentication to further harden the login process.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.