bun-smtp supports four SASL authentication methods: PLAIN, LOGIN, CRAM-MD5, and XOAUTH2. You can control which methods are available and enforce authentication requirements through configuration options.
Control authentication behavior with these options:
const server = new SMTPServer({ authMethods: ["PLAIN", "LOGIN", "CRAM-MD5", "XOAUTH2"], allowInsecureAuth: false, // require TLS before AUTH (default) authOptional: false, // require AUTH (default)});
By default, allowInsecureAuth is false, meaning clients must complete STARTTLS before authenticating. This prevents credentials from being transmitted over unencrypted connections.
Both PLAIN and LOGIN methods deliver credentials in the same format. The only difference is the wire protocol — both provide username and password fields.
1
Configure the server
Enable PLAIN and LOGIN in your server configuration:
const server = new SMTPServer({ authMethods: ["PLAIN", "LOGIN"], onAuth(auth, session, callback) { // Handle authentication },});
CRAM-MD5 provides challenge-response authentication without transmitting the password. The server sends a challenge, and the client responds with an HMAC-MD5 digest.
CRAM-MD5 requires you to have access to the plaintext password (or a reversibly encrypted version) to verify the response. If you only store password hashes, CRAM-MD5 won’t work.
const server = new SMTPServer({ authMethods: ["CRAM-MD5"], onAuth(auth, session, callback) { if (auth.method !== "CRAM-MD5") { return callback(new Error("Unsupported method")); } // Look up the stored password for this user const storedPassword = lookupPassword(auth.username); if (auth.validatePassword(storedPassword)) { callback(null, { user: auth.username }); } else { callback(new Error("Invalid credentials")); } },});
When authentication fails, you can pass a data object in the response to trigger an XOAUTH2 error challenge. This allows the client to understand why authentication failed and potentially retry with a refreshed token.
Whatever you pass as user in the success response is stored on session.user for the rest of the connection. You can pass any value — a string, number, or object:
String
Object
callback(null, { user: "user@example.com" });// Later in onData:onData(stream, session, callback) { console.log(session.user); // "user@example.com"}