refactor: separate the method of counting failed attempts

This commit is contained in:
Polianin Nikita 2024-12-25 05:46:27 +03:00
parent 8c51ba83a4
commit 71c31c0bbb

View File

@ -24,6 +24,7 @@ public class AuthService(ICacheService cache, IAccessToken accessTokenService, I
private static string GetAuthCacheKey(string fingerprint) => $"{fingerprint}_auth_token";
private static string GetFirstAuthCacheKey(string fingerprint) => $"{fingerprint}_auth_token_first";
private static string GetAttemptFailedCountKey(string fingerprint) => $"{fingerprint}_login_failed";
private Task SetAuthTokenDataToCache(AuthToken data, CancellationToken cancellation) =>
cache.SetAsync(
@ -47,34 +48,47 @@ public class AuthService(ICacheService cache, IAccessToken accessTokenService, I
private Task RevokeAccessToken(string token) =>
revokedToken.AddTokenToRevokedAsync(token, accessTokenService.GetExpireDateTime(token));
private async Task VerifyUserOrThrowError(RequestContextInfo requestContext, User user, string password, string username,
CancellationToken cancellation = default)
private async Task RecordFailedLoginAttempt(string fingerprint, string userId, CancellationToken cancellation)
{
if ((user.Email.Equals(username, StringComparison.OrdinalIgnoreCase) || user.Username.Equals(username, StringComparison.OrdinalIgnoreCase)) &&
passwordService.VerifyPassword(password, user.Salt, user.PasswordHash))
return;
var failedLoginAttemptsCount = await cache.GetAsync<int?>(GetAttemptFailedCountKey(fingerprint), cancellation) ?? 1;
var failedLoginCacheExpiration = TimeSpan.FromHours(1);
var failedLoginCacheName = $"{requestContext.Fingerprint}_login_failed";
var countFailedLogin = await cache.GetAsync<int?>(failedLoginCacheName, cancellation) ?? 1;
var cacheSaveTime = TimeSpan.FromHours(1);
await cache.SetAsync(failedLoginCacheName, countFailedLogin + 1, slidingExpiration: cacheSaveTime, cancellationToken: cancellation);
if (countFailedLogin > 5)
if (failedLoginAttemptsCount > 5)
{
logger.LogWarning(
"Multiple unsuccessful login attempts for user ID {UserId}. Attempt count: {AttemptNumber}.",
user.Id,
countFailedLogin);
"Multiple unsuccessful login attempts for user ID {UserId}. Fingerprint: {Fingerprint}. Attempt count: {AttemptNumber}.",
userId,
fingerprint,
failedLoginAttemptsCount);
throw new SecurityException("Too many unsuccessful login attempts. Please try again later.");
}
logger.LogInformation(
"Login attempt failed for user ID {UserId}. Fingerprint: {Fingerprint}. Attempt count: {AttemptNumber}.",
user.Id,
requestContext.Fingerprint,
countFailedLogin);
userId,
fingerprint,
failedLoginAttemptsCount);
await cache.SetAsync(GetAttemptFailedCountKey(fingerprint), failedLoginAttemptsCount + 1,
slidingExpiration: failedLoginCacheExpiration, cancellationToken: cancellation);
}
private Task ResetFailedLoginAttempts(string fingerprint, CancellationToken cancellation) =>
cache.RemoveAsync(GetAttemptFailedCountKey(fingerprint), cancellation);
private async Task VerifyUserOrThrowError(RequestContextInfo requestContext, User user, string password, string username,
CancellationToken cancellation = default)
{
if ((user.Email.Equals(username, StringComparison.OrdinalIgnoreCase) ||
user.Username.Equals(username, StringComparison.OrdinalIgnoreCase)) &&
passwordService.VerifyPassword(password, user.Salt, user.PasswordHash))
{
await ResetFailedLoginAttempts(requestContext.Fingerprint, cancellation);
return;
}
await RecordFailedLoginAttempt(requestContext.Fingerprint, user.Id, cancellation);
throw new SecurityException("Authentication failed. Please check your credentials.");
}