Account lockout is an important security feature that protects user accounts from brute-force attacks and repeated unauthorized login attempts. Without account lockout, an attacker could try thousands of password combinations until they eventually guess the correct one.

Most modern systems (Google, Microsoft, GitHub, banking apps) use account lockout to:

In this article, you will learn how to build a complete Account Lockout System using:

This article uses simple language and includes complete practical code for beginners.

What You Will Build

By the end of this tutorial, you will implement:

  1. Tracking failed login attempts in the database

  2. Automatic lockout after a certain number of failed attempts

  3. Auto unlock after a specified time (e.g., 5 or 15 minutes)

  4. Error messages guiding the user

  5. Angular UI updates

  6. Backend checks to enforce lockout

  7. Optional features like permanent lockout or admin unlock

Why Account Lockout Matters

Attackers use automated tools to attempt thousands of password guesses per minute. Without account lockout, they could easily breach weak or reused passwords.

Account lockout helps by:

It is a proven layer of security and is recommended in OWASP and NIST guidelines.

System Architecture Overview

The system will use these fields in the User table:

Flow

  1. User enters email and password

  2. Backend checks if the account is locked

  3. If locked, deny login

  4. If credentials are wrong, increase FailedLoginAttempts

  5. If attempts reach the limit, set lockout time (example: 5 minutes)

  6. If credentials are correct, reset FailedLoginAttempts

  7. Allow login

This protects your system from brute-force attacks.

Part 1: Backend Implementation (ASP.NET Core)

Step 1: Update User Model

File: Models/User.cs

public class User
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }

    public int FailedLoginAttempts { get; set; } = 0;
    public DateTime? LockoutEnd { get; set; }
}

Step 2: Account Lockout Configuration

Define your lockout policy:

public class LockoutSettings
{
    public const int MaxFailedAttempts = 5;  
    public const int LockoutMinutes = 5;    
}

Step 3: Modify Login Endpoint

File: AuthController.cs

[HttpPost("login")]
public async Task<IActionResult> Login(LoginDto request)
{
    var user = await _context.Users.FirstOrDefaultAsync(u => u.Email == request.Email);
    if (user == null)
        return BadRequest(new { Message = "Invalid email or password" });

    // Check if locked
    if (user.LockoutEnd.HasValue && user.LockoutEnd.Value > DateTime.Now)
    {
        var remaining = user.LockoutEnd.Value - DateTime.Now;

        return BadRequest(new 
        { 
            Message = $"Account locked. Try again in {remaining.Minutes} minutes and {remaining.Seconds} seconds." 
        });
    }

    // Check password
    if (!BCrypt.Net.BCrypt.Verify(request.Password, user.PasswordHash))
    {
        user.FailedLoginAttempts++;

        if (user.FailedLoginAttempts >= LockoutSettings.MaxFailedAttempts)
        {
            user.LockoutEnd = DateTime.Now.AddMinutes(LockoutSettings.LockoutMinutes);
            await _context.SaveChangesAsync();

            return BadRequest(new 
            { 
                Message = $"Account locked due to many failed attempts. Try again after {LockoutSettings.LockoutMinutes} minutes." 
            });
        }

        await _context.SaveChangesAsync();

        return BadRequest(new 
        { 
            Message = $"Invalid password. Attempts remaining: {LockoutSettings.MaxFailedAttempts - user.FailedLoginAttempts}" 
        });
    }

    // Successful login: reset attempts
    user.FailedLoginAttempts = 0;
    user.LockoutEnd = null;
    await _context.SaveChangesAsync();

    var token = _jwtService.GenerateToken(user);

    return Ok(new { Token = token });
}

What this code does

Part 2: SQL Table Update

Update your Users table by adding the new fields:

ALTER TABLE Users 
ADD FailedLoginAttempts INT DEFAULT 0;

ALTER TABLE Users 
ADD LockoutEnd DATETIME NULL;

Part 3: Frontend Implementation (Angular)

Step 1: Modify Login Component

login.component.ts

submit() {
  this.auth.login(this.model).subscribe({
    next: res => {
      this.auth.saveToken(res.token);
      this.router.navigate(['/dashboard']);
    },
    error: err => {
      this.message = err.error.message;
    }
  });
}

Step 2: Display Messages in Template

login.component.html

<h2>Login</h2>

<input type="text" [(ngModel)]="model.email" placeholder="Email" />
<input type="password" [(ngModel)]="model.password" placeholder="Password" />

<button (click)="submit()">Login</button>

<p style="color: red">{{ message }}</p>

Angular will now show:

Part 4: How the Lockout System Works

Example policy

Example scenario

User enters wrong password:

After lockout ends:

Part 5: Additional Security Improvements

You can enhance the system by adding:

1. Permanent Lockout After Multiple Lockouts

If user triggers 3 lockouts → Permanently lock until admin resets.

2. Email Notification on Lockout

Send alert:
"Your account was locked due to suspicious activity."

3. IP-Based Lockout

Prevent brute-force attacks from a specific IP.

4. CAPTCHA Before Login

After 3 failures → show CAPTCHA (e.g., Google reCAPTCHA).

5. Log Every Failed Attempt

Store IP, browser, timestamp in a separate table.

6. Admin Dashboard to Unlock Users

Allow support staff to manually reset lockout.

If you want any of these features, I can create a separate article with full code.

Part 6: Testing the Lockout System

Step 1

Try logging in with wrong password 5 times.

Expected behavior:

Step 2

Try logging in during the lockout time.

Expected behavior:

Step 3

Wait for lockout duration, then login with correct password.

Expected behavior:

Conclusion

You have now built a complete, secure Account Lockout System using:

Account lockout is a powerful protection against brute-force and automated login attacks. Every serious application should implement it to improve security and protect user accounts.