Jamesabira / Open-CV

0 stars 0 forks source link

Account lockout logic #4

Open Jamesabira opened 10 months ago

Jamesabira commented 10 months ago

Account lockout logic helps protect against brute force attacks by temporarily locking out an account after a certain number of failed login attempts. Implemented in Node.js with Express and MongoDB:

// Import necessary libraries
const express = require('express');
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);

// Create an Express application
const app = express();

// Middleware for parsing JSON and URL-encoded data
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Set up sessions with MongoDB as the store
app.use(session({
  secret: 'your-secret-key',
  resave: false,
  saveUninitialized: true,
  store: new MongoStore({ mongooseConnection: mongoose.connection }),
}));

// Connect to the MongoDB database
mongoose.connect('mongodb://localhost/login-register-system', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useCreateIndex: true,
});

// Define a User model
const User = mongoose.model('User', {
  firstName: String,
  lastName: String,
  email: String,
  phoneNumber: String,
  username: { type: String, unique: true },
  password: String,
  failedLoginAttempts: { type: Number, default: 0 },
  isLockedOut: { type: Boolean, default: false },
  lockoutUntil: { type: Date, default: Date.now },
});

// Middleware to hash passwords before saving to the database
User.pre('save', async function(next) {
  if (!this.isModified('password')) {
    return next();
  }
  try {
    const hash = await bcrypt.hash(this.password, 10);
    this.password = hash;
    next();
  } catch (error) {
    next(error);
  }
});

// Login route
app.post('/login', async (req, res) => {
  // Handle user login
  const { username, password } = req.body;

  try {
    const user = await User.findOne({ username });
    if (!user) {
      return res.status(401).json({ error: 'Invalid username or password.' });
    }

    if (user.isLockedOut && user.lockoutUntil > Date.now()) {
      return res.status(403).json({ error: 'Account locked. Try again later.' });
    }

    const isValidPassword = await bcrypt.compare(password, user.password);

    if (isValidPassword) {
      // Reset failed login attempts upon successful login
      user.failedLoginAttempts = 0;
      return res.json({ message: 'Login successful.' });
    } else {
      user.failedLoginAttempts++;
      if (user.failedLoginAttempts >= 3) {
        // Lock the account for 1 minute
        user.isLockedOut = true;
        user.lockoutUntil = new Date(Date.now() + 60000);
        return res.status(403).json({ error: 'Account locked. Try again later.' });
      }
      await user.save();
      return res.status(401).json({ error: 'Invalid username or password.' });
    }
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: 'Login failed.' });
  }
});

// Start the server
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

step-by-step explanation of the account lockout logic:

  1. When a user attempts to log in, we check if their account is locked (user.isLockedOut) and if the lockout period (user.lockoutUntil) has not yet expired.

  2. If the account is locked, we return a 403 Forbidden response and inform the user to try again later.

  3. If the account is not locked, we proceed to validate the password. If the login is successful, we reset the failed login attempts to 0.

  4. If the login fails, we increment the failed login attempts counter. If it reaches 3 or more, we lock the account and set the lockout period to 1 minute.

  5. We save the user's data in the database after each login attempt or account lockout.