@@ -12,6 +12,9 @@ namespace UniGetUI.Services
1212{
1313 public class GitHubAuthService
1414 {
15+ private const string MissingClientId = "CLIENT_ID_UNSET" ;
16+ private const string MissingClientSecret = "CLIENT_SECRET_UNSET" ;
17+ private static readonly TimeSpan LoginTimeout = TimeSpan . FromMinutes ( 2 ) ;
1518 private readonly string GitHubClientId = Secrets . GetGitHubClientId ( ) ;
1619 private readonly string GitHubClientSecret = Secrets . GetGitHubClientSecret ( ) ;
1720 private const string RedirectUri = "http://127.0.0.1:58642/" ;
@@ -47,6 +50,15 @@ public async Task<bool> SignInAsync()
4750 {
4851 try
4952 {
53+ if ( ! HasConfiguredOAuthClient ( ) )
54+ {
55+ Logger . Error (
56+ "GitHub sign-in is not configured for this build. Missing OAuth client ID or client secret."
57+ ) ;
58+ AuthStatusChanged ? . Invoke ( this , EventArgs . Empty ) ;
59+ return false ;
60+ }
61+
5062 Logger . Info ( "Initiating GitHub sign-in process using loopback redirect..." ) ;
5163
5264 var request = new OauthLoginRequest ( GitHubClientId )
@@ -74,15 +86,25 @@ public async Task<bool> SignInAsync()
7486 loginBackend = new GHAuthApiRunner ( ) ;
7587 loginBackend . OnLogin += BackgroundApiOnOnLogin ;
7688 await loginBackend . Start ( ) ;
77- await Launcher . LaunchUriAsync ( oauthLoginUrl ) ;
7889
79- while ( codeFromAPI is null )
90+ bool launchSucceeded = await Launcher . LaunchUriAsync ( oauthLoginUrl ) ;
91+ if ( ! launchSucceeded )
92+ {
93+ Logger . Error ( "Failed to launch the browser for GitHub sign-in." ) ;
94+ AuthStatusChanged ? . Invoke ( this , EventArgs . Empty ) ;
95+ return false ;
96+ }
97+
98+ DateTime timeoutAt = DateTime . UtcNow . Add ( LoginTimeout ) ;
99+ while ( codeFromAPI is null && DateTime . UtcNow < timeoutAt )
80100 await Task . Delay ( 100 ) ;
81101
82- loginBackend . OnLogin -= BackgroundApiOnOnLogin ;
83- await loginBackend . Stop ( ) ;
84- loginBackend . Dispose ( ) ;
85- loginBackend = null ;
102+ if ( string . IsNullOrEmpty ( codeFromAPI ) )
103+ {
104+ Logger . Error ( "GitHub sign-in timed out before the loopback callback was received." ) ;
105+ AuthStatusChanged ? . Invoke ( this , EventArgs . Empty ) ;
106+ return false ;
107+ }
86108
87109 return await _completeSignInAsync ( codeFromAPI ) ;
88110 }
@@ -94,6 +116,26 @@ public async Task<bool> SignInAsync()
94116 AuthStatusChanged ? . Invoke ( this , EventArgs . Empty ) ;
95117 return false ;
96118 }
119+ finally
120+ {
121+ if ( loginBackend is not null )
122+ {
123+ try
124+ {
125+ loginBackend . OnLogin -= BackgroundApiOnOnLogin ;
126+ await loginBackend . Stop ( ) ;
127+ loginBackend . Dispose ( ) ;
128+ }
129+ catch ( Exception ex )
130+ {
131+ Logger . Warn ( ex ) ;
132+ }
133+ finally
134+ {
135+ loginBackend = null ;
136+ }
137+ }
138+ }
97139 }
98140
99141 private string ? codeFromAPI ;
@@ -103,6 +145,18 @@ private void BackgroundApiOnOnLogin(object? sender, string c)
103145 codeFromAPI = c ;
104146 }
105147
148+ private bool HasConfiguredOAuthClient ( )
149+ {
150+ return ! string . IsNullOrWhiteSpace ( GitHubClientId )
151+ && ! string . IsNullOrWhiteSpace ( GitHubClientSecret )
152+ && ! string . Equals ( GitHubClientId , MissingClientId , StringComparison . Ordinal )
153+ && ! string . Equals (
154+ GitHubClientSecret ,
155+ MissingClientSecret ,
156+ StringComparison . Ordinal
157+ ) ;
158+ }
159+
106160 private async Task < bool > _completeSignInAsync ( string code )
107161 {
108162 try
0 commit comments