add alias to jwt, remove caching from auth, remove db usage from files
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
namespace MareSynchronosServer.Authentication;
|
||||
|
||||
public record SecretKeyAuthReply(bool Success, string Uid, string PrimaryUid, bool TempBan, bool Permaban);
|
||||
public record SecretKeyAuthReply(bool Success, string Uid, string PrimaryUid, string Alias, bool TempBan, bool Permaban);
|
||||
|
||||
@@ -10,30 +10,24 @@ namespace MareSynchronosServer.Authentication;
|
||||
public class SecretKeyAuthenticatorService
|
||||
{
|
||||
private readonly MareMetrics _metrics;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly IDbContextFactory<MareDbContext> _dbContextFactory;
|
||||
private readonly IConfigurationService<MareConfigurationAuthBase> _configurationService;
|
||||
private readonly ILogger<SecretKeyAuthenticatorService> _logger;
|
||||
private readonly ConcurrentDictionary<string, SecretKeyAuthReply> _cachedPositiveResponses = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, SecretKeyFailedAuthorization> _failedAuthorizations = new(StringComparer.Ordinal);
|
||||
|
||||
public SecretKeyAuthenticatorService(MareMetrics metrics, IServiceScopeFactory serviceScopeFactory, IConfigurationService<MareConfigurationAuthBase> configuration, ILogger<SecretKeyAuthenticatorService> logger)
|
||||
public SecretKeyAuthenticatorService(MareMetrics metrics, IDbContextFactory<MareDbContext> dbContextFactory,
|
||||
IConfigurationService<MareConfigurationAuthBase> configuration, ILogger<SecretKeyAuthenticatorService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_configurationService = configuration;
|
||||
_metrics = metrics;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
|
||||
public async Task<SecretKeyAuthReply> AuthorizeAsync(string ip, string hashedSecretKey)
|
||||
{
|
||||
_metrics.IncCounter(MetricsAPI.CounterAuthenticationRequests);
|
||||
|
||||
if (_cachedPositiveResponses.TryGetValue(hashedSecretKey, out var cachedPositiveResponse))
|
||||
{
|
||||
_metrics.IncCounter(MetricsAPI.CounterAuthenticationCacheHits);
|
||||
return cachedPositiveResponse;
|
||||
}
|
||||
|
||||
if (_failedAuthorizations.TryGetValue(ip, out var existingFailedAuthorization)
|
||||
&& existingFailedAuthorization.FailedAttempts > _configurationService.GetValueOrDefault(nameof(MareConfigurationAuthBase.FailedAuthForTempBan), 5))
|
||||
{
|
||||
@@ -50,12 +44,12 @@ public class SecretKeyAuthenticatorService
|
||||
_failedAuthorizations.Remove(ip, out _);
|
||||
});
|
||||
}
|
||||
return new(Success: false, Uid: null, PrimaryUid: null, TempBan: true, Permaban: false);
|
||||
return new(Success: false, Uid: null, PrimaryUid: null, Alias: null, TempBan: true, Permaban: false);
|
||||
}
|
||||
|
||||
using var scope = _serviceScopeFactory.CreateScope();
|
||||
using var context = scope.ServiceProvider.GetService<MareDbContext>();
|
||||
var authReply = await context.Auth.AsNoTracking().SingleOrDefaultAsync(u => u.HashedKey == hashedSecretKey).ConfigureAwait(false);
|
||||
using var context = await _dbContextFactory.CreateDbContextAsync().ConfigureAwait(false);
|
||||
var authReply = await context.Auth.Include(a => a.User).AsNoTracking()
|
||||
.SingleOrDefaultAsync(u => u.HashedKey == hashedSecretKey).ConfigureAwait(false);
|
||||
var isBanned = authReply?.IsBanned ?? false;
|
||||
var primaryUid = authReply?.PrimaryUserUID ?? authReply?.UserUID;
|
||||
|
||||
@@ -65,21 +59,13 @@ public class SecretKeyAuthenticatorService
|
||||
isBanned = isBanned || primaryUser.IsBanned;
|
||||
}
|
||||
|
||||
SecretKeyAuthReply reply = new(authReply != null, authReply?.UserUID, authReply?.PrimaryUserUID ?? authReply?.UserUID, TempBan: false, isBanned);
|
||||
SecretKeyAuthReply reply = new(authReply != null, authReply?.UserUID,
|
||||
authReply?.PrimaryUserUID ?? authReply?.UserUID, authReply.User.Alias ?? string.Empty, TempBan: false, isBanned);
|
||||
|
||||
if (reply.Success)
|
||||
{
|
||||
_metrics.IncCounter(MetricsAPI.CounterAuthenticationSuccesses);
|
||||
_metrics.IncGauge(MetricsAPI.GaugeAuthenticationCacheEntries);
|
||||
|
||||
_cachedPositiveResponses[hashedSecretKey] = reply;
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMinutes(5)).ConfigureAwait(false);
|
||||
_cachedPositiveResponses.TryRemove(hashedSecretKey, out _);
|
||||
_metrics.DecGauge(MetricsAPI.GaugeAuthenticationCacheEntries);
|
||||
});
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -107,6 +93,6 @@ public class SecretKeyAuthenticatorService
|
||||
}
|
||||
}
|
||||
|
||||
return new(Success: false, Uid: null, PrimaryUid: null, TempBan: false, Permaban: false);
|
||||
return new(Success: false, Uid: null, PrimaryUid: null, Alias: null, TempBan: false, Permaban: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public class JwtController : Controller
|
||||
{
|
||||
var uid = HttpContext.User.Claims.Single(p => string.Equals(p.Type, MareClaimTypes.Uid, StringComparison.Ordinal))!.Value;
|
||||
var ident = HttpContext.User.Claims.Single(p => string.Equals(p.Type, MareClaimTypes.CharaIdent, StringComparison.Ordinal))!.Value;
|
||||
var alias = HttpContext.User.Claims.SingleOrDefault(p => string.Equals(p.Type, MareClaimTypes.Alias))?.Value ?? string.Empty;
|
||||
|
||||
if (await _mareDbContext.Auth.Where(u => u.UserUID == uid || u.PrimaryUserUID == uid).AnyAsync(a => a.IsBanned))
|
||||
{
|
||||
@@ -74,7 +75,7 @@ public class JwtController : Controller
|
||||
}
|
||||
|
||||
_logger.LogInformation("RenewToken:SUCCESS:{id}:{ident}", uid, ident);
|
||||
return await CreateJwtFromId(uid, ident);
|
||||
return await CreateJwtFromId(uid, ident, alias);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -108,7 +109,7 @@ public class JwtController : Controller
|
||||
if (!authResult.Success && authResult.TempBan)
|
||||
{
|
||||
_logger.LogWarning("Authenticate:TEMPBAN:{id}:{ident}", authResult.Uid ?? "NOUID", charaIdent);
|
||||
return Unauthorized("You are temporarily banned. Try connecting again in 5 minutes.");
|
||||
return Unauthorized("Due to an excessive amount of failed authentication attempts you are temporarily banned. Check your Secret Key configuration and try connecting again in 5 minutes.");
|
||||
}
|
||||
if (authResult.Permaban)
|
||||
{
|
||||
@@ -126,7 +127,7 @@ public class JwtController : Controller
|
||||
}
|
||||
|
||||
_logger.LogInformation("Authenticate:SUCCESS:{id}:{ident}", authResult.Uid, charaIdent);
|
||||
return await CreateJwtFromId(authResult.Uid, charaIdent);
|
||||
return await CreateJwtFromId(authResult.Uid, charaIdent, authResult.Alias ?? string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -150,12 +151,13 @@ public class JwtController : Controller
|
||||
return handler.CreateJwtSecurityToken(token);
|
||||
}
|
||||
|
||||
private async Task<IActionResult> CreateJwtFromId(string uid, string charaIdent)
|
||||
private async Task<IActionResult> CreateJwtFromId(string uid, string charaIdent, string alias)
|
||||
{
|
||||
var token = CreateJwt(new List<Claim>()
|
||||
{
|
||||
new Claim(MareClaimTypes.Uid, uid),
|
||||
new Claim(MareClaimTypes.CharaIdent, charaIdent),
|
||||
new Claim(MareClaimTypes.Alias, alias),
|
||||
new Claim(MareClaimTypes.Expires, DateTime.UtcNow.AddHours(6).Ticks.ToString(CultureInfo.InvariantCulture)),
|
||||
new Claim(MareClaimTypes.Continent, await _geoIPProvider.GetCountryFromIP(_accessor))
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user