Skip to main content

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)

Package Information

  • 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 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
});
argon2Enabled
boolean
default:"false"
Enable Argon2 password hashing instead of bcrypt
argon2Type
string
default:"argon2id"
Argon2 variant: argon2i (optimized for password hashing), argon2d (optimized for cryptocurrencies), or argon2id (hybrid, recommended)
argon2TimeCost
number
default:"2"
Number of iterations (higher = more secure, slower)
argon2MemoryCost
number
default:"19456"
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 });
}

Password Storage Format

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