feat: improve logging
All checks were successful
.NET Test Pipeline / build-and-test (push) Successful in 1m12s
Build and Deploy Docker Container / build-and-deploy (push) Successful in 2m11s

This commit is contained in:
Polianin Nikita 2024-12-23 07:48:28 +03:00
parent 053f01eec1
commit 5ff8744a55
3 changed files with 26 additions and 20 deletions

View File

@ -65,7 +65,10 @@ public static class LoggerConfiguration
return app.Use(async (context, next) => return app.Use(async (context, next) =>
{ {
var traceId = Activity.Current?.Id ?? context.TraceIdentifier; var traceId = Activity.Current?.Id ?? context.TraceIdentifier;
using (LogContext.PushProperty("TraceId", traceId)) using (LogContext.PushProperty("TraceId", traceId))
using (LogContext.PushProperty("UserAgent", context.Request.Headers.UserAgent.ToString()))
using (LogContext.PushProperty("RemoteIPAddress", context.Connection.RemoteIpAddress?.ToString()))
{ {
await next(); await next();
} }

View File

@ -63,19 +63,16 @@ public class AuthService(ICacheService cache, IAccessToken accessTokenService, I
if (countFailedLogin > 5) if (countFailedLogin > 5)
{ {
logger.LogWarning( logger.LogWarning(
"Multiple unsuccessful login attempts for user ID {UserId} from IP {UserIp}. Attempt count: {AttemptNumber}.", "Multiple unsuccessful login attempts for user ID {UserId}. Attempt count: {AttemptNumber}.",
user.Id, user.Id,
requestContext.Ip,
countFailedLogin); countFailedLogin);
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}. IP: {UserIp}, User-Agent: {UserAgent}, Fingerprint: {Fingerprint}. Attempt count: {AttemptNumber}.", "Login attempt failed for user ID {UserId}. Fingerprint: {Fingerprint}. Attempt count: {AttemptNumber}.",
user.Id, user.Id,
requestContext.Ip,
requestContext.UserAgent,
requestContext.Fingerprint, requestContext.Fingerprint,
countFailedLogin); countFailedLogin);
@ -100,10 +97,8 @@ public class AuthService(ICacheService cache, IAccessToken accessTokenService, I
cookieOptions.SetCookie(context, CookieNames.RefreshToken, authToken.RefreshToken, DateTime.UtcNow.Add(Lifetime)); cookieOptions.SetCookie(context, CookieNames.RefreshToken, authToken.RefreshToken, DateTime.UtcNow.Add(Lifetime));
logger.LogInformation( logger.LogInformation(
"Login successful for user ID {UserId}. IP: {UserIp}, User-Agent: {UserAgent}, Fingerprint: {Fingerprint}.", "Login successful for user ID {UserId}. Fingerprint: {Fingerprint}.",
authToken.UserId, authToken.UserId,
authToken.Ip,
authToken.UserAgent,
authToken.Fingerprint); authToken.Fingerprint);
} }
@ -184,10 +179,8 @@ public class AuthService(ICacheService cache, IAccessToken accessTokenService, I
cookieOptions.DropCookie(context, CookieNames.AccessToken); cookieOptions.DropCookie(context, CookieNames.AccessToken);
cookieOptions.DropCookie(context, CookieNames.RefreshToken); cookieOptions.DropCookie(context, CookieNames.RefreshToken);
logger.LogWarning("Token validation failed for user ID {UserId}. IP: {UserIp}, User-Agent: {UserAgent}, Fingerprint: {Fingerprint}. Reason: {Reason}.", logger.LogWarning("Token validation failed for user ID {UserId}. Fingerprint: {Fingerprint}. Reason: {Reason}.",
authToken.UserId, authToken.UserId,
authToken.Ip,
authToken.UserAgent,
authToken.Fingerprint, authToken.Fingerprint,
authToken.RefreshToken != requestContext.RefreshToken ? authToken.RefreshToken != requestContext.RefreshToken ?
$"Cached refresh token '{authToken.RefreshToken}' does not match the provided refresh token '{requestContext.RefreshToken}'" : $"Cached refresh token '{authToken.RefreshToken}' does not match the provided refresh token '{requestContext.RefreshToken}'" :

View File

@ -110,21 +110,25 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
{ {
var (clientId, _) = providers[provider]; var (clientId, _) = providers[provider];
var requestInfo = new RequestContextInfo(context, cookieOptions);
var state = GetHmacString(requestInfo, secretKey);
var redirectUrl = $"?client_id={clientId}" + var redirectUrl = $"?client_id={clientId}" +
"&response_type=code" + "&response_type=code" +
$"&redirect_uri={redirectUri}" + $"&redirect_uri={redirectUri}" +
$"&scope={ProviderData[provider].Scope}" + $"&scope={ProviderData[provider].Scope}" +
$"&state={GetHmacString(new RequestContextInfo(context, cookieOptions), secretKey)}_{Enum.GetName(provider)}"; $"&state={state}_{Enum.GetName(provider)}";
logger.LogInformation("Redirecting user Fingerprint: {Fingerprint} to OAuth provider {Provider} with state: {State}",
requestInfo.Fingerprint,
provider,
state);
return new Uri(ProviderData[provider].RedirectUrl + redirectUrl); return new Uri(ProviderData[provider].RedirectUrl + redirectUrl);
} }
public (OAuthProvider Provider, Uri Redirect)[] GetAvailableProviders(string redirectUri) public (OAuthProvider Provider, Uri Redirect)[] GetAvailableProviders(string redirectUri) =>
{ [.. providers.Select(x => (x.Key, new Uri(redirectUri.TrimEnd('/') + "/?provider=" + (int)x.Key)))];
return [.. providers.Select(x => (x.Key, new Uri(redirectUri.TrimEnd('/') + "/?provider=" + (int)x.Key)))];
}
public async Task<(OAuthProvider provider, OAuthUser User)> LoginOAuth(HttpContext context, CookieOptionsParameters cookieOptions, string redirectUrl, string code, string state, CancellationToken cancellation = default) public async Task<(OAuthProvider provider, OAuthUser User)> LoginOAuth(HttpContext context, CookieOptionsParameters cookieOptions, string redirectUrl, string code, string state, CancellationToken cancellation = default)
{ {
@ -139,11 +143,17 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
} }
var secretStateData = string.Join("_", partsState.SkipLast(1)); var secretStateData = string.Join("_", partsState.SkipLast(1));
var secretData = GetHmacString(new RequestContextInfo(context, cookieOptions), secretKey); var requestInfo = new RequestContextInfo(context, cookieOptions);
var secretData = GetHmacString(requestInfo, secretKey);
if (secretData != secretStateData) if (secretData != secretStateData)
{ {
logger.LogWarning("Fingerprint mismatch. Possible CSRF attack detected."); logger.LogWarning(
"Fingerprint mismatch. Possible CSRF attack detected. Fingerprint: {Fingerprint}, State: {State}, ExpectedState: {ExpectedState}",
requestInfo.Fingerprint,
secretData,
secretStateData
);
throw new SecurityException("Suspicious activity detected. Please try again."); throw new SecurityException("Suspicious activity detected. Please try again.");
} }
@ -154,7 +164,7 @@ public class OAuthService(ILogger<OAuthService> logger, Dictionary<OAuthProvider
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.LogWarning(ex, "Failed to exchange authorization code for tokens with provider {Provider}", provider); logger.LogWarning(ex, "Failed to exchange code for access token with provider {Provider}. State: {State}", provider, state);
} }
if (accessToken == null) if (accessToken == null)