Skip to content

Commit 7c4ac98

Browse files
committed
optimize cache cleanup
1 parent 4529123 commit 7c4ac98

File tree

8 files changed

+66
-26
lines changed

8 files changed

+66
-26
lines changed

src/Backend/Nager.AuthenticationService.Abstraction/Models/AuthenticationInfo.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,10 @@ public class AuthenticationInfo
99
public DateTime LastInvalid { get; set; }
1010

1111
public int InvalidCount { get; set; }
12+
13+
public override string ToString()
14+
{
15+
return $"LastValid:{this.LastValid} LastInvalid:{this.LastInvalid} InvalidCount:{this.InvalidCount}";
16+
}
1217
}
1318
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace Nager.AuthenticationService.Abstraction.Models
2+
{
3+
/// <summary>
4+
/// Validate Token Request
5+
/// </summary>
6+
public class ValidateTokenRequest
7+
{
8+
/// <summary>
9+
/// Mfa Identifier from Authenticate request
10+
/// </summary>
11+
public required string MfaIdentifier { get; set; }
12+
13+
/// <summary>
14+
/// Mfa Token
15+
/// </summary>
16+
public required string Token { get; set; }
17+
18+
/// <summary>
19+
/// IpAddress
20+
/// </summary>
21+
public string? IpAddress { get; set; }
22+
}
23+
}

src/Backend/Nager.AuthenticationService.Abstraction/Services/IUserAuthenticationService.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ Task<AuthenticationResult> ValidateCredentialsAsync(
2020
CancellationToken cancellationToken = default);
2121

2222
Task<ValidateTokenResult> ValidateTokenAsync(
23-
string mfaIdentifier,
24-
string token,
23+
ValidateTokenRequest validateTokenRequest,
2524
CancellationToken cancellationToken = default);
2625

2726
/// <summary>

src/Backend/Nager.AuthenticationService.WebApi/Controllers/AuthenticationController.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,14 @@ public async Task<ActionResult<AuthenticationResponseDto>> AuthenticateMfaTokenA
200200
[Required][FromBody] AuthenticationMfaTokenRequestDto request,
201201
CancellationToken cancellationToken = default)
202202
{
203-
var validateTokenResult = await this._userAuthenticationService.ValidateTokenAsync(request.MfaIdentifier, request.Token, cancellationToken);
203+
var ipAddress = HttpContext.GetIpAddress();
204+
205+
var validateTokenResult = await this._userAuthenticationService.ValidateTokenAsync(new ValidateTokenRequest
206+
{
207+
MfaIdentifier = request.MfaIdentifier,
208+
Token = request.Token,
209+
IpAddress = ipAddress
210+
}, cancellationToken);
204211
if (!validateTokenResult.Success)
205212
{
206213
return StatusCode(StatusCodes.Status400BadRequest);
@@ -224,12 +231,10 @@ public async Task<ActionResult<AuthenticationResponseDto>> AuthenticateMfaTokenA
224231
/// <returns></returns>
225232
/// <response code="204">Token valid</response>
226233
/// <response code="401">Token invalid</response>
227-
/// <response code="500">Unexpected error</response>
228234
[HttpPost]
229235
[Route("Validate")]
230236
[ProducesResponseType(StatusCodes.Status204NoContent)]
231237
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
232-
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
233238
public ActionResult<AuthenticationResponseDto> Validate()
234239
{
235240
return StatusCode(StatusCodes.Status204NoContent);

src/Backend/Nager.AuthenticationService.WebApi/Controllers/MonitoringController.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.AspNetCore.Authorization;
22
using Microsoft.AspNetCore.Mvc;
33
using Microsoft.Extensions.Caching.Memory;
4+
using Nager.AuthenticationService.Abstraction.Models;
45
using Nager.AuthenticationService.WebApi.Dtos;
56

67
namespace Nager.AuthenticationService.WebApi.Controllers
@@ -53,9 +54,9 @@ public ActionResult<CacheItemDto> GetCacheSnapshot(
5354
break;
5455
}
5556

56-
if (this._memoryCache.TryGetValue<int>(key, out var value))
57+
if (this._memoryCache.TryGetValue<AuthenticationInfo>(key, out var authenticationInfo))
5758
{
58-
cacheItems.Add(new CacheItemDto { Key = $"{key}", Value = $"{value}" });
59+
cacheItems.Add(new CacheItemDto { Key = $"{key}", Value = $"{authenticationInfo}" });
5960
}
6061
}
6162

src/Backend/Nager.AuthenticationService.WebApi/Controllers/UserManagementController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public async Task<ActionResult<UserInfoDto[]>> QueryUsersAsync(
8787
EmailAddress = userInfo.EmailAddress,
8888
Firstname = userInfo.Firstname,
8989
Lastname = userInfo.Lastname,
90-
Roles = userInfo.Roles,
90+
Roles = userInfo.Roles ?? [],
9191
LastFailedValidationTimestamp = userInfo.LastFailedValidationTimestamp,
9292
LastSuccessfulValidationTimestamp = userInfo.LastSuccessfulValidationTimestamp,
9393
MfaActive = userInfo.MfaActive
Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
using System.ComponentModel.DataAnnotations;
2-
3-
namespace Nager.AuthenticationService.WebApi.Dtos
1+
namespace Nager.AuthenticationService.WebApi.Dtos
42
{
3+
/// <summary>
4+
/// Authentication Mfa Token Request Dto
5+
/// </summary>
56
public class AuthenticationMfaTokenRequestDto
67
{
7-
[Required]
8-
public string MfaIdentifier { get; set; }
8+
/// <summary>
9+
/// Mfa Identifier from Authenticate request
10+
/// </summary>
11+
public required string MfaIdentifier { get; set; }
912

10-
[Required]
11-
public string Token { get; set; }
13+
/// <summary>
14+
/// The one Time Token
15+
/// </summary>
16+
public required string Token { get; set; }
1217
}
1318
}

src/Backend/Nager.AuthenticationService.WebApi/Services/UserAuthenticationService.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class UserAuthenticationService : IUserAuthenticationService
1818
private readonly IMemoryCache _memoryCache;
1919
private readonly string _cacheKeyPrefix = "AuthenticationInfo";
2020
private readonly TimeSpan _cacheLiveTime = TimeSpan.FromMinutes(10);
21+
private readonly TimeSpan _mfaTokenAcceptanceWindow = TimeSpan.FromMinutes(2);
2122
private readonly int _delayTimeMultiplier = 400; //ms
2223
private readonly int _maxInvalidLogins = 10;
2324
private readonly int _maxInvalidLoginsBeforeDelay = 3;
@@ -116,10 +117,7 @@ public async Task<AuthenticationResult> ValidateCredentialsAsync(
116117
AuthenticationRequest authenticationRequest,
117118
CancellationToken cancellationToken = default)
118119
{
119-
if (authenticationRequest == null)
120-
{
121-
throw new ArgumentNullException(nameof(authenticationRequest));
122-
}
120+
ArgumentNullException.ThrowIfNull(authenticationRequest);
123121

124122
if (string.IsNullOrEmpty(authenticationRequest.IpAddress))
125123
{
@@ -185,7 +183,7 @@ public async Task<AuthenticationResult> ValidateCredentialsAsync(
185183

186184
this._memoryCache.Set(cacheKey, authenticationRequest.EmailAddress, new MemoryCacheEntryOptions
187185
{
188-
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(120)
186+
AbsoluteExpirationRelativeToNow = this._mfaTokenAcceptanceWindow
189187
});
190188

191189
return new AuthenticationResult
@@ -242,11 +240,10 @@ await this._userRepository.SetLastValidationTimestampAsync(o => o.Id == userEnti
242240

243241
/// <inheritdoc />
244242
public async Task<ValidateTokenResult> ValidateTokenAsync(
245-
string mfaIdentifier,
246-
string token,
243+
ValidateTokenRequest validateTokenRequest,
247244
CancellationToken cancellationToken = default)
248245
{
249-
var cacheKey = this.GetCacheKey(mfaIdentifier);
246+
var cacheKey = this.GetCacheKey(validateTokenRequest.MfaIdentifier);
250247
if (!this._memoryCache.TryGetValue<string>(cacheKey, out var emailAddress))
251248
{
252249
this._logger.LogError($"{nameof(ValidateTokenAsync)} - CacheKey {cacheKey} not found");
@@ -267,8 +264,6 @@ public async Task<ValidateTokenResult> ValidateTokenAsync(
267264
};
268265
}
269266

270-
var timeTolerance = TimeSpan.FromSeconds(20);
271-
272267
var userEntity = await this._userRepository.GetAsync(o => o.EmailAddress == emailAddress);
273268
if (userEntity == null)
274269
{
@@ -280,13 +275,20 @@ public async Task<ValidateTokenResult> ValidateTokenAsync(
280275
};
281276
}
282277

278+
var timeTolerance = TimeSpan.FromSeconds(20);
283279
var twoFactorAuthenticator = new TwoFactorAuthenticator();
284-
var isTokenValid = twoFactorAuthenticator.ValidateTwoFactorPIN(userEntity.MfaSecret, token, timeTolerance);
280+
var isTokenValid = twoFactorAuthenticator.ValidateTwoFactorPIN(userEntity.MfaSecret, validateTokenRequest.Token, timeTolerance);
285281

286282
if (isTokenValid)
287283
{
288284
this._memoryCache.Remove(cacheKey);
289285

286+
if (!string.IsNullOrEmpty(validateTokenRequest.IpAddress))
287+
{
288+
this.SetValidLogin(validateTokenRequest.IpAddress);
289+
}
290+
this.SetValidLogin(emailAddress);
291+
290292
using var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(this._timeoutDatabaseUpdate));
291293
await this._userRepository.SetLastSuccessfulValidationTimestampAsync(o => o.Id == userEntity.Id, cancellationTokenSource.Token)
292294
.ContinueWith(task => { });

0 commit comments

Comments
 (0)