From ba3be1e3bb7611fb6fc27569ffc1889f118f957e Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 16 Nov 2025 17:05:28 +0800 Subject: [PATCH] :loud_sound: Add verbose logs for oidc --- .../Auth/OpenId/ConnectionController.cs | 45 +++++++++++++++---- .../Auth/OpenId/OidcController.cs | 9 +++- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/DysonNetwork.Pass/Auth/OpenId/ConnectionController.cs b/DysonNetwork.Pass/Auth/OpenId/ConnectionController.cs index 94f367b..830ea4a 100644 --- a/DysonNetwork.Pass/Auth/OpenId/ConnectionController.cs +++ b/DysonNetwork.Pass/Auth/OpenId/ConnectionController.cs @@ -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 logger ) : ControllerBase { private const string StateCachePrefix = "oidc-state:"; @@ -152,8 +154,13 @@ public class ConnectionController( { var stateValue = await cache.GetAsync(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"]; - - return Redirect(string.IsNullOrEmpty(returnUrl) ? siteUrl + "/auth/callback" : returnUrl); + var redirectUrl = string.IsNullOrEmpty(returnUrl) ? siteUrl + "/auth/callback" : returnUrl; + + logger.LogInformation("Redirecting after OIDC connection to {RedirectUrl}", redirectUrl); + return Redirect(redirectUrl); } private async Task 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}"); } @@ -305,13 +320,21 @@ 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(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 ExtractCallbackData(HttpRequest request) diff --git a/DysonNetwork.Pass/Auth/OpenId/OidcController.cs b/DysonNetwork.Pass/Auth/OpenId/OidcController.cs index 9aae198..1291aa5 100644 --- a/DysonNetwork.Pass/Auth/OpenId/OidcController.cs +++ b/DysonNetwork.Pass/Auth/OpenId/OidcController.cs @@ -14,7 +14,8 @@ public class OidcController( IServiceProvider serviceProvider, AppDatabase db, AccountService accounts, - ICacheService cache + ICacheService cache, + ILogger logger ) : ControllerBase { @@ -25,9 +26,10 @@ public class OidcController( public async Task 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}"); } }