mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
refactor(auth): enhance authentication detection and status handling
- Improved the CodexCliDetector to provide detailed logging and better error handling when reading the authentication file. - Updated the authentication method determination in the settings and setup views to prioritize CLI-based methods over traditional API key methods. - Expanded the CodexAuthStatus interface to include new authentication methods, ensuring accurate representation of the authentication state. - Enhanced UI feedback in the settings view to reflect the new authentication methods, improving user experience.
This commit is contained in:
@@ -72,38 +72,109 @@ class CodexCliDetector {
|
||||
|
||||
// Check if auth file exists
|
||||
if (fs.existsSync(authPath)) {
|
||||
const content = fs.readFileSync(authPath, 'utf-8');
|
||||
const auth = JSON.parse(content);
|
||||
console.log('[CodexCliDetector] Auth file exists, reading content...');
|
||||
let auth = null;
|
||||
try {
|
||||
const content = fs.readFileSync(authPath, 'utf-8');
|
||||
auth = JSON.parse(content);
|
||||
console.log('[CodexCliDetector] Auth file content keys:', Object.keys(auth));
|
||||
console.log('[CodexCliDetector] Auth file has token object:', !!auth.token);
|
||||
if (auth.token) {
|
||||
console.log('[CodexCliDetector] Token object keys:', Object.keys(auth.token));
|
||||
}
|
||||
|
||||
// Check for token object structure (from codex auth login)
|
||||
// Structure: { token: { Id_token, access_token, refresh_token }, last_refresh: ... }
|
||||
if (auth.token && typeof auth.token === 'object') {
|
||||
const token = auth.token;
|
||||
if (token.Id_token || token.access_token || token.refresh_token || token.id_token) {
|
||||
return {
|
||||
// Check for token object structure (from codex auth login)
|
||||
// Structure: { token: { Id_token, access_token, refresh_token }, last_refresh: ... }
|
||||
if (auth.token && typeof auth.token === 'object') {
|
||||
const token = auth.token;
|
||||
if (token.Id_token || token.access_token || token.refresh_token || token.id_token) {
|
||||
const result = {
|
||||
authenticated: true,
|
||||
method: 'cli_tokens', // Distinguish token-based auth from API key auth
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (cli_tokens):', result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for tokens at root level (alternative structure)
|
||||
if (auth.access_token || auth.refresh_token || auth.Id_token || auth.id_token) {
|
||||
const result = {
|
||||
authenticated: true,
|
||||
method: 'cli_tokens', // These are tokens, not API keys
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (cli_tokens - root level):', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check for various possible API key fields that codex might use
|
||||
// Note: access_token is NOT an API key, it's a token, so we check for it above
|
||||
if (auth.api_key || auth.openai_api_key || auth.apiKey) {
|
||||
const result = {
|
||||
authenticated: true,
|
||||
method: 'auth_file',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (auth_file - API key):', result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for various possible auth fields that codex might use
|
||||
if (auth.api_key || auth.openai_api_key || auth.access_token || auth.apiKey) {
|
||||
} catch (error) {
|
||||
console.error('[CodexCliDetector] Error reading/parsing auth file:', error.message);
|
||||
// If we can't parse the file, we can't determine auth status
|
||||
return {
|
||||
authenticated: true,
|
||||
method: 'auth_file',
|
||||
hasAuthFile: true,
|
||||
authenticated: false,
|
||||
method: 'none',
|
||||
hasAuthFile: false,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
}
|
||||
|
||||
// Also check if the file has any meaningful content (non-empty object)
|
||||
// This is a fallback - but we should still try to detect if it's tokens
|
||||
if (!auth) {
|
||||
// File exists but couldn't be parsed
|
||||
return {
|
||||
authenticated: false,
|
||||
method: 'none',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
}
|
||||
|
||||
const keys = Object.keys(auth);
|
||||
console.log('[CodexCliDetector] File has content, keys:', keys);
|
||||
if (keys.length > 0) {
|
||||
// Check again for tokens in case we missed them (maybe nested differently)
|
||||
const hasTokens = keys.some(key =>
|
||||
key.toLowerCase().includes('token') ||
|
||||
key.toLowerCase().includes('refresh') ||
|
||||
(auth[key] && typeof auth[key] === 'object' && (
|
||||
auth[key].access_token || auth[key].refresh_token || auth[key].Id_token || auth[key].id_token
|
||||
))
|
||||
);
|
||||
|
||||
if (hasTokens) {
|
||||
const result = {
|
||||
authenticated: true,
|
||||
method: 'cli_tokens',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (cli_tokens - fallback detection):', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// File exists and has content, likely authenticated
|
||||
// Try to verify by checking if codex command works
|
||||
try {
|
||||
@@ -116,34 +187,45 @@ class CodexCliDetector {
|
||||
timeout: 3000
|
||||
});
|
||||
// If command succeeds, assume authenticated
|
||||
return {
|
||||
// But check if it's likely tokens vs API key based on file structure
|
||||
const likelyTokens = keys.some(key => key.toLowerCase().includes('token') || key.toLowerCase().includes('refresh'));
|
||||
const result = {
|
||||
authenticated: true,
|
||||
method: 'auth_file',
|
||||
method: likelyTokens ? 'cli_tokens' : 'auth_file',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (verified via CLI, method:', result.method, '):', result);
|
||||
return result;
|
||||
} catch (cmdError) {
|
||||
// Command failed, but file exists - might still be authenticated
|
||||
// Return authenticated if file has content
|
||||
return {
|
||||
// Check if it's likely tokens
|
||||
const likelyTokens = keys.some(key => key.toLowerCase().includes('token') || key.toLowerCase().includes('refresh'));
|
||||
const result = {
|
||||
authenticated: true,
|
||||
method: 'auth_file',
|
||||
method: likelyTokens ? 'cli_tokens' : 'auth_file',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (file exists, method:', result.method, '):', result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} catch (verifyError) {
|
||||
// Verification failed, but file exists with content
|
||||
return {
|
||||
// Check if it's likely tokens
|
||||
const likelyTokens = keys.some(key => key.toLowerCase().includes('token') || key.toLowerCase().includes('refresh'));
|
||||
const result = {
|
||||
authenticated: true,
|
||||
method: 'auth_file',
|
||||
method: likelyTokens ? 'cli_tokens' : 'auth_file',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (fallback, method:', result.method, '):', result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,10 +238,20 @@ export function SettingsView() {
|
||||
const result = await api.setup.getCodexStatus();
|
||||
if (result.success && result.auth) {
|
||||
const auth = result.auth;
|
||||
// Determine method - prioritize cli_verified and cli_tokens over auth_file
|
||||
const method = auth.method === "cli_verified" || auth.method === "cli_tokens"
|
||||
? auth.method === "cli_verified" ? "cli_verified" : "cli_tokens"
|
||||
: auth.method === "auth_file"
|
||||
? "api_key"
|
||||
: auth.method === "env_var"
|
||||
? "env"
|
||||
: "none";
|
||||
|
||||
const authStatus: CodexAuthStatus = {
|
||||
authenticated: auth.authenticated,
|
||||
method: auth.hasEnvApiKey ? "env" : auth.hasStoredApiKey ? "api_key" : "none",
|
||||
apiKeyValid: auth.hasStoredApiKey || auth.hasEnvApiKey,
|
||||
method,
|
||||
// Only set apiKeyValid for actual API key methods, not CLI login
|
||||
apiKeyValid: method === "cli_verified" || method === "cli_tokens" ? undefined : (auth.hasAuthFile || auth.hasEnvKey),
|
||||
};
|
||||
setCodexAuthStatus(authStatus);
|
||||
}
|
||||
@@ -932,7 +942,9 @@ export function SettingsView() {
|
||||
<span className="text-muted-foreground">
|
||||
Method:{" "}
|
||||
<span className="font-mono text-foreground">
|
||||
{codexAuthStatus.method === "api_key"
|
||||
{codexAuthStatus.method === "cli_verified" || codexAuthStatus.method === "cli_tokens"
|
||||
? "CLI Login (OpenAI Account)"
|
||||
: codexAuthStatus.method === "api_key"
|
||||
? "API Key (Auth File)"
|
||||
: codexAuthStatus.method === "env"
|
||||
? "API Key (Environment)"
|
||||
@@ -940,12 +952,17 @@ export function SettingsView() {
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
{codexAuthStatus.apiKeyValid && (
|
||||
{codexAuthStatus.method === "cli_verified" || codexAuthStatus.method === "cli_tokens" ? (
|
||||
<div className="flex items-center gap-2 text-green-400">
|
||||
<CheckCircle2 className="w-3 h-3 shrink-0" />
|
||||
<span>Account authenticated</span>
|
||||
</div>
|
||||
) : codexAuthStatus.apiKeyValid ? (
|
||||
<div className="flex items-center gap-2 text-green-400">
|
||||
<CheckCircle2 className="w-3 h-3 shrink-0" />
|
||||
<span>API key configured</span>
|
||||
</div>
|
||||
)}
|
||||
) : null}
|
||||
{apiKeyStatus?.hasOpenAIKey && (
|
||||
<div className="flex items-center gap-2 text-blue-400">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { useSetupStore } from "@/store/setup-store";
|
||||
import { useSetupStore, type CodexAuthStatus } from "@/store/setup-store";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
import {
|
||||
@@ -805,13 +805,22 @@ function CodexSetupStep({
|
||||
setCodexCliStatus(cliStatus);
|
||||
|
||||
if (result.auth) {
|
||||
const authStatus = {
|
||||
const method = result.auth.method === "cli_verified" || result.auth.method === "cli_tokens"
|
||||
? (result.auth.method === "cli_verified" ? "cli_verified" : "cli_tokens")
|
||||
: result.auth.method === "auth_file"
|
||||
? "api_key"
|
||||
: result.auth.method === "env_var"
|
||||
? "env"
|
||||
: "none";
|
||||
|
||||
const authStatus: CodexAuthStatus = {
|
||||
authenticated: result.auth.authenticated,
|
||||
method: result.auth.method === "auth_file" ? "api_key" : result.auth.method === "env_var" ? "env" : "none",
|
||||
apiKeyValid: result.auth.authenticated,
|
||||
method,
|
||||
// Only set apiKeyValid for actual API key methods, not CLI login
|
||||
apiKeyValid: method === "cli_verified" || method === "cli_tokens" ? undefined : result.auth.authenticated,
|
||||
};
|
||||
console.log("[Codex Setup] Auth Status:", authStatus);
|
||||
setCodexAuthStatus(authStatus as any);
|
||||
setCodexAuthStatus(authStatus);
|
||||
} else {
|
||||
console.log("[Codex Setup] No auth info in result");
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ export interface ElectronAPI {
|
||||
path?: string;
|
||||
auth?: {
|
||||
authenticated: boolean;
|
||||
method: string;
|
||||
method: string; // Can be: "cli_verified", "cli_tokens", "auth_file", "env_var", "none"
|
||||
hasAuthFile: boolean;
|
||||
hasEnvKey: boolean;
|
||||
hasStoredApiKey?: boolean;
|
||||
@@ -561,7 +561,7 @@ interface SetupAPI {
|
||||
path?: string;
|
||||
auth?: {
|
||||
authenticated: boolean;
|
||||
method: string;
|
||||
method: string; // Can be: "cli_verified", "cli_tokens", "auth_file", "env_var", "none"
|
||||
hasAuthFile: boolean;
|
||||
hasEnvKey: boolean;
|
||||
hasStoredApiKey?: boolean;
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface ClaudeAuthStatus {
|
||||
// Codex Auth Status
|
||||
export interface CodexAuthStatus {
|
||||
authenticated: boolean;
|
||||
method: "api_key" | "env" | "none";
|
||||
method: "api_key" | "env" | "cli_verified" | "cli_tokens" | "none";
|
||||
apiKeyValid?: boolean;
|
||||
mcpConfigured?: boolean;
|
||||
error?: string;
|
||||
|
||||
Reference in New Issue
Block a user