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