Authentication and Security
Make your SMTP server secure and add user authentication.
Security is Top Priority
Zetian supports modern security standards: TLS 1.2/1.3, STARTTLS, multiple authentication mechanisms and customizable auth handlers.
Basic Authentication
Authentication with simple username and password:
using Zetian;
using Zetian.Authentication;
// Simple username/password authentication
var server = new SmtpServerBuilder()
.Port(587)
.RequireAuthentication()
.AllowPlainTextAuthentication()
.SimpleAuthentication("admin", "password123")
.Build();
// For multiple users
var users = new Dictionary<string, string>
{
["admin"] = "admin123",
["user1"] = "pass123",
["demo"] = "demo123"
};
var server = new SmtpServerBuilder()
.Port(587)
.RequireAuthentication()
.AllowPlainTextAuthentication()
.AuthenticationHandler(async (username, password) =>
{
if (users.TryGetValue(username, out var correctPassword) &&
password == correctPassword)
{
return AuthenticationResult.Succeed(username);
}
return AuthenticationResult.Fail("Invalid credentials");
})
.Build();
Custom Authentication
Integration with database or external systems:
// Custom authentication handler with database
public class DatabaseAuthHandler
{
private readonly IUserRepository _userRepository;
public DatabaseAuthHandler(IUserRepository userRepository)
{
_userRepository = userRepository;
}
// AuthenticationHandler delegate signature: (string?, string?) => Task<AuthenticationResult>
public async Task<AuthenticationResult> AuthenticateAsync(
string? username,
string? password)
{
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
return AuthenticationResult.Fail("Username and password required");
}
// Get user from database
var user = await _userRepository.GetByUsernameAsync(username);
if (user == null)
{
return AuthenticationResult.Fail("User not found");
}
// Check password hash (use BCrypt in production)
// Install: dotnet add package BCrypt.Net-Next
// if (!BCrypt.Net.BCrypt.Verify(password, user.PasswordHash))
if (password != user.PasswordHash) // Don't use plain text in production!
{
return AuthenticationResult.Fail("Invalid password");
}
// Is account active?
if (!user.IsActive)
{
return AuthenticationResult.Fail("Account is disabled");
}
// Successful authentication
// Note: AuthenticationResult.Succeed only takes username
return AuthenticationResult.Succeed(username);
}
}
// Usage
var userRepository = new YourUserRepository(); // Your database implementation
var authHandler = new DatabaseAuthHandler(userRepository);
var server = new SmtpServerBuilder()
.Port(587)
.RequireAuthentication()
.AllowPlainTextAuthentication() // For testing without TLS
.AuthenticationHandler(authHandler.AuthenticateAsync)
.Build();
Database Integration
SQL, NoSQL or any data source
Password Hashing
BCrypt, Argon2 or other hash algorithms
Account Status
Active/passive account check, role-based access
TLS/SSL Security
TLS/SSL configuration for encrypted connections:
// Secure connection with TLS/SSL
var server = new SmtpServerBuilder()
.Port(587)
.Certificate("certificate.pfx", "certificate_password")
.RequireSecureConnection() // TLS required
.RequireAuthentication() // Auth required
.Build();
// Allow plain text authentication (for testing without TLS)
var testServer = new SmtpServerBuilder()
.Port(587)
.RequireAuthentication()
.AllowPlainTextAuthentication() // Allow auth without TLS
.SimpleAuthentication("admin", "password")
.Build();
// SSL/TLS with certificate object
var cert = new X509Certificate2("certificate.pfx", "password");
var server = new SmtpServerBuilder()
.Port(465)
.Certificate(cert)
.RequireSecureConnection()
.RequireAuthentication()
.Build();
STARTTLS (Port 587)
Initially plain text, transition to encryption with STARTTLS command.
- • Recommended for modern email clients
- • Backward compatible
- • Flexible security
Implicit TLS (Port 465)
Fully encrypted from the beginning of connection.
- • Maximum security
- • Old standard but still in use
- • Known as SMTPS
Authentication Mechanisms
Supported authentication mechanisms and events:
// Different authentication mechanisms
var server = new SmtpServerBuilder()
.Port(587)
.RequireAuthentication()
.AllowPlainTextAuthentication() // Allow auth without TLS
// Add authentication mechanisms
.AddAuthenticationMechanism("PLAIN") // Default
.AddAuthenticationMechanism("LOGIN") // Legacy support
// Custom authentication handler for all mechanisms
.AuthenticationHandler(async (username, password) =>
{
// Your authentication logic here
return AuthenticationResult.Succeed(username);
})
.Build();
// Authentication tracking via session events
server.SessionCompleted += (sender, e) =>
{
if (e.Session.IsAuthenticated)
{
Console.WriteLine($"User session completed: {e.Session.AuthenticatedIdentity}");
}
};
// Message from authenticated user
server.MessageReceived += (sender, e) =>
{
if (e.Session.IsAuthenticated)
{
Console.WriteLine($"Message from authenticated user: {e.Session.AuthenticatedIdentity}");
}
};
PLAIN
Base64 encoded username and password
LOGIN
Legacy type, Microsoft Outlook compatible
CRAM-MD5
Challenge-response based (optional)
SMTP Authentication Flow
A typical SMTP authentication session:
// Authentication flow
// 1. Client: Sends AUTH PLAIN command
// 2. Server: Continues with 334 response
// 3. Client: Sends Base64 encoded credentials
// 4. Server: Validates and responds
// Example SMTP session:
C: EHLO client.example.com
S: 250-smtp.example.com
S: 250-AUTH PLAIN LOGIN
S: 250 STARTTLS
C: STARTTLS
S: 220 Ready to start TLS
[TLS handshake]
C: EHLO client.example.com
S: 250-smtp.example.com
S: 250 AUTH PLAIN LOGIN
C: AUTH PLAIN
S: 334
C: AGFkbWluAHBhc3N3b3JkMTIz
S: 235 Authentication successful
// Now can send mail
C: MAIL FROM:<[email protected]>
S: 250 OK
Security Tip
PLAIN and LOGIN mechanisms encode passwords with Base64 but do not encrypt. Therefore, they must be used with TLS/SSL.