From 1a5d0bbfc0d9b15239b55ca4d831c6c73fa148fd Mon Sep 17 00:00:00 2001 From: LittleSheep <littlesheep.code@hotmail.com> Date: Tue, 17 Jun 2025 00:40:05 +0800 Subject: [PATCH] :bug: Fix bugs --- DysonNetwork.Sphere/.gitignore | 1 + .../Auth/OpenId/ConnectionController.cs | 52 ++++++++++---- .../Auth/OpenId/OidcController.cs | 2 +- .../DysonNetwork.Sphere.csproj | 71 ++++++++++--------- DysonNetwork.Sphere/Program.cs | 30 ++++++++ 5 files changed, 108 insertions(+), 48 deletions(-) diff --git a/DysonNetwork.Sphere/.gitignore b/DysonNetwork.Sphere/.gitignore index dea484d..eccb44f 100644 --- a/DysonNetwork.Sphere/.gitignore +++ b/DysonNetwork.Sphere/.gitignore @@ -1,5 +1,6 @@ Keys Uploads +DataProtection-Keys node_modules bun.lock diff --git a/DysonNetwork.Sphere/Auth/OpenId/ConnectionController.cs b/DysonNetwork.Sphere/Auth/OpenId/ConnectionController.cs index ba93e44..ecaad6f 100644 --- a/DysonNetwork.Sphere/Auth/OpenId/ConnectionController.cs +++ b/DysonNetwork.Sphere/Auth/OpenId/ConnectionController.cs @@ -192,32 +192,51 @@ public class ConnectionController( } catch (Exception ex) { - return BadRequest($"Error processing callback: {ex.Message}"); + return BadRequest($"Error processing {provider} authentication: {ex.Message}"); } + if (string.IsNullOrEmpty(userInfo.UserId)) + { + return BadRequest($"{provider} did not return a valid user identifier."); + } + + // Check if this provider account is already connected to any user var existingConnection = await db.AccountConnections .FirstOrDefaultAsync(c => c.Provider.Equals(provider, StringComparison.OrdinalIgnoreCase) && c.ProvidedIdentifier == userInfo.UserId); + // If it's connected to a different user, return error if (existingConnection != null && existingConnection.AccountId != accountId) { return BadRequest($"This {provider} account is already linked to another user."); } - var userConnection = await db.AccountConnections - .FirstOrDefaultAsync(c => - c.AccountId == accountId && c.Provider.Equals(provider, StringComparison.OrdinalIgnoreCase)); + // Check if the current user already has this provider connected + var userHasProvider = await db.AccountConnections + .AnyAsync(c => + c.AccountId == accountId && + c.Provider.Equals(provider, StringComparison.OrdinalIgnoreCase)); - var clock = SystemClock.Instance; - if (userConnection != null) + if (userHasProvider) { - userConnection.AccessToken = userInfo.AccessToken; - userConnection.RefreshToken = userInfo.RefreshToken; - userConnection.LastUsedAt = clock.GetCurrentInstant(); + // Update existing connection with new tokens + var connection = await db.AccountConnections + .FirstOrDefaultAsync(c => + c.AccountId == accountId && + c.Provider.Equals(provider, StringComparison.OrdinalIgnoreCase)); + + if (connection != null) + { + connection.AccessToken = userInfo.AccessToken; + connection.RefreshToken = userInfo.RefreshToken; + connection.LastUsedAt = SystemClock.Instance.GetCurrentInstant(); + connection.Meta = userInfo.ToMetadata(); + } } else { + // Create new connection db.AccountConnections.Add(new AccountConnection { AccountId = accountId, @@ -225,17 +244,26 @@ public class ConnectionController( ProvidedIdentifier = userInfo.UserId!, AccessToken = userInfo.AccessToken, RefreshToken = userInfo.RefreshToken, - LastUsedAt = clock.GetCurrentInstant(), + LastUsedAt = SystemClock.Instance.GetCurrentInstant(), Meta = userInfo.ToMetadata(), }); } - await db.SaveChangesAsync(); + try + { + await db.SaveChangesAsync(); + } + catch (DbUpdateException ex) + { + return StatusCode(500, $"Failed to save {provider} connection. Please try again."); + } + // Clean up and redirect var returnUrl = HttpContext.Session.GetString($"oidc_return_url_{callbackData.State}"); HttpContext.Session.Remove($"oidc_return_url_{callbackData.State}"); + HttpContext.Session.Remove($"oidc_state_{callbackData.State}"); - return Redirect(string.IsNullOrEmpty(returnUrl) ? "/" : returnUrl); + return Redirect(string.IsNullOrEmpty(returnUrl) ? "/settings/connections" : returnUrl); } private async Task<IActionResult> HandleLoginOrRegistration( diff --git a/DysonNetwork.Sphere/Auth/OpenId/OidcController.cs b/DysonNetwork.Sphere/Auth/OpenId/OidcController.cs index 4e42927..ada8bc1 100644 --- a/DysonNetwork.Sphere/Auth/OpenId/OidcController.cs +++ b/DysonNetwork.Sphere/Auth/OpenId/OidcController.cs @@ -23,7 +23,7 @@ public class OidcController( { var oidcService = GetOidcService(provider); - // If user is already authenticated, treat as an account connection request + // If the user is already authenticated, treat as an account connection request if (HttpContext.Items["CurrentUser"] is Account.Account currentUser) { var state = Guid.NewGuid().ToString(); diff --git a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj index 084f9d2..0b42478 100644 --- a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj +++ b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj @@ -16,38 +16,39 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="BCrypt.Net-Next" Version="4.0.3"/> - <PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.3.4"/> - <PackageReference Include="EFCore.BulkExtensions" Version="9.0.1"/> - <PackageReference Include="EFCore.BulkExtensions.PostgreSql" Version="9.0.1"/> - <PackageReference Include="EFCore.NamingConventions" Version="9.0.0"/> - <PackageReference Include="FFMpegCore" Version="5.2.0"/> + <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" /> + <PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.3.4" /> + <PackageReference Include="EFCore.BulkExtensions" Version="9.0.1" /> + <PackageReference Include="EFCore.BulkExtensions.PostgreSql" Version="9.0.1" /> + <PackageReference Include="EFCore.NamingConventions" Version="9.0.0" /> + <PackageReference Include="FFMpegCore" Version="5.2.0" /> <PackageReference Include="Livekit.Server.Sdk.Dotnet" Version="1.0.8" /> - <PackageReference Include="MailKit" Version="4.11.0"/> - <PackageReference Include="MaxMind.GeoIP2" Version="5.3.0"/> - <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4"/> - <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.2"/> + <PackageReference Include="MailKit" Version="4.11.0" /> + <PackageReference Include="MaxMind.GeoIP2" Version="5.3.0" /> + <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" /> + <PackageReference Include="Microsoft.AspNetCore.DataProtection.Extensions" Version="8.0.0" /> + <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.3"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4"/> + <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" /> <PackageReference Include="MimeTypes" Version="2.5.2"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Minio" Version="6.0.4"/> - <PackageReference Include="NetVips" Version="3.0.1"/> - <PackageReference Include="NetVips.Native.linux-x64" Version="8.16.1"/> - <PackageReference Include="NetVips.Native.osx-arm64" Version="8.16.1"/> + <PackageReference Include="Minio" Version="6.0.4" /> + <PackageReference Include="NetVips" Version="3.0.1" /> + <PackageReference Include="NetVips.Native.linux-x64" Version="8.16.1" /> + <PackageReference Include="NetVips.Native.osx-arm64" Version="8.16.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> - <PackageReference Include="NodaTime" Version="3.2.2"/> + <PackageReference Include="NodaTime" Version="3.2.2" /> <PackageReference Include="NodaTime.Serialization.JsonNet" Version="3.2.0" /> - <PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.3.0"/> - <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4"/> - <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0"/> - <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite" Version="9.0.4"/> - <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="9.0.4"/> + <PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.3.0" /> + <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" /> + <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0" /> + <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite" Version="9.0.4" /> + <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="9.0.4" /> <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" /> <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" /> <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" /> @@ -59,18 +60,18 @@ <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.4.1" /> <PackageReference Include="prometheus-net.EntityFramework" Version="0.9.5" /> <PackageReference Include="prometheus-net.SystemMetrics" Version="3.1.0" /> - <PackageReference Include="Quartz" Version="3.14.0"/> - <PackageReference Include="Quartz.AspNetCore" Version="3.14.0"/> - <PackageReference Include="Quartz.Extensions.Hosting" Version="3.14.0"/> + <PackageReference Include="Quartz" Version="3.14.0" /> + <PackageReference Include="Quartz.AspNetCore" Version="3.14.0" /> + <PackageReference Include="Quartz.Extensions.Hosting" Version="3.14.0" /> <PackageReference Include="SkiaSharp" Version="2.88.9" /> <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" /> <PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.9" /> <PackageReference Include="SkiaSharp.NativeAssets.macOS" Version="2.88.9" /> <PackageReference Include="StackExchange.Redis" Version="2.8.37" /> <PackageReference Include="StackExchange.Redis.Extensions.AspNetCore" Version="11.0.0" /> - <PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.0"/> - <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="8.1.0"/> - <PackageReference Include="tusdotnet" Version="2.8.1"/> + <PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.0" /> + <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="8.1.0" /> + <PackageReference Include="tusdotnet" Version="2.8.1" /> </ItemGroup> <ItemGroup> @@ -80,7 +81,7 @@ </ItemGroup> <ItemGroup> - <Folder Include="Migrations\"/> + <Folder Include="Migrations\" /> </ItemGroup> <ItemGroup> @@ -151,13 +152,13 @@ </ItemGroup> <ItemGroup> - <_ContentIncludedByDefault Remove="app\publish\appsettings.json"/> - <_ContentIncludedByDefault Remove="app\publish\DysonNetwork.Sphere.deps.json"/> - <_ContentIncludedByDefault Remove="app\publish\DysonNetwork.Sphere.runtimeconfig.json"/> - <_ContentIncludedByDefault Remove="app\publish\DysonNetwork.Sphere.staticwebassets.endpoints.json"/> - <_ContentIncludedByDefault Remove="app\publish\Keys\Solian.json"/> - <_ContentIncludedByDefault Remove="app\publish\package-lock.json"/> - <_ContentIncludedByDefault Remove="app\publish\package.json"/> + <_ContentIncludedByDefault Remove="app\publish\appsettings.json" /> + <_ContentIncludedByDefault Remove="app\publish\DysonNetwork.Sphere.deps.json" /> + <_ContentIncludedByDefault Remove="app\publish\DysonNetwork.Sphere.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="app\publish\DysonNetwork.Sphere.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="app\publish\Keys\Solian.json" /> + <_ContentIncludedByDefault Remove="app\publish\package-lock.json" /> + <_ContentIncludedByDefault Remove="app\publish\package.json" /> </ItemGroup> </Project> diff --git a/DysonNetwork.Sphere/Program.cs b/DysonNetwork.Sphere/Program.cs index e1515d7..4b4272d 100644 --- a/DysonNetwork.Sphere/Program.cs +++ b/DysonNetwork.Sphere/Program.cs @@ -35,6 +35,7 @@ using Quartz; using StackExchange.Redis; using tusdotnet; using tusdotnet.Stores; +using Microsoft.AspNetCore.DataProtection; var builder = WebApplication.CreateBuilder(args); @@ -90,12 +91,41 @@ builder.Services.AddSingleton<ICacheService, CacheServiceRedis>(); builder.Services.AddHttpClient(); +// Configure Data Protection for persistent session keys +var keysDirectory = Path.Combine(builder.Environment.ContentRootPath, "DataProtection-Keys"); +Directory.CreateDirectory(keysDirectory); + +builder.Services.AddDataProtection() + .PersistKeysToFileSystem(new DirectoryInfo(keysDirectory)) + .SetApplicationName("DysonNetwork.Sphere"); + +// Configure cookie policy to be essential for session +builder.Services.Configure<CookiePolicyOptions>(options => +{ + options.CheckConsentNeeded = _ => false; // Required for session to work without consent + options.MinimumSameSitePolicy = SameSiteMode.Lax; +}); + +// Add session with consistent cookie settings +builder.Services.AddSession(options => +{ + options.Cookie.Name = "_dynses"; + options.Cookie.HttpOnly = true; + options.Cookie.IsEssential = true; + options.IdleTimeout = TimeSpan.FromMinutes(30); +}); + // Register OIDC services builder.Services.AddScoped<OidcService, GoogleOidcService>(); builder.Services.AddScoped<OidcService, AppleOidcService>(); builder.Services.AddScoped<OidcService, GitHubOidcService>(); builder.Services.AddScoped<OidcService, MicrosoftOidcService>(); builder.Services.AddScoped<OidcService, DiscordOidcService>(); +builder.Services.AddScoped<GoogleOidcService>(); +builder.Services.AddScoped<AppleOidcService>(); +builder.Services.AddScoped<GitHubOidcService>(); +builder.Services.AddScoped<MicrosoftOidcService>(); +builder.Services.AddScoped<DiscordOidcService>(); builder.Services.AddControllers().AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower;