Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 35 additions & 8 deletions server/routes/cli-auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,29 @@ async function checkClaudeCredentials() {
// Priority 2: Check ~/.claude/.credentials.json for OAuth tokens
// This is the standard authentication method used by Claude CLI after running
// 'claude /login' or 'claude setup-token' commands.
const notAuthenticatedResult = {
authenticated: false,
email: null,
method: null,
error: 'Claude CLI is not authenticated. Run claude /login or configure ANTHROPIC_API_KEY.'
};

const credPath = path.join(os.homedir(), '.claude', '.credentials.json');

try {
const credPath = path.join(os.homedir(), '.claude', '.credentials.json');
const content = await fs.readFile(credPath, 'utf8');
const creds = JSON.parse(content);
let creds;

try {
creds = JSON.parse(content);
} catch {
return {
authenticated: false,
email: null,
method: null,
error: 'Claude credentials are unreadable. Run claude /login again.'
};
}

const oauth = creds.claudeAiOauth;
if (oauth && oauth.accessToken) {
Expand All @@ -185,18 +204,26 @@ async function checkClaudeCredentials() {
method: 'credentials_file'
};
}

return {
authenticated: false,
email: null,
method: null,
error: 'Claude login has expired. Run claude /login again.'
};
}

return {
authenticated: false,
email: null,
method: null
};
return notAuthenticatedResult;
} catch (error) {
if (error?.code === 'ENOENT') {
return notAuthenticatedResult;
}

return {
authenticated: false,
email: null,
method: null
method: null,
error: 'Unable to read Claude credentials. Run claude /login again.'
};
}
}
Expand Down
32 changes: 21 additions & 11 deletions src/components/settings/hooks/useSettingsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,36 +268,41 @@ export function useSettingsController({ isOpen, initialTab, projects, onClose }:
setCodexAuthStatus(status);
}, []);

const checkAuthStatus = useCallback(async (provider: AgentProvider) => {
const checkAuthStatus = useCallback(async (provider: AgentProvider): Promise<AuthStatus> => {
try {
const response = await authenticatedFetch(AUTH_STATUS_ENDPOINTS[provider]);

if (!response.ok) {
setAuthStatusByProvider(provider, {
const status = {
authenticated: false,
email: null,
loading: false,
error: 'Failed to check authentication status',
});
return;
};
setAuthStatusByProvider(provider, status);
return status;
}

const data = await toResponseJson<StatusApiResponse>(response);
setAuthStatusByProvider(provider, {
const status = {
authenticated: Boolean(data.authenticated),
email: data.email || null,
loading: false,
error: data.error || null,
method: data.method,
});
};
setAuthStatusByProvider(provider, status);
return status;
} catch (error) {
console.error(`Error checking ${provider} auth status:`, error);
setAuthStatusByProvider(provider, {
const status = {
authenticated: false,
email: null,
loading: false,
error: getErrorMessage(error),
});
};
setAuthStatusByProvider(provider, status);
return status;
}
}, [setAuthStatusByProvider]);

Expand Down Expand Up @@ -729,12 +734,17 @@ export function useSettingsController({ isOpen, initialTab, projects, onClose }:
}, [projects]);

const handleLoginComplete = useCallback((exitCode: number) => {
if (exitCode !== 0 || !loginProvider) {
if (!loginProvider) {
return;
}

setSaveStatus('success');
void checkAuthStatus(loginProvider);
void (async () => {
const authStatus = await checkAuthStatus(loginProvider);

if (exitCode === 0) {
setSaveStatus(authStatus.authenticated ? 'success' : 'error');
}
Comment on lines +744 to +746
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t gate final login outcome on process exit code after auth re-check.

At Line 744, requiring exitCode === 0 can hide a valid login when checkAuthStatus() confirms authentication, and non-zero exits skip setting an explicit error state.

Suggested fix
-      if (exitCode === 0) {
-        setSaveStatus(authStatus.authenticated ? 'success' : 'error');
-      }
+      setSaveStatus(authStatus.authenticated ? 'success' : 'error');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (exitCode === 0) {
setSaveStatus(authStatus.authenticated ? 'success' : 'error');
}
setSaveStatus(authStatus.authenticated ? 'success' : 'error');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/settings/hooks/useSettingsController.ts` around lines 744 -
746, The code currently only updates save status when exitCode === 0 which can
mask a successful auth re-check; remove the exitCode gating in the block around
checkAuthStatus()/authStatus and always setSaveStatus(authStatus.authenticated ?
'success' : 'error') based on the result of checkAuthStatus(); locate the logic
in useSettingsController.ts where exitCode and authStatus are used and update
the conditional that calls setSaveStatus (and optionally log non-zero exitCode
separately) so final login outcome relies on authStatus.authenticated not
exitCode.

})();
}, [checkAuthStatus, loginProvider]);

const saveSettings = useCallback(async () => {
Expand Down