> ## 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.

# accounts-password

> Password authentication for Meteor user accounts

The `accounts-password` package adds secure password-based authentication to Meteor applications, supporting both bcrypt and Argon2 password hashing.

## Installation

```bash theme={null}
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:

```javascript theme={null}
Accounts.config({
  bcryptRounds: 10 // Default rounds (higher = more secure, slower)
});
```

### Argon2 (Recommended)

<Info>
  Argon2 is the modern password hashing algorithm that won the Password Hashing Competition in 2015. It's recommended for new applications.
</Info>

```javascript theme={null}
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
});
```

<ParamField path="argon2Enabled" type="boolean" default="false">
  Enable Argon2 password hashing instead of bcrypt
</ParamField>

<ParamField path="argon2Type" type="string" default="argon2id">
  Argon2 variant: `argon2i` (optimized for password hashing), `argon2d` (optimized for cryptocurrencies), or `argon2id` (hybrid, recommended)
</ParamField>

<ParamField path="argon2TimeCost" type="number" default="2">
  Number of iterations (higher = more secure, slower)
</ParamField>

<ParamField path="argon2MemoryCost" type="number" default="19456">
  Memory usage in KiB (higher = more secure, more memory)
</ParamField>

## User Creation

### Client-Side

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
Meteor.loginWithPassword(
  'user@example.com',
  'password123',
  (error) => {
    if (error) {
      console.error('Login failed:', error.reason);
    } else {
      console.log('Logged in successfully');
    }
  }
);
```

### With Username

```javascript theme={null}
Meteor.loginWithPassword(
  'johndoe',
  'password123',
  (error) => {
    if (error) {
      console.error('Login failed:', error.reason);
    }
  }
);
```

### With Username or Email

```javascript theme={null}
Meteor.loginWithPassword(
  { username: 'johndoe' },
  'password123'
);

Meteor.loginWithPassword(
  { email: 'john@example.com' },
  'password123'
);
```

## Password Management

### Change Password

```javascript theme={null}
// 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)

```javascript theme={null}
// Server: Admin sets user password
await Accounts.setPasswordAsync(userId, 'newPassword', {
  logout: true // Logout other sessions
});
```

### Password Reset

#### Request Reset

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// Server: Send verification email
await Accounts.sendVerificationEmail(userId, email);
```

### Verify Email with Token

```javascript theme={null}
// 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

```javascript theme={null}
// Server: Configure automatic verification emails
Accounts.config({
  sendVerificationEmail: true
});
```

## Email Templates

Customize the emails sent by accounts-password:

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// Server: Disable client-side account creation
Accounts.config({
  forbidClientAccountCreation: true
});
```

### Token Expiration

```javascript theme={null}
Accounts.config({
  passwordResetTokenExpirationInDays: 3,
  passwordEnrollTokenExpirationInDays: 30
});
```

### Ambiguous Error Messages

<Info>
  Enable ambiguous errors to prevent user enumeration attacks.
</Info>

```javascript theme={null}
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:

```javascript theme={null}
// 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

```javascript theme={null}
// 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:

```javascript theme={null}
// 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: [...]
    }
  }
}
```

<Warning>
  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.
</Warning>

## Example: Complete Registration Flow

```javascript theme={null}
// 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

* [packages/accounts-password](https://github.com/meteor/meteor/tree/devel/packages/accounts-password)
