🔊 Add verbose logs for oidc

This commit is contained in:
2025-11-16 17:05:28 +08:00
parent 6fd90c424d
commit ba3be1e3bb
2 changed files with 43 additions and 11 deletions

View File

@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using DysonNetwork.Shared.Cache;
using Microsoft.AspNetCore.WebUtilities;
using NodaTime;
using DysonNetwork.Shared.Models;
@@ -17,7 +18,8 @@ public class ConnectionController(
AccountService accounts,
AuthService auth,
ICacheService cache,
IConfiguration configuration
IConfiguration configuration,
ILogger<ConnectionController> logger
) : ControllerBase
{
private const string StateCachePrefix = "oidc-state:";
@@ -152,8 +154,13 @@ public class ConnectionController(
{
var stateValue = await cache.GetAsync<string>(stateKey);
if (string.IsNullOrEmpty(stateValue) || !OidcState.TryParse(stateValue, out oidcState) || oidcState == null)
{
logger.LogWarning("Invalid or expired OIDC state: {State}", callbackData.State);
return BadRequest("Invalid or expired state parameter");
}
}
logger.LogInformation("OIDC callback for provider {Provider} with state {State} and flow {FlowType}", provider, callbackData.State, oidcState.FlowType);
// Remove the state from cache to prevent replay attacks
await cache.RemoveAsync(stateKey);
@@ -174,13 +181,16 @@ public class ConnectionController(
{
// Login/Registration flow
if (!string.IsNullOrEmpty(oidcState.DeviceId))
{
callbackData.State = oidcState.DeviceId;
}
// Store return URL if provided
if (string.IsNullOrEmpty(oidcState.ReturnUrl) || oidcState.ReturnUrl == "/")
{
logger.LogInformation("No returnUrl provided in OIDC state, will use default.");
return await HandleLoginOrRegistration(provider, oidcService, callbackData);
}
logger.LogInformation("Storing returnUrl {ReturnUrl} for state {State}", oidcState.ReturnUrl, callbackData.State);
var returnUrlKey = $"{ReturnUrlCachePrefix}{callbackData.State}";
await cache.SetAsync(returnUrlKey, oidcState.ReturnUrl, StateExpiration);
@@ -206,6 +216,7 @@ public class ConnectionController(
}
catch (Exception ex)
{
logger.LogError(ex, "Error processing OIDC callback for provider {Provider} during connection flow", provider);
return BadRequest($"Error processing {provider} authentication: {ex.Message}");
}
@@ -270,8 +281,9 @@ public class ConnectionController(
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
catch (DbUpdateException ex)
{
logger.LogError(ex, "Failed to save OIDC connection for provider {Provider}", provider);
return StatusCode(500, $"Failed to save {provider} connection. Please try again.");
}
@@ -281,8 +293,10 @@ public class ConnectionController(
await cache.RemoveAsync(returnUrlKey);
var siteUrl = configuration["SiteUrl"];
var redirectUrl = string.IsNullOrEmpty(returnUrl) ? siteUrl + "/auth/callback" : returnUrl;
return Redirect(string.IsNullOrEmpty(returnUrl) ? siteUrl + "/auth/callback" : returnUrl);
logger.LogInformation("Redirecting after OIDC connection to {RedirectUrl}", redirectUrl);
return Redirect(redirectUrl);
}
private async Task<IActionResult> HandleLoginOrRegistration(
@@ -298,6 +312,7 @@ public class ConnectionController(
}
catch (Exception ex)
{
logger.LogError(ex, "Error processing OIDC callback for provider {Provider} during login/registration flow", provider);
return BadRequest($"Error processing callback: {ex.Message}");
}
@@ -306,12 +321,20 @@ public class ConnectionController(
return BadRequest($"Email or user ID is missing from {provider}'s response");
}
// Retrieve and clean up the return URL
var returnUrlKey = $"{ReturnUrlCachePrefix}{callbackData.State}";
var returnUrl = await cache.GetAsync<string>(returnUrlKey);
await cache.RemoveAsync(returnUrlKey);
var siteUrl = configuration["SiteUrl"];
var redirectBaseUrl = string.IsNullOrEmpty(returnUrl) ? siteUrl + "/auth/callback" : returnUrl;
var connection = await db.AccountConnections
.Include(c => c.Account)
.FirstOrDefaultAsync(c => c.Provider == provider && c.ProvidedIdentifier == userInfo.UserId);
var clock = SystemClock.Instance;
var siteUrl = configuration["SiteUrl"];
if (connection != null)
{
// Login existing user
@@ -325,7 +348,9 @@ public class ConnectionController(
HttpContext,
deviceId ?? string.Empty);
return Redirect(siteUrl + $"/auth/callback?challenge={challenge.Id}");
var redirectUrl = QueryHelpers.AddQueryString(redirectBaseUrl, "challenge", challenge.Id.ToString());
logger.LogInformation("OIDC login successful for user {UserId}. Redirecting to {RedirectUrl}", connection.AccountId, redirectUrl);
return Redirect(redirectUrl);
}
// Register new user
@@ -349,7 +374,9 @@ public class ConnectionController(
var loginSession = await auth.CreateSessionForOidcAsync(account, clock.GetCurrentInstant());
var loginToken = auth.CreateToken(loginSession);
return Redirect(siteUrl + $"/auth/callback?token={loginToken}");
var finalRedirectUrl = QueryHelpers.AddQueryString(redirectBaseUrl, "token", loginToken);
logger.LogInformation("OIDC registration successful for new user {UserId}. Redirecting to {RedirectUrl}", account.Id, finalRedirectUrl);
return Redirect(finalRedirectUrl);
}
private static async Task<OidcCallbackData> ExtractCallbackData(HttpRequest request)

View File

@@ -14,7 +14,8 @@ public class OidcController(
IServiceProvider serviceProvider,
AppDatabase db,
AccountService accounts,
ICacheService cache
ICacheService cache,
ILogger<OidcController> logger
)
: ControllerBase
{
@@ -25,9 +26,10 @@ public class OidcController(
public async Task<ActionResult> OidcLogin(
[FromRoute] string provider,
[FromQuery] string? returnUrl = "/",
[FromHeader(Name = "X-Device-Id")] string? deviceId = null
[FromQuery] string? deviceId = null
)
{
logger.LogInformation("OIDC login request for provider {Provider} with returnUrl {ReturnUrl} and deviceId {DeviceId}", provider, returnUrl, deviceId);
try
{
var oidcService = GetOidcService(provider);
@@ -41,6 +43,7 @@ public class OidcController(
// Create and store connection state
var oidcState = OidcState.ForConnection(currentUser.Id, provider, nonce, deviceId);
await cache.SetAsync($"{StateCachePrefix}{state}", oidcState, StateExpiration);
logger.LogInformation("OIDC connection flow started for user {UserId} with state {State}", currentUser.Id, state);
// The state parameter sent to the provider is the GUID key for the cache.
var authUrl = await oidcService.GetAuthorizationUrlAsync(state, nonce);
@@ -54,12 +57,14 @@ public class OidcController(
// Create login state with return URL and device ID
var oidcState = OidcState.ForLogin(returnUrl ?? "/", deviceId);
await cache.SetAsync($"{StateCachePrefix}{state}", oidcState, StateExpiration);
logger.LogInformation("OIDC login flow started with state {State} and returnUrl {ReturnUrl}", state, oidcState.ReturnUrl);
var authUrl = await oidcService.GetAuthorizationUrlAsync(state, nonce);
return Redirect(authUrl);
}
}
catch (Exception ex)
{
logger.LogError(ex, "Error initiating OIDC flow for provider {Provider}", provider);
return BadRequest($"Error initiating OpenID Connect flow: {ex.Message}");
}
}