Feature: Secret Key User System & Inline Qwen OAuth

Phase 1: User Authentication System
- Added user-data.js: Secret code generation, user creation, session management
- Added UserAuth.tsx: LoginGate, onboarding wizard, secret code reveal dialog
- Users get isolated environments (projects, chats, API keys)

Phase 2: Inline Qwen OAuth (No External CLI)
- Added qwen-oauth.js: Device Authorization Grant with PKCE
- Added QwenAuthDialog.tsx: Full inline auth flow with user code display
- Tokens saved per-user with legacy fallback

Phase 3: Integration
- Updated main.js with IPC handlers for user auth and Qwen OAuth
- Updated preload.js with electron.user and electron.qwenAuth bridges
- Wrapped App.tsx with LoginGate for authentication enforcement

Based on analysis of qwen-code repository OAuth implementation.
This commit is contained in:
Gemini AI
2025-12-20 18:31:50 +04:00
Unverified
parent f35f7bd6c5
commit b6f2c68243
9 changed files with 2863 additions and 5 deletions

View File

@@ -97,5 +97,64 @@ contextBridge.exposeInMainWorld('electron', {
getKeyStatus: () => ipcRenderer.invoke('ollama-get-key-status'),
saveKey: (key) => ipcRenderer.invoke('ollama-save-key', { key }),
getModels: () => ipcRenderer.invoke('ollama-get-models')
},
// ==========================================
// USER AUTHENTICATION SYSTEM
// ==========================================
user: {
// Get list of secret questions
getSecretQuestions: () => ipcRenderer.invoke('user-get-secret-questions'),
// Create a new account (returns { success, user, secretCode, session } or { success: false, error })
create: (displayName, questionId, answer) =>
ipcRenderer.invoke('user-create', { displayName, questionId, answer }),
// Login with secret code (returns { success, user, session } or { success: false, error })
login: (secretCode) => ipcRenderer.invoke('user-login', { secretCode }),
// Get current session (returns session object or null)
getSession: () => ipcRenderer.invoke('user-get-session'),
// Logout (optionally clean data)
logout: (cleanData = false) => ipcRenderer.invoke('user-logout', { cleanData }),
// Get user statistics
getStats: (userId) => ipcRenderer.invoke('user-get-stats', { userId }),
// Clean all user data (projects, chats, keys)
cleanData: (userId) => ipcRenderer.invoke('user-clean-data', { userId }),
// Get user's projects directory path
getProjectsDir: (userId) => ipcRenderer.invoke('user-get-projects-dir', { userId })
},
// ==========================================
// QWEN OAUTH (INLINE DEVICE FLOW)
// ==========================================
qwenAuth: {
// Start the device authorization flow (triggers browser open)
start: () => ipcRenderer.send('qwen-auth-start'),
// Cancel ongoing authorization
cancel: () => ipcRenderer.send('qwen-auth-cancel'),
// Get current auth status
getStatus: () => ipcRenderer.invoke('qwen-get-auth-status'),
// Clear saved tokens
clearTokens: () => ipcRenderer.invoke('qwen-clear-tokens'),
// Event listeners for auth flow
onProgress: (callback) => ipcRenderer.on('qwen-auth-progress', (_, data) => callback(data)),
onSuccess: (callback) => ipcRenderer.on('qwen-auth-success', (_, creds) => callback(creds)),
onError: (callback) => ipcRenderer.on('qwen-auth-error', (_, error) => callback(error)),
// Cleanup listeners
removeListeners: () => {
ipcRenderer.removeAllListeners('qwen-auth-progress');
ipcRenderer.removeAllListeners('qwen-auth-success');
ipcRenderer.removeAllListeners('qwen-auth-error');
}
}
});