diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 76d3ef87f..07c447d05 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -89,6 +89,8 @@ jobs: permissions: contents: read env: + UNIGETUI_GITHUB_CLIENT_ID: ${{ secrets.UNIGETUI_GITHUB_CLIENT_ID }} + UNIGETUI_GITHUB_CLIENT_SECRET: ${{ secrets.UNIGETUI_GITHUB_CLIENT_SECRET }} NUGET_PACKAGES: ${{ github.workspace }}\.nuget\packages strategy: fail-fast: false @@ -99,6 +101,19 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Validate GitHub OAuth secrets + shell: pwsh + run: | + if ([string]::IsNullOrWhiteSpace($env:UNIGETUI_GITHUB_CLIENT_ID)) { + throw "UNIGETUI_GITHUB_CLIENT_ID is not configured for this build environment." + } + + if ([string]::IsNullOrWhiteSpace($env:UNIGETUI_GITHUB_CLIENT_SECRET)) { + throw "UNIGETUI_GITHUB_CLIENT_SECRET is not configured for this build environment." + } + + Write-Host "::notice::GitHub OAuth secrets are configured for this build." + - name: Install .NET uses: actions/setup-dotnet@v5 with: diff --git a/src/UniGetUI/Pages/DialogPages/DialogHelper_Generic.cs b/src/UniGetUI/Pages/DialogPages/DialogHelper_Generic.cs index 93dd21896..0a1900c29 100644 --- a/src/UniGetUI/Pages/DialogPages/DialogHelper_Generic.cs +++ b/src/UniGetUI/Pages/DialogPages/DialogHelper_Generic.cs @@ -488,7 +488,7 @@ public static async Task ShowTelemetryDialog() p.Inlines.Add(new LineBreak()); var link = new Hyperlink { - NavigateUri = new Uri("https://www.marticliment.com/unigetui/privacy/"), + NavigateUri = new Uri("https://devolutions.net/legal/"), }; link.Inlines.Add( new Run diff --git a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs index 8ab5c784f..2060bfdac 100644 --- a/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs +++ b/src/UniGetUI/Pages/SettingsPages/GeneralPages/Backup.xaml.cs @@ -354,7 +354,7 @@ private void EnablePackageBackupCheckBox_CLOUD_StateChanged(object? sender, Even private void MoreInfoBtn_OnClick(object sender, RoutedEventArgs e) { - MainApp.Instance.MainWindow.NavigationPage.ShowHelp("cloud-backup-overview/"); + CoreTools.Launch("https://devolutions.net/unigetui"); } } } diff --git a/src/UniGetUI/Services/GitHubAuthService.cs b/src/UniGetUI/Services/GitHubAuthService.cs index 958192cbb..8a7507838 100644 --- a/src/UniGetUI/Services/GitHubAuthService.cs +++ b/src/UniGetUI/Services/GitHubAuthService.cs @@ -12,6 +12,9 @@ namespace UniGetUI.Services { public class GitHubAuthService { + private const string MissingClientId = "CLIENT_ID_UNSET"; + private const string MissingClientSecret = "CLIENT_SECRET_UNSET"; + private static readonly TimeSpan LoginTimeout = TimeSpan.FromMinutes(2); private readonly string GitHubClientId = Secrets.GetGitHubClientId(); private readonly string GitHubClientSecret = Secrets.GetGitHubClientSecret(); private const string RedirectUri = "http://127.0.0.1:58642/"; @@ -47,6 +50,15 @@ public async Task SignInAsync() { try { + if (!HasConfiguredOAuthClient()) + { + Logger.Error( + "GitHub sign-in is not configured for this build. Missing OAuth client ID or client secret." + ); + AuthStatusChanged?.Invoke(this, EventArgs.Empty); + return false; + } + Logger.Info("Initiating GitHub sign-in process using loopback redirect..."); var request = new OauthLoginRequest(GitHubClientId) @@ -74,15 +86,25 @@ public async Task SignInAsync() loginBackend = new GHAuthApiRunner(); loginBackend.OnLogin += BackgroundApiOnOnLogin; await loginBackend.Start(); - await Launcher.LaunchUriAsync(oauthLoginUrl); - while (codeFromAPI is null) + bool launchSucceeded = await Launcher.LaunchUriAsync(oauthLoginUrl); + if (!launchSucceeded) + { + Logger.Error("Failed to launch the browser for GitHub sign-in."); + AuthStatusChanged?.Invoke(this, EventArgs.Empty); + return false; + } + + DateTime timeoutAt = DateTime.UtcNow.Add(LoginTimeout); + while (codeFromAPI is null && DateTime.UtcNow < timeoutAt) await Task.Delay(100); - loginBackend.OnLogin -= BackgroundApiOnOnLogin; - await loginBackend.Stop(); - loginBackend.Dispose(); - loginBackend = null; + if (string.IsNullOrEmpty(codeFromAPI)) + { + Logger.Error("GitHub sign-in timed out before the loopback callback was received."); + AuthStatusChanged?.Invoke(this, EventArgs.Empty); + return false; + } return await _completeSignInAsync(codeFromAPI); } @@ -94,6 +116,26 @@ public async Task SignInAsync() AuthStatusChanged?.Invoke(this, EventArgs.Empty); return false; } + finally + { + if (loginBackend is not null) + { + try + { + loginBackend.OnLogin -= BackgroundApiOnOnLogin; + await loginBackend.Stop(); + loginBackend.Dispose(); + } + catch (Exception ex) + { + Logger.Warn(ex); + } + finally + { + loginBackend = null; + } + } + } } private string? codeFromAPI; @@ -103,6 +145,18 @@ private void BackgroundApiOnOnLogin(object? sender, string c) codeFromAPI = c; } + private bool HasConfiguredOAuthClient() + { + return !string.IsNullOrWhiteSpace(GitHubClientId) + && !string.IsNullOrWhiteSpace(GitHubClientSecret) + && !string.Equals(GitHubClientId, MissingClientId, StringComparison.Ordinal) + && !string.Equals( + GitHubClientSecret, + MissingClientSecret, + StringComparison.Ordinal + ); + } + private async Task _completeSignInAsync(string code) { try diff --git a/src/UniGetUI/Services/UserAvatar.cs b/src/UniGetUI/Services/UserAvatar.cs index f99edc45f..bcc00942f 100644 --- a/src/UniGetUI/Services/UserAvatar.cs +++ b/src/UniGetUI/Services/UserAvatar.cs @@ -68,7 +68,14 @@ private async Task _loginButton_Click() return; } - await client.SignInAsync(); + bool success = await client.SignInAsync(); + if (!success) + { + DialogHelper.ShowDismissableBalloon( + CoreTools.Translate("Failed"), + CoreTools.Translate("An error occurred while logging in: ") + ); + } } catch (Exception ex) { @@ -132,7 +139,7 @@ private PointButton GenerateLoginControl() FontSize = 12, }; hyperlinkButton.Click += (_, _) => - MainApp.Instance.MainWindow.NavigationPage.ShowHelp("cloud-backup-overview/"); + CoreTools.Launch("https://devolutions.net/unigetui"); var loginButton = new PointButton { @@ -235,7 +242,7 @@ private async Task GenerateLogoutControl() FontSize = 12, }; hyperlinkButton.Click += (_, _) => - MainApp.Instance.MainWindow.NavigationPage.ShowHelp("cloud-backup-overview/"); + CoreTools.Launch("https://devolutions.net/unigetui"); var hyperlinkButton2 = new HyperlinkButton { diff --git a/src/UniGetUI/Services/generate-secrets.ps1 b/src/UniGetUI/Services/generate-secrets.ps1 index 69da177ac..c6ce3180a 100644 --- a/src/UniGetUI/Services/generate-secrets.ps1 +++ b/src/UniGetUI/Services/generate-secrets.ps1 @@ -12,14 +12,14 @@ if (-not (Test-Path -Path "Generated Files")) { } -$clientId = $env:GH_UGUI_CLIENT_ID -$clientSecret = $env:GH_UGUI_CLIENT_SECRET +$clientId = $env:UNIGETUI_GITHUB_CLIENT_ID +$clientSecret = $env:UNIGETUI_GITHUB_CLIENT_SECRET if (-not $clientId) { $clientId = "CLIENT_ID_UNSET" } if (-not $clientSecret) { $clientSecret = "CLIENT_SECRET_UNSET" } @" -// Auto-generated file - do not modidy +// Auto-generated file - do not modify namespace UniGetUI.Services { internal static partial class Secrets @@ -29,4 +29,4 @@ namespace UniGetUI.Services } } "@ | Set-Content -Encoding UTF8 "Generated Files\Secrets.Generated.cs" -cp "Generated Files\Secrets.Generated.cs" "$OutputPath\Generated Files\Secrets.Generated.cs" +Copy-Item "Generated Files\Secrets.Generated.cs" "$OutputPath\Generated Files\Secrets.Generated.cs"