account lockout
This commit is contained in:
@@ -27,8 +27,8 @@ export default function LoginView({ onSignup }: { onSignup: () => void }) {
|
||||
try {
|
||||
const response = await http.post("/User/login", loginRequest);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error("Login failed");
|
||||
if (response.status === 401) {
|
||||
throw new Error("Invalid email or password.");
|
||||
}
|
||||
|
||||
const { token } = response.data;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
import { toast } from "react-toastify";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
|
||||
interface SessionTimeoutProps {
|
||||
@@ -47,11 +47,7 @@ const SessionTimeout: React.FC<SessionTimeoutProps> = ({
|
||||
return () => clearInterval(interval);
|
||||
}, [lastActivityTime, timeout, onLogout, notified]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToastContainer />
|
||||
</>
|
||||
);
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export default SessionTimeout;
|
||||
|
||||
@@ -33,10 +33,10 @@ http.interceptors.response.use(
|
||||
function (error) {
|
||||
// Any status codes that falls outside the range of 2xx cause this function to trigger
|
||||
// Do something with response error
|
||||
if (error.response.status === 401 || error.response.status === 403) {
|
||||
localStorage.clear();
|
||||
window.location.assign("/error");
|
||||
}
|
||||
// if (error.response.status === 401 || error.response.status === 403) {
|
||||
// localStorage.clear();
|
||||
// window.location.assign("/error");
|
||||
// }
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -70,11 +70,33 @@ namespace AceJobAgency.Controllers
|
||||
public IActionResult Login([FromBody] LoginRequest request)
|
||||
{
|
||||
var user = _context.Users.FirstOrDefault(u => u.Email == request.Email && u.IsActive == 1);
|
||||
if (user == null || !BCrypt.Net.BCrypt.Verify(request.Password, user.Password))
|
||||
if (user == null)
|
||||
{
|
||||
return Unauthorized("Invalid email or password.");
|
||||
}
|
||||
|
||||
if (user.IsLockedOut && user.LockoutEndTime > DateTime.Now)
|
||||
{
|
||||
return Unauthorized("Account is locked. Try again later.");
|
||||
}
|
||||
|
||||
if (!BCrypt.Net.BCrypt.Verify(request.Password, user.Password))
|
||||
{
|
||||
user.FailedLoginAttempts++;
|
||||
if (user.FailedLoginAttempts >= 5)
|
||||
{
|
||||
user.IsLockedOut = true;
|
||||
user.LockoutEndTime = DateTime.Now.AddMinutes(1);
|
||||
}
|
||||
_context.SaveChanges();
|
||||
return Unauthorized("Invalid email or password.");
|
||||
}
|
||||
|
||||
user.FailedLoginAttempts = 0;
|
||||
user.IsLockedOut = false;
|
||||
user.LockoutEndTime = null;
|
||||
_context.SaveChanges();
|
||||
|
||||
var token = GenerateJwtToken(user);
|
||||
return Ok(new { token });
|
||||
}
|
||||
@@ -184,7 +206,7 @@ namespace AceJobAgency.Controllers
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id),
|
||||
new Claim(ClaimTypes.Email, user.Email)
|
||||
]),
|
||||
Expires = DateTime.UtcNow.AddHours(2),
|
||||
Expires = DateTime.UtcNow.AddMinutes(15),
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
|
||||
SecurityAlgorithms.HmacSha256Signature)
|
||||
};
|
||||
|
||||
@@ -50,5 +50,12 @@ namespace AceJobAgency.Entities
|
||||
|
||||
[DataType(DataType.Date)]
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.Now;
|
||||
|
||||
public int FailedLoginAttempts { get; set; } = 0;
|
||||
|
||||
public bool IsLockedOut { get; set; } = false;
|
||||
|
||||
[DataType(DataType.DateTime)]
|
||||
public DateTime? LockoutEndTime { get; set; }
|
||||
}
|
||||
}
|
||||
100
AceJobAgency/Migrations/20250209045614_AddedAccountLockoutFields.Designer.cs
generated
Normal file
100
AceJobAgency/Migrations/20250209045614_AddedAccountLockoutFields.Designer.cs
generated
Normal file
@@ -0,0 +1,100 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using AceJobAgency.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AceJobAgency.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20250209045614_AddedAccountLockoutFields")]
|
||||
partial class AddedAccountLockoutFields
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.2")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("AceJobAgency.Entities.User", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasMaxLength(36)
|
||||
.HasColumnType("varchar(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("DateOfBirth")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<int>("FailedLoginAttempts")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<int>("Gender")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("IsActive")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("IsLockedOut")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<DateTime?>("LockoutEndTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("NationalRegistrationIdentityCardNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<string>("ResumeName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("WhoAmI")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace AceJobAgency.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddedAccountLockoutFields : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "FailedLoginAttempts",
|
||||
table: "Users",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsLockedOut",
|
||||
table: "Users",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "LockoutEndTime",
|
||||
table: "Users",
|
||||
type: "datetime(6)",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "FailedLoginAttempts",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsLockedOut",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LockoutEndTime",
|
||||
table: "Users");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,9 @@ namespace AceJobAgency.Migrations
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("varchar(128)");
|
||||
|
||||
b.Property<int>("FailedLoginAttempts")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
@@ -50,11 +53,17 @@ namespace AceJobAgency.Migrations
|
||||
b.Property<int>("IsActive")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("IsLockedOut")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<DateTime?>("LockoutEndTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("NationalRegistrationIdentityCardNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
|
||||
Reference in New Issue
Block a user