Authentication is one of the most critical parts of any web application. A poorly implemented login system can lead to security breaches, data theft, and user mistrust. While most frameworks provide built-in authentication, real-world applications often require custom rules to meet specific business and security needs.
In this article, we’ll discuss how to design a secure login system with custom validation rules, covering both best practices and practical implementation.
Why do We Need Custom Rules?
Default login systems usually check only for,
Valid username/email
Correct password
However, modern applications require more than that. Examples of custom rules include.
Password must meet complexity requirements (uppercase, lowercase, number, special character).
User must verify their email before logging in.
Accounts should be locked after multiple failed login attempts.
Login should be restricted by IP or device.
Users must accept the terms & conditions or complete their profile before accessing.
By applying such rules, you make your application much more complicated to attack.
Core Components of a Secure Login System
User Model: Stores login details.
Password Policies: Enforces strong password rules.
Account Lockout: Protects against brute-force attacks.
Multi-Factor Authentication (MFA): Adds an extra security layer.
Custom Validation Rules: Implements business-specific logic.
Example. Adding Custom Validation Rules
Let’s walk through some practical custom rules.
1. Password Policy Validation
We can create a custom validation attribute to enforce strong passwords.
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
public class StrongPasswordAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
return new ValidationResult("Password is required.");
string password = value.ToString();
if (password.Length < 8 ||
!Regex.IsMatch(password, "[A-Z]") ||
!Regex.IsMatch(password, "[a-z]") ||
!Regex.IsMatch(password, "[0-9]") ||
!Regex.IsMatch(password, "[^a-zA-Z0-9]"))
{
return new ValidationResult(
"Password must be at least 8 characters long and contain uppercase, lowercase, number, and special character."
);
}
return ValidationResult.Success;
}
}
Use it in your Login/Register Model.
public class LoginModel
{
[Required]
public string Username { get; set; }
[Required]
[DataType(DataType.Password)]
[StrongPassword]
public string Password { get; set; }
}
2. Account Lockout Rule
You can lock a user account after multiple failed login attempts.
public class LoginAttemptTracker
{
private static Dictionary<string, int> _failedAttempts = new Dictionary<string, int>();
public static bool IsLocked(string username)
{
return _failedAttempts.ContainsKey(username) && _failedAttempts[username] >= 3;
}
public static void RecordFailure(string username)
{
if (_failedAttempts.ContainsKey(username))
{
_failedAttempts[username]++;
}
else
{
_failedAttempts[username] = 1;
}
}
public static void ResetAttempts(string username)
{
if (_failedAttempts.ContainsKey(username))
{
_failedAttempts.Remove(username);
}
}
}
In your Controller.
[HttpPost]
public IActionResult Login(LoginModel model)
{
if (!ModelState.IsValid)
return View(model);
if (LoginAttemptTracker.IsLocked(model.Username))
{
ModelState.AddModelError("", "Account is locked due to multiple failed login attempts.");
return View(model);
}
// Check credentials (fake check for demo)
bool isValidUser = (model.Username == "harshit" && model.Password == "Pass@123");
if (!isValidUser)
{
LoginAttemptTracker.RecordFailure(model.Username);
ModelState.AddModelError("", "Invalid credentials.");
return View(model);
}
LoginAttemptTracker.ResetAttempts(model.Username);
return RedirectToAction("Dashboard");
}
3. Enforcing Email Verification
You may require users to verify their email before login.
public class User
{
public string Username { get; set; }
public string PasswordHash { get; set; }
public bool IsEmailVerified { get; set; }
}
During login
if (!user.IsEmailVerified)
{
ModelState.AddModelError("", "Please verify your email before logging in.");
return View(model);
}
Best Practices
Always hash & salt passwords (never store plain text).
Use HTTPS to secure login traffic.
Enable Two-Factor Authentication (2FA) for sensitive accounts.
Log login attempts for audit and monitoring.
Rate-limit login requests to prevent brute force attacks.
Conclusion
A secure login system is more than just checking a username and password. By implementing custom rules such as password policies, account lockout, and email verification, you can significantly increase the security of your application.
These practices not only protect your users but also build trust in your application’s security.