fix: refresh Claude auth status after login flow (#617)

* fix: refresh Claude auth status after login flow

* fix: rely on refreshed auth status after login

---------

Co-authored-by: HolyCode User <noreply@holycode.local>
This commit is contained in:
CoderLuii
2026-05-31 07:17:27 -04:00
committed by GitHub
parent dbc41dc91d
commit 1e125f3db5
3 changed files with 52 additions and 16 deletions

View File

@@ -16,6 +16,10 @@ type ClaudeCredentialsStatus = {
error?: string; error?: string;
}; };
const hasErrorCode = (error: unknown, code: string): boolean => (
error instanceof Error && 'code' in error && error.code === code
);
export class ClaudeProviderAuth implements IProviderAuth { export class ClaudeProviderAuth implements IProviderAuth {
/** /**
* Checks whether the Claude Code CLI is available on this host. * Checks whether the Claude Code CLI is available on this host.
@@ -77,6 +81,8 @@ export class ClaudeProviderAuth implements IProviderAuth {
* Checks Claude credentials in the same priority order used by Claude Code. * Checks Claude credentials in the same priority order used by Claude Code.
*/ */
private async checkCredentials(): Promise<ClaudeCredentialsStatus> { private async checkCredentials(): Promise<ClaudeCredentialsStatus> {
const missingCredentialsError = 'Claude CLI is not authenticated. Run claude /login or configure ANTHROPIC_API_KEY.';
if (process.env.ANTHROPIC_API_KEY?.trim()) { if (process.env.ANTHROPIC_API_KEY?.trim()) {
return { authenticated: true, email: 'API Key Auth', method: 'api_key' }; return { authenticated: true, email: 'API Key Auth', method: 'api_key' };
} }
@@ -110,15 +116,33 @@ export class ClaudeProviderAuth implements IProviderAuth {
return { return {
authenticated: false, authenticated: false,
email, email: null,
method: 'credentials_file', method: null,
error: 'OAuth token has expired. Please re-authenticate with claude login', error: 'Claude login has expired. Run claude /login again.',
}; };
} }
return { authenticated: false, email: null, method: null }; return {
} catch { authenticated: false,
return { authenticated: false, email: null, method: null }; email: null,
method: null,
error: missingCredentialsError,
};
} catch (error) {
let errorMessage = 'Unable to read Claude credentials. Run claude /login again.';
if (hasErrorCode(error, 'ENOENT')) {
errorMessage = missingCredentialsError;
} else if (error instanceof SyntaxError) {
errorMessage = 'Claude credentials are unreadable. Run claude /login again.';
}
return {
authenticated: false,
email: null,
method: null,
error: errorMessage,
};
} }
} }
} }

View File

@@ -70,34 +70,39 @@ export function useProviderAuthStatus(
})); }));
}, []); }, []);
const checkProviderAuthStatus = useCallback(async (provider: LLMProvider) => { const checkProviderAuthStatus = useCallback(async (provider: LLMProvider): Promise<ProviderAuthStatus> => {
setProviderLoading(provider); setProviderLoading(provider);
try { try {
const response = await authenticatedFetch(PROVIDER_AUTH_STATUS_ENDPOINTS[provider]); const response = await authenticatedFetch(PROVIDER_AUTH_STATUS_ENDPOINTS[provider]);
if (!response.ok) { if (!response.ok) {
setProviderStatus(provider, { const status: ProviderAuthStatus = {
authenticated: false, authenticated: false,
email: null, email: null,
method: null, method: null,
loading: false, loading: false,
error: FALLBACK_STATUS_ERROR, error: FALLBACK_STATUS_ERROR,
}); };
return; setProviderStatus(provider, status);
return status;
} }
const payload = (await response.json()) as ProviderAuthStatusApiResponse; const payload = (await response.json()) as ProviderAuthStatusApiResponse;
setProviderStatus(provider, toProviderAuthStatus(payload.data)); const status = toProviderAuthStatus(payload.data);
setProviderStatus(provider, status);
return status;
} catch (caughtError) { } catch (caughtError) {
console.error(`Error checking ${provider} auth status:`, caughtError); console.error(`Error checking ${provider} auth status:`, caughtError);
setProviderStatus(provider, { const status: ProviderAuthStatus = {
authenticated: false, authenticated: false,
email: null, email: null,
method: null, method: null,
loading: false, loading: false,
error: toErrorMessage(caughtError), error: toErrorMessage(caughtError),
}); };
setProviderStatus(provider, status);
return status;
} }
}, [setProviderLoading, setProviderStatus]); }, [setProviderLoading, setProviderStatus]);

View File

@@ -213,12 +213,19 @@ export function useSettingsController({ isOpen, initialTab }: UseSettingsControl
}, []); }, []);
const handleLoginComplete = useCallback((exitCode: number) => { const handleLoginComplete = useCallback((exitCode: number) => {
if (exitCode !== 0 || !loginProvider) { if (!loginProvider) {
return; return;
} }
setSaveStatus('success'); void (async () => {
void checkProviderAuthStatus(loginProvider); const authStatus = await checkProviderAuthStatus(loginProvider);
if (exitCode !== 0) {
console.warn(`Login process exited with code ${exitCode}; refreshing auth status before setting save status.`);
}
setSaveStatus(authStatus.authenticated ? 'success' : 'error');
})();
}, [checkProviderAuthStatus, loginProvider]); }, [checkProviderAuthStatus, loginProvider]);
const saveSettings = useCallback(async () => { const saveSettings = useCallback(async () => {