From 1e8ef41e648be3259a6e97974c1264564da5108b Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Fri, 3 Apr 2026 15:04:12 -0500 Subject: [PATCH] feat: add OAuth2 frontend UI and complete integration flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 2.2: OAuth2 flow - FRONTEND COMPLETE ✅ Implemented: - TypeScript command wrappers in tauriCommands.ts * initiateOauthCmd(service) -> OAuthInitResponse * handleOauthCallbackCmd(service, code, stateKey) * test*ConnectionCmd() for all services * OAuthInitResponse and ConnectionResult types - Complete Settings/Integrations UI * Three integration cards: Confluence, ServiceNow, ADO * Connect with OAuth2 buttons (Confluence, ADO) * Basic auth note for ServiceNow * Configuration inputs: baseUrl, username, projectName, spaceKey * Test connection buttons with loading states * Success/error feedback with color-coded messages * OAuth2 flow instructions for users - OAuth2 flow in browser * Opens auth URL in default browser via shell plugin * User authenticates with service * Redirected to localhost:8765/callback * Callback server handles token exchange automatically * Success message shown to user - CSP updates in tauri.conf.json * Added http://localhost:8765 (callback server) * Added https://auth.atlassian.com (Confluence OAuth) * Added https://*.atlassian.net (Confluence API) * Added https://login.microsoftonline.com (ADO OAuth) * Added https://dev.azure.com (ADO API) - UI improvements * Fixed Cancel button variant (ghost instead of secondary) * Loading spinners with Loader2 icon * Check/X icons for success/error states * Disabled states when not configured * Optimistic UI updates on connect Frontend + Backend = COMPLETE END-TO-END OAUTH2 FLOW: 1. User goes to Settings → Integrations 2. Enters base URL and config 3. Clicks 'Connect with OAuth2' 4. Browser opens with service auth page 5. User logs in and authorizes 6. Redirected to localhost:8765/callback 7. Token exchanged and encrypted automatically 8. Stored in SQLite credentials table 9. Ready for API calls to external services ✅ TypeScript: All types checked, no errors Frontend build: ✅ Built in 2.26s Total lines: ~400 lines of new UI code Next: Phase 2.3 - Integration API clients (Confluence REST, ServiceNow REST, ADO REST) --- src-tauri/tauri.conf.json | 2 +- src/lib/tauriCommands.ts | 27 ++ src/pages/NewIssue/index.tsx | 2 +- src/pages/Settings/Integrations.tsx | 457 ++++++++++++++++++++++++---- 4 files changed, 435 insertions(+), 53 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index d40213f8..8d1f6343 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -10,7 +10,7 @@ }, "app": { "security": { - "csp": "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: asset: https:; connect-src 'self' http://localhost:11434 https://api.openai.com https://api.anthropic.com https://api.mistral.ai https://generativelanguage.googleapis.com" + "csp": "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: asset: https:; connect-src 'self' http://localhost:11434 http://localhost:8765 https://api.openai.com https://api.anthropic.com https://api.mistral.ai https://generativelanguage.googleapis.com https://auth.atlassian.com https://*.atlassian.net https://login.microsoftonline.com https://dev.azure.com" }, "windows": [ { diff --git a/src/lib/tauriCommands.ts b/src/lib/tauriCommands.ts index e5c3c109..9c04741b 100644 --- a/src/lib/tauriCommands.ts +++ b/src/lib/tauriCommands.ts @@ -360,3 +360,30 @@ export const updateSettingsCmd = (partialSettings: Partial) => export const getAuditLogCmd = (filter: AuditFilter) => invoke("get_audit_log", { filter }); + +// ─── OAuth & Integrations ───────────────────────────────────────────────────── + +export interface OAuthInitResponse { + auth_url: string; + state: string; +} + +export interface ConnectionResult { + success: boolean; + message: string; +} + +export const initiateOauthCmd = (service: string) => + invoke("initiate_oauth", { service }); + +export const handleOauthCallbackCmd = (service: string, code: string, stateKey: string) => + invoke("handle_oauth_callback", { service, code, stateKey }); + +export const testConfluenceConnectionCmd = (baseUrl: string, credentials: Record) => + invoke("test_confluence_connection", { baseUrl, credentials }); + +export const testServiceNowConnectionCmd = (instanceUrl: string, credentials: Record) => + invoke("test_servicenow_connection", { instanceUrl, credentials }); + +export const testAzureDevOpsConnectionCmd = (orgUrl: string, credentials: Record) => + invoke("test_azuredevops_connection", { orgUrl, credentials }); diff --git a/src/pages/NewIssue/index.tsx b/src/pages/NewIssue/index.tsx index 44dc38e8..782d20ed 100644 --- a/src/pages/NewIssue/index.tsx +++ b/src/pages/NewIssue/index.tsx @@ -162,7 +162,7 @@ export default function NewIssue() { + + + + + {testResults.confluence && ( +
+ {testResults.confluence.success ? ( + + ) : ( + + )} + {testResults.confluence.message}
- - - - {integration.name} - - {integration.description} - - -
    - {integration.features.map((feature) => ( -
  • -
    - {feature} -
  • - ))} -
-
- - ))} + )} + + + + {/* ServiceNow */} + + + + + ServiceNow + + + Link incidents and push resolution steps. Uses basic authentication (username + password). + + + +
+ + updateConfig("servicenow", "baseUrl", e.target.value)} + /> +
+ +
+ + updateConfig("servicenow", "username", e.target.value)} + /> +
+ +
+ + +

+ ServiceNow credentials are stored securely after first login. OAuth2 not supported. +

+
+ +
+ + + +
+ + {testResults.servicenow && ( +
+ {testResults.servicenow.success ? ( + + ) : ( + + )} + {testResults.servicenow.message} +
+ )} +
+
+ + {/* Azure DevOps */} + + + + + Azure DevOps + + + Create work items and attach RCA documents. Requires OAuth2 authentication with Microsoft. + + + +
+ + updateConfig("azuredevops", "baseUrl", e.target.value)} + /> +
+ +
+ + updateConfig("azuredevops", "projectName", e.target.value)} + /> +
+ +
+ + + +
+ + {testResults.azuredevops && ( +
+ {testResults.azuredevops.success ? ( + + ) : ( + + )} + {testResults.azuredevops.message} +
+ )} +
+
+ +
+

How OAuth2 Authentication Works:

+
    +
  1. Click "Connect with OAuth2" to open the service's authentication page
  2. +
  3. Log in with your service credentials in your default browser
  4. +
  5. Authorize TFTSR to access your account
  6. +
  7. You'll be automatically redirected back and the connection will be saved
  8. +
  9. Tokens are encrypted and stored locally in your secure database
  10. +
);