fix: check .claude.json for OAuth auth in profile scorer (#652)

* fix: check .claude.json for OAuth auth in profile scorer

The isProfileAuthenticated() function only checked legacy credential files
(credentials, credentials.json, .credentials, settings.json) when determining
if a profile is eligible for auto-switch.

Claude Code CLI (v1.0+) stores OAuth authentication in .claude.json with an
oauthAccount field containing accountUuid and emailAddress. This meant profiles
authenticated via OAuth were silently rejected by the profile scorer, causing
auto-switch to fail even when 'Reactive Recovery' was enabled.

This fix adds a check for .claude.json containing oauthAccount info before
falling through to legacy credential file checks.

Fixes incomplete resolution of #365 and #43

* fix: add type validation and error logging per review feedback

- Add typeof check before accessing oauthAccount properties
- Log parse errors with console.warn for debugging malformed .claude.json

Co-authored-by: gemini-code-assist[bot] <176abortvfax+gemini-code-assist[bot]@Fusers.noreply.github.com>

---------

Co-authored-by: gemini-code-assist[bot] <176abortvfax+gemini-code-assist[bot]@Fusers.noreply.github.com>
Co-authored-by: Andy <119136210+AndyMik90@users.noreply.github.com>
This commit is contained in:
Michael Ludlow
2026-01-04 14:35:38 -05:00
committed by GitHub
parent e1e8943051
commit eeef8a3d4a
@@ -56,7 +56,7 @@ export async function createProfileDirectory(profileName: string): Promise<strin
/** /**
* Check if a profile has valid authentication * Check if a profile has valid authentication
* (checks if the config directory has credential files) * (checks if the config directory has credential files or OAuth account info)
*/ */
export function isProfileAuthenticated(profile: ClaudeProfile): boolean { export function isProfileAuthenticated(profile: ClaudeProfile): boolean {
const configDir = profile.configDir; const configDir = profile.configDir;
@@ -64,7 +64,24 @@ export function isProfileAuthenticated(profile: ClaudeProfile): boolean {
return false; return false;
} }
// Claude stores auth in .claude/credentials or similar files // Check for .claude.json with OAuth account info (modern Claude Code CLI)
// This is how Claude Code CLI stores OAuth authentication since v1.0
const claudeJsonPath = join(configDir, '.claude.json');
if (existsSync(claudeJsonPath)) {
try {
const content = readFileSync(claudeJsonPath, 'utf-8');
const data = JSON.parse(content);
// Check for oauthAccount which indicates successful OAuth authentication
if (data && typeof data === 'object' && (data.oauthAccount?.accountUuid || data.oauthAccount?.emailAddress)) {
return true;
}
} catch (error) {
// Log parse errors for debugging, but fall through to legacy checks
console.warn(`[profile-utils] Failed to read or parse ${claudeJsonPath}:`, error);
}
}
// Legacy: Claude stores auth in .claude/credentials or similar files
// Check for common auth indicators // Check for common auth indicators
const possibleAuthFiles = [ const possibleAuthFiles = [
join(configDir, 'credentials'), join(configDir, 'credentials'),