feat: improve logging
This commit is contained in:
parent
053f01eec1
commit
5ff8744a55
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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}'" :
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user