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:
@@ -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');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user