@@ -54,6 +54,9 @@ export class UiProvider implements vscode.WebviewViewProvider {
5454 case "loadConfig" :
5555 this . loadConfiguration ( ) ;
5656 break ;
57+ case "loginWithGitHub" :
58+ this . loginWithGitHub ( ) ;
59+ break ;
5760 case "testConnection" :
5861 this . testGitHubConnection ( ) ;
5962 break ;
@@ -165,6 +168,26 @@ export class UiProvider implements vscode.WebviewViewProvider {
165168 }
166169 this . logger . info ( "UiProvider" , `Using threat model: ${ threatModel } ` ) ;
167170
171+ // Check GitHub auth status
172+ try {
173+ const authStatus = await this . _githubService . checkGitHubAuth ( ) ;
174+
175+ // Send auth status to the WebUI
176+ this . _view ?. webview . postMessage ( {
177+ command : "authStatus" ,
178+ isAuthenticated : authStatus . isAuthenticated ,
179+ displayName : authStatus . displayName
180+ } ) ;
181+
182+ if ( authStatus . isAuthenticated ) {
183+ this . logger . info ( "UiProvider" , `User is authenticated with GitHub as: ${ authStatus . displayName } ` ) ;
184+ } else {
185+ this . logger . info ( "UiProvider" , "User is not authenticated with GitHub" ) ;
186+ }
187+ } catch ( error ) {
188+ this . logger . warn ( "UiProvider" , "Failed to check GitHub authentication status" , error ) ;
189+ }
190+
168191 // Auto-select GitHub repository languages if no manual selection exists
169192 let languages = config . get < string [ ] > ( "languages" , [ ] ) ;
170193 if ( languages . length === 0 ) {
@@ -593,6 +616,70 @@ export class UiProvider implements vscode.WebviewViewProvider {
593616 }
594617 }
595618
619+ private async loginWithGitHub ( ) {
620+ this . logger . logServiceCall ( "UiProvider" , "loginWithGitHub" , "started" ) ;
621+
622+ try {
623+ // Update UI to show authentication is in progress
624+ this . _view ?. webview . postMessage ( {
625+ command : "authStarted" ,
626+ message : "GitHub authentication in progress..."
627+ } ) ;
628+
629+ // Call GitHub Service to authenticate
630+ const success = await this . _githubService . authenticateWithGitHub ( ) ;
631+
632+ if ( success ) {
633+ this . logger . logServiceCall ( "UiProvider" , "loginWithGitHub" , "completed" ) ;
634+
635+ try {
636+ // Try to get repository info with the new token
637+ const repoInfo = await this . _githubService . getRepositoryInfo ( ) ;
638+ const config = vscode . workspace . getConfiguration ( "codeql-scanner" ) ;
639+ const token = config . get < string > ( "github.token" ) ;
640+
641+ // Update UI with authentication status
642+ this . _view ?. webview . postMessage ( {
643+ command : "authCompleted" ,
644+ success : true ,
645+ message : "Successfully authenticated with GitHub" ,
646+ config : {
647+ githubToken : token ,
648+ githubOwner : repoInfo . owner ,
649+ githubRepo : repoInfo . repo ,
650+ }
651+ } ) ;
652+ } catch ( repoError ) {
653+ // Still authenticated but couldn't get repo info
654+ this . logger . warn ( "UiProvider" , "Authenticated but couldn't get repository info" , repoError ) ;
655+
656+ this . _view ?. webview . postMessage ( {
657+ command : "authCompleted" ,
658+ success : true ,
659+ message : "Successfully authenticated with GitHub, but couldn't retrieve repository information." ,
660+ } ) ;
661+ }
662+
663+ // Reload the configuration to update the UI
664+ this . loadConfiguration ( ) ;
665+ } else {
666+ this . logger . logServiceCall ( "UiProvider" , "loginWithGitHub" , "failed" ) ;
667+ this . _view ?. webview . postMessage ( {
668+ command : "authCompleted" ,
669+ success : false ,
670+ message : "GitHub authentication failed or was cancelled." ,
671+ } ) ;
672+ }
673+ } catch ( error ) {
674+ this . logger . logServiceCall ( "UiProvider" , "loginWithGitHub" , "failed" , error ) ;
675+ this . _view ?. webview . postMessage ( {
676+ command : "authCompleted" ,
677+ success : false ,
678+ message : `GitHub authentication failed: ${ error instanceof Error ? error . message : String ( error ) } ` ,
679+ } ) ;
680+ }
681+ }
682+
596683 private mapGitHubSeverityToLocal ( severity ?: string ) : string {
597684 if ( ! severity ) return "medium" ;
598685
@@ -927,6 +1014,44 @@ export class UiProvider implements vscode.WebviewViewProvider {
9271014 50% { opacity: 0.7; transform: scale(1.05); }
9281015 }
9291016
1017+ /* GitHub Login Button */
1018+ .github-login-btn {
1019+ background: linear-gradient(135deg, #2F4858 0%, #333 100%);
1020+ color: white;
1021+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
1022+ position: relative;
1023+ border: 1px solid rgba(255, 255, 255, 0.2);
1024+ }
1025+
1026+ .github-login-btn.authenticated {
1027+ background: linear-gradient(135deg, #28a745 0%, #22863a 100%);
1028+ border: 1px solid rgba(40, 167, 69, 0.5);
1029+ box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3);
1030+ }
1031+
1032+ .github-login-btn:hover:not(:disabled) {
1033+ background: linear-gradient(135deg, #24292e 0%, #1b1f23 100%);
1034+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
1035+ }
1036+
1037+ .github-login-btn.authenticated:hover:not(:disabled) {
1038+ background: linear-gradient(135deg, #22863a 0%, #1e7e34 100%);
1039+ box-shadow: 0 6px 20px rgba(40, 167, 69, 0.4);
1040+ }
1041+
1042+ .github-login-btn:disabled {
1043+ background: linear-gradient(135deg, #9ca3af 0%, #6b7280 100%);
1044+ }
1045+
1046+ .github-icon {
1047+ font-size: 16px;
1048+ transition: all 0.3s ease;
1049+ }
1050+
1051+ .github-login-btn:hover:not(:disabled) .github-icon {
1052+ transform: scale(1.1);
1053+ }
1054+
9301055 /* Fetch Remote Button */
9311056 #fetchButton {
9321057 background: linear-gradient(135deg, #48bb78 0%, #38a169 100%);
@@ -1841,6 +1966,12 @@ export class UiProvider implements vscode.WebviewViewProvider {
18411966 <div class="section scan-section">
18421967 <h3>🚀 Actions</h3>
18431968 <div class="button-group">
1969+ <div class="button-row">
1970+ <button onclick="loginWithGitHub()" id="loginButton" class="action-button github-login-btn">
1971+ <span class="github-icon">G</span>
1972+ <span>Sign in with GitHub</span>
1973+ </button>
1974+ </div>
18441975 <div class="button-row">
18451976 <button onclick="runLocalScan()" id="scanButton" class="action-button">
18461977 <span class="scan-icon">🔍</span>
@@ -2212,6 +2343,55 @@ export class UiProvider implements vscode.WebviewViewProvider {
22122343 vscode.postMessage({ command: 'testConnection' });
22132344 }
22142345
2346+ function updateLoginButtonState(isAuthenticated, displayName) {
2347+ const loginButton = document.getElementById('loginButton');
2348+ if (!loginButton) return;
2349+
2350+ const loginIcon = loginButton.querySelector('.github-icon');
2351+ const loginText = loginButton.querySelector('span:last-child');
2352+
2353+ if (isAuthenticated) {
2354+ loginIcon.textContent = '✓';
2355+ if (displayName) {
2356+ loginText.textContent = 'Signed in as ' + displayName;
2357+ } else {
2358+ loginText.textContent = 'Signed in to GitHub';
2359+ }
2360+ loginButton.classList.add('authenticated');
2361+
2362+ // Change button action to log out or switch accounts
2363+ loginButton.onclick = function() {
2364+ // Just trigger login again - VS Code will handle account selection
2365+ loginWithGitHub();
2366+ };
2367+ } else {
2368+ loginIcon.textContent = 'G';
2369+ loginText.textContent = 'Sign in with GitHub';
2370+ loginButton.classList.remove('authenticated');
2371+
2372+ // Reset button action to login
2373+ loginButton.onclick = function() {
2374+ loginWithGitHub();
2375+ };
2376+ }
2377+ }
2378+
2379+ function loginWithGitHub() {
2380+ const loginButton = document.getElementById('loginButton');
2381+ if (loginButton) {
2382+ loginButton.disabled = true;
2383+ loginButton.classList.add('loading');
2384+
2385+ // Update text and icon
2386+ const loginIcon = loginButton.querySelector('.github-icon');
2387+ const loginText = loginButton.querySelector('span:last-child');
2388+ loginIcon.textContent = '...';
2389+ loginText.textContent = 'Authenticating...';
2390+ }
2391+
2392+ vscode.postMessage({ command: 'loginWithGitHub' });
2393+ }
2394+
22152395 function runLocalScan() {
22162396 const scanButton = document.getElementById('scanButton');
22172397 scanButton.disabled = true;
@@ -2454,6 +2634,53 @@ export class UiProvider implements vscode.WebviewViewProvider {
24542634 showMessage(message.message, !message.success);
24552635 break;
24562636
2637+ case 'authStarted':
2638+ showMessage(message.message, false);
2639+ break;
2640+
2641+ case 'authStatus':
2642+ updateLoginButtonState(message.isAuthenticated, message.displayName);
2643+ break;
2644+
2645+ case 'authCompleted':
2646+ const loginButton = document.getElementById('loginButton');
2647+ if (loginButton) {
2648+ loginButton.disabled = false;
2649+ loginButton.classList.remove('loading');
2650+
2651+ // Add success or error animation
2652+ if (message.success) {
2653+ loginButton.classList.add('success');
2654+ setTimeout(() => loginButton.classList.remove('success'), 600);
2655+
2656+ // Update the button to show logged-in state
2657+ updateLoginButtonState(true, message.config?.githubOwner);
2658+ } else {
2659+ loginButton.classList.add('error');
2660+ setTimeout(() => loginButton.classList.remove('error'), 600);
2661+
2662+ const loginIcon = loginButton.querySelector('.github-icon');
2663+ const loginText = loginButton.querySelector('span:last-child');
2664+
2665+ loginIcon.textContent = 'X';
2666+ loginText.textContent = 'Sign in Failed';
2667+
2668+ // Reset to normal state after 3 seconds
2669+ setTimeout(() => {
2670+ loginIcon.textContent = 'G';
2671+ loginText.textContent = 'Sign in with GitHub';
2672+ }, 3000);
2673+ }
2674+ }
2675+
2676+ showMessage(message.message, !message.success);
2677+
2678+ // If authentication was successful, reload configuration to update UI
2679+ if (message.success && message.config) {
2680+ loadConfig();
2681+ }
2682+ break;
2683+
24572684 case 'scanStarted':
24582685 showMessage(message.message, false);
24592686 break;
0 commit comments