Documentation Index
Fetch the complete documentation index at: https://mintlify.com/meteor/meteor/llms.txt
Use this file to discover all available pages before exploring further.
The accounts-password package adds secure password-based authentication to Meteor applications, supporting both bcrypt and Argon2 password hashing.
Installation
meteor add accounts-password
This package automatically includes accounts-base.
Overview
The accounts-password package provides:
- Secure password hashing with bcrypt or Argon2
- User creation with email/username and password
- Password reset and change functionality
- Email verification
- Enrollment emails for new users
- Account lockout after failed attempts (with rate limiting)
- Version: 3.2.2
- Summary: Password support for accounts
- Dependencies:
accounts-base, sha, email, random, check
- NPM packages:
bcrypt@5.0.1, argon2@0.41.1
Password Hashing
Bcrypt (Default)
By default, passwords are hashed using bcrypt:
Accounts.config({
bcryptRounds: 10 // Default rounds (higher = more secure, slower)
});
Argon2 (Recommended)
Argon2 is the modern password hashing algorithm that won the Password Hashing Competition in 2015. It’s recommended for new applications.
Accounts.config({
argon2Enabled: true,
argon2Type: 'argon2id', // 'argon2i', 'argon2d', or 'argon2id'
argon2TimeCost: 2, // Number of iterations
argon2MemoryCost: 19456, // Memory in KiB (default ~19MB)
argon2Parallelism: 1 // Degree of parallelism
});
Enable Argon2 password hashing instead of bcrypt
Argon2 variant: argon2i (optimized for password hashing), argon2d (optimized for cryptocurrencies), or argon2id (hybrid, recommended)
Number of iterations (higher = more secure, slower)
Memory usage in KiB (higher = more secure, more memory)
User Creation
Client-Side
// Create user with email
Accounts.createUser({
email: 'user@example.com',
password: 'securePassword123',
profile: { name: 'Jane Doe' }
}, (error) => {
if (error) {
console.error('Error creating account:', error.reason);
} else {
console.log('Account created and logged in');
}
});
// Create user with username
Accounts.createUser({
username: 'johndoe',
email: 'john@example.com',
password: 'securePassword123'
});
Server-Side
// Create user programmatically
const userId = await Accounts.createUserAsync({
email: 'admin@example.com',
password: 'initialPassword',
profile: { name: 'Admin User', role: 'admin' }
});
console.log('Created user:', userId);
Login
With Email
Meteor.loginWithPassword(
'user@example.com',
'password123',
(error) => {
if (error) {
console.error('Login failed:', error.reason);
} else {
console.log('Logged in successfully');
}
}
);
With Username
Meteor.loginWithPassword(
'johndoe',
'password123',
(error) => {
if (error) {
console.error('Login failed:', error.reason);
}
}
);
With Username or Email
Meteor.loginWithPassword(
{ username: 'johndoe' },
'password123'
);
Meteor.loginWithPassword(
{ email: 'john@example.com' },
'password123'
);
Password Management
Change Password
// Client: User changes their own password
Accounts.changePassword(
'oldPassword',
'newPassword',
(error) => {
if (error) {
console.error('Password change failed:', error.reason);
} else {
console.log('Password changed successfully');
}
}
);
Set Password (Server)
// Server: Admin sets user password
await Accounts.setPasswordAsync(userId, 'newPassword', {
logout: true // Logout other sessions
});
Password Reset
Request Reset
// Client: Request password reset email
Accounts.forgotPassword(
{ email: 'user@example.com' },
(error) => {
if (error) {
console.error('Error:', error.reason);
} else {
console.log('Password reset email sent');
}
}
);
Reset with Token
// Client: Reset password using token from email
Accounts.resetPassword(
token,
'newPassword',
(error) => {
if (error) {
console.error('Reset failed:', error.reason);
} else {
console.log('Password reset successful');
}
}
);
Email Verification
Send Verification Email
// Server: Send verification email
await Accounts.sendVerificationEmail(userId, email);
Verify Email with Token
// Client: Verify email using token from email
Accounts.verifyEmail(token, (error) => {
if (error) {
console.error('Verification failed:', error.reason);
} else {
console.log('Email verified successfully');
}
});
Auto-send on Registration
// Server: Configure automatic verification emails
Accounts.config({
sendVerificationEmail: true
});
Email Templates
Customize the emails sent by accounts-password:
// Server: Customize email templates
Accounts.emailTemplates.siteName = 'My App';
Accounts.emailTemplates.from = 'My App <noreply@myapp.com>';
// Enrollment email (for new users)
Accounts.emailTemplates.enrollAccount.subject = (user) => {
return `Welcome to ${Accounts.emailTemplates.siteName}, ${user.profile.name}`;
};
Accounts.emailTemplates.enrollAccount.text = (user, url) => {
return `Welcome! Click this link to activate your account:\n${url}`;
};
// Password reset email
Accounts.emailTemplates.resetPassword.subject = (user) => {
return 'Password reset request';
};
Accounts.emailTemplates.resetPassword.html = (user, url) => {
return `
<h1>Password Reset</h1>
<p>Hi ${user.username || user.emails[0].address},</p>
<p>Click the link below to reset your password:</p>
<a href="${url}">Reset Password</a>
`;
};
// Email verification
Accounts.emailTemplates.verifyEmail.subject = (user) => {
return 'Verify your email address';
};
Accounts.emailTemplates.verifyEmail.text = (user, url) => {
return `Click this link to verify your email: ${url}`;
};
User Enrollment
// Server: Create user and send enrollment email
const userId = await Accounts.createUserAsync({
email: 'newuser@example.com',
profile: { name: 'New User' }
// No password set yet
});
// Send enrollment email with password setup link
await Accounts.sendEnrollmentEmail(userId);
Security Configuration
Restrict Account Creation
// Server: Disable client-side account creation
Accounts.config({
forbidClientAccountCreation: true
});
Token Expiration
Accounts.config({
passwordResetTokenExpirationInDays: 3,
passwordEnrollTokenExpirationInDays: 30
});
Ambiguous Error Messages
Enable ambiguous errors to prevent user enumeration attacks.
Accounts.config({
ambiguousErrorMessages: true
});
// Instead of "User not found" or "Incorrect password",
// users see "Username or password incorrect"
Rate Limiting
The package automatically uses ddp-rate-limiter to prevent brute force attacks:
// Server: Customize rate limits (optional)
const { DDPRateLimiter } = require('meteor/ddp-rate-limiter');
// Limit login attempts
DDPRateLimiter.addRule({
type: 'method',
name: 'login',
connectionId() { return true; }
}, 5, 60000); // 5 attempts per minute
Password Validation
// Server: Add password strength requirements
Accounts.validateNewUser((user) => {
const password = user.services?.password;
// Check if password exists (might be OAuth user)
if (!password) {
return true;
}
// You can't access the plain password here,
// but you can validate during createUser:
return true;
});
// Client: Validate before sending
function createUserWithValidation(email, password, profile) {
// Validate password strength
if (password.length < 8) {
throw new Error('Password must be at least 8 characters');
}
if (!/[A-Z]/.test(password)) {
throw new Error('Password must contain uppercase letter');
}
if (!/[0-9]/.test(password)) {
throw new Error('Password must contain a number');
}
return Accounts.createUser({ email, password, profile });
}
Passwords are stored in the user document:
// User document structure
{
_id: "userId",
emails: [
{ address: "user@example.com", verified: true }
],
services: {
password: {
bcrypt: "$2b$10$...", // Bcrypt hash (legacy)
argon2: "$argon2id$..." // Argon2 hash (if enabled)
},
email: {
verificationTokens: [...],
reset: [...]
}
}
}
Passwords are automatically hashed before storage. The plain password is SHA-256 hashed on the client before transmission, then bcrypt/argon2 hashed on the server.
Example: Complete Registration Flow
// Server: Configure accounts
Accounts.config({
sendVerificationEmail: true,
forbidClientAccountCreation: false,
argon2Enabled: true
});
// Validate new users
Accounts.validateNewUser((user) => {
if (user.emails && user.emails[0]) {
const email = user.emails[0].address;
if (!email.match(/^[^@]+@[^@]+\.[^@]+$/)) {
throw new Meteor.Error('invalid-email', 'Invalid email format');
}
}
return true;
});
// Add creation timestamp
Accounts.onCreateUser((options, user) => {
user.createdAt = new Date();
if (options.profile) {
user.profile = options.profile;
}
return user;
});
// Client: Register user
Accounts.createUser({
email: 'user@example.com',
password: 'SecurePass123!',
profile: { name: 'Alice Smith' }
}, (error) => {
if (error) {
alert('Registration failed: ' + error.reason);
} else {
alert('Account created! Please check your email to verify.');
}
});
Source Code