Authentication

WildDuck provides a centralized authentication system used by all protocols (IMAP, POP3, SMTP) and the HTTP API.
Password Authentication
User passwords are hashed using pbkdf2 by default (SHA-256, 100,000 iterations, 16-byte salt). Password hashes are stored in the users collection in MongoDB.
WildDuck can also verify passwords stored in other formats — bcrypt, argon2, unixcrypt (SHA-512/SHA-256), MD5 crypt, and DES crypt. This is useful when migrating users from other mail systems. On successful login, passwords in legacy formats are automatically rehashed to the default algorithm (pbkdf2).
WildDuck optionally checks passwords against the Have I Been Pwned breached passwords database. If enabled, users will not be able to set passwords that appear in known data breaches.
Authentication Scopes
Each authentication attempt has a scope that determines what the credential grants access to:
| Scope | Usage |
|---|---|
| master | Full account access — only the main account password can be used for this scope |
| imap | IMAP protocol access |
| pop3 | POP3 protocol access |
| smtp | SMTP submission access |
When 2FA is enabled, the main account password is restricted to the "master" scope only. All protocol access (IMAP, POP3, SMTP) must use Application-Specific Passwords.
Two-Factor Authentication
TOTP (Time-based One-Time Passwords)
WildDuck generates random TOTP seed tokens that are encrypted using AES-256-GCM with a master password from the application configuration. The encrypted seed is stored in the user's database entry.
Setup flow:
POST /users/:user/2fa/totp/setup— generates a TOTP seed and returns a QR code- User scans QR code with an authenticator app
POST /users/:user/2fa/totp/enable— verifies a TOTP token and enables 2FA
Rate limiting: 6 invalid TOTP checks are allowed within a 180-second window before the account is temporarily locked.
WebAuthn / FIDO2
WildDuck supports WebAuthn/FIDO2 hardware security keys (e.g., YubiKey) as a second factor. This replaces the legacy U2F implementation.
Registration flow:
POST /users/:user/2fa/webauthn/register/start— begins FIDO2 registration, returns a challenge- User interacts with their security key
POST /users/:user/2fa/webauthn/register/complete— completes registration
Authentication flow:
POST /users/:user/2fa/webauthn/authenticate/start— begins authentication challenge- User interacts with their security key
POST /users/:user/2fa/webauthn/authenticate/complete— verifies the response
Multiple security keys can be registered per user. Keys can be removed via DELETE /users/:user/2fa/webauthn/:device.
Custom 2FA
WildDuck also supports plugging in custom 2FA mechanisms through its API.
Application-Specific Passwords (ASP)
When 2FA is enabled, users need Application-Specific Passwords to access IMAP, POP3, and SMTP. ASPs provide scoped access without exposing the master password.
Characteristics:
- 16-byte strings of lowercase random latin characters
- Whitespace is ignored (allows formatting as groups for readability)
- Hashed with pbkdf2 (same algorithm as user passwords)
- The first 4 characters are MD5-hashed for fast ASP detection during login
- Each ASP has a defined scope (e.g.,
["imap", "pop3"]) — the "master" scope is never allowed - ASPs can have a TTL (time-to-live) for temporary access
- Last-use tracking records when each ASP was last used
API endpoints:
GET /users/:user/asps— list application passwordsPOST /users/:user/asps— create a new ASP with specified scopesDELETE /users/:user/asps/:asp— revoke an application password
iOS Mobile Configuration
WildDuck can generate Apple .mobileconfig profile files for iOS/macOS email client auto-configuration. These profiles include the ASP credentials and server settings, making it easy for Apple device users to set up their email.
Rate Limiting
Authentication attempts are rate-limited to prevent brute-force attacks:
| Check | Limit | Window |
|---|---|---|
| Password | 12 failures | 120 seconds |
| TOTP | 6 failures | 180 seconds |
Successful authentication clears the rate limit counters for that account. The window starts from the first failed attempt.
Rate limiting is enforced via Redis counters with TTL-based expiration.