mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 21:03:08 +00:00
fix(auth): Improve OAuth credential detection and startup warning
- Enhanced getClaudeAuthIndicators() to return detailed check information including file paths checked and specific error details for debugging - Added debug logging to server startup credential detection for easier troubleshooting in Docker environments - Show paths that were checked in the warning message to help users debug mount issues - Added support for CLAUDE_CODE_OAUTH_TOKEN environment variable - Return authType in verify-claude-auth response to distinguish between OAuth and CLI authentication methods - Updated UI to show specific success messages for Claude Code subscription vs generic CLI auth - Added Docker troubleshooting tips to sandbox risk dialog - Added comprehensive unit tests for OAuth credential detection scenarios Closes #721 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -121,21 +121,89 @@ const BOX_CONTENT_WIDTH = 67;
|
||||
// The Claude Agent SDK can use either ANTHROPIC_API_KEY or Claude Code CLI authentication
|
||||
(async () => {
|
||||
const hasAnthropicKey = !!process.env.ANTHROPIC_API_KEY;
|
||||
const hasEnvOAuthToken = !!process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
||||
|
||||
logger.debug('[CREDENTIAL_CHECK] Starting credential detection...');
|
||||
logger.debug('[CREDENTIAL_CHECK] Environment variables:', {
|
||||
hasAnthropicKey,
|
||||
hasEnvOAuthToken,
|
||||
});
|
||||
|
||||
if (hasAnthropicKey) {
|
||||
logger.info('✓ ANTHROPIC_API_KEY detected');
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasEnvOAuthToken) {
|
||||
logger.info('✓ CLAUDE_CODE_OAUTH_TOKEN detected');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for Claude Code CLI authentication
|
||||
// Store indicators outside the try block so we can use them in the warning message
|
||||
let cliAuthIndicators: Awaited<ReturnType<typeof getClaudeAuthIndicators>> | null = null;
|
||||
|
||||
try {
|
||||
const indicators = await getClaudeAuthIndicators();
|
||||
cliAuthIndicators = await getClaudeAuthIndicators();
|
||||
const indicators = cliAuthIndicators;
|
||||
|
||||
// Log detailed credential detection results
|
||||
logger.debug('[CREDENTIAL_CHECK] Claude CLI auth indicators:', {
|
||||
hasCredentialsFile: indicators.hasCredentialsFile,
|
||||
hasSettingsFile: indicators.hasSettingsFile,
|
||||
hasStatsCacheWithActivity: indicators.hasStatsCacheWithActivity,
|
||||
hasProjectsSessions: indicators.hasProjectsSessions,
|
||||
credentials: indicators.credentials,
|
||||
});
|
||||
|
||||
logger.debug('[CREDENTIAL_CHECK] File check details:', {
|
||||
settingsFile: {
|
||||
path: indicators.checks.settingsFile.path,
|
||||
exists: indicators.checks.settingsFile.exists,
|
||||
readable: indicators.checks.settingsFile.readable,
|
||||
error: indicators.checks.settingsFile.error,
|
||||
},
|
||||
statsCache: {
|
||||
path: indicators.checks.statsCache.path,
|
||||
exists: indicators.checks.statsCache.exists,
|
||||
readable: indicators.checks.statsCache.readable,
|
||||
hasDailyActivity: indicators.checks.statsCache.hasDailyActivity,
|
||||
error: indicators.checks.statsCache.error,
|
||||
},
|
||||
projectsDir: {
|
||||
path: indicators.checks.projectsDir.path,
|
||||
exists: indicators.checks.projectsDir.exists,
|
||||
readable: indicators.checks.projectsDir.readable,
|
||||
entryCount: indicators.checks.projectsDir.entryCount,
|
||||
error: indicators.checks.projectsDir.error,
|
||||
},
|
||||
credentialFiles: indicators.checks.credentialFiles.map((cf) => ({
|
||||
path: cf.path,
|
||||
exists: cf.exists,
|
||||
readable: cf.readable,
|
||||
error: cf.error,
|
||||
})),
|
||||
});
|
||||
|
||||
const hasCliAuth =
|
||||
indicators.hasStatsCacheWithActivity ||
|
||||
(indicators.hasSettingsFile && indicators.hasProjectsSessions) ||
|
||||
(indicators.hasCredentialsFile &&
|
||||
(indicators.credentials?.hasOAuthToken || indicators.credentials?.hasApiKey));
|
||||
|
||||
logger.debug('[CREDENTIAL_CHECK] Auth determination:', {
|
||||
hasCliAuth,
|
||||
reason: hasCliAuth
|
||||
? indicators.hasStatsCacheWithActivity
|
||||
? 'stats cache with activity'
|
||||
: indicators.hasSettingsFile && indicators.hasProjectsSessions
|
||||
? 'settings file + project sessions'
|
||||
: indicators.credentials?.hasOAuthToken
|
||||
? 'credentials file with OAuth token'
|
||||
: 'credentials file with API key'
|
||||
: 'no valid credentials found',
|
||||
});
|
||||
|
||||
if (hasCliAuth) {
|
||||
logger.info('✓ Claude Code CLI authentication detected');
|
||||
return;
|
||||
@@ -145,7 +213,7 @@ const BOX_CONTENT_WIDTH = 67;
|
||||
logger.warn('Error checking for Claude Code CLI authentication:', error);
|
||||
}
|
||||
|
||||
// No authentication found - show warning
|
||||
// No authentication found - show warning with paths that were checked
|
||||
const wHeader = '⚠️ WARNING: No Claude authentication configured'.padEnd(BOX_CONTENT_WIDTH);
|
||||
const w1 = 'The Claude Agent SDK requires authentication to function.'.padEnd(BOX_CONTENT_WIDTH);
|
||||
const w2 = 'Options:'.padEnd(BOX_CONTENT_WIDTH);
|
||||
@@ -158,6 +226,33 @@ const BOX_CONTENT_WIDTH = 67;
|
||||
BOX_CONTENT_WIDTH
|
||||
);
|
||||
|
||||
// Build paths checked summary from the indicators (if available)
|
||||
let pathsCheckedInfo = '';
|
||||
if (cliAuthIndicators) {
|
||||
const pathsChecked: string[] = [];
|
||||
|
||||
// Collect paths that were checked
|
||||
if (cliAuthIndicators.checks.settingsFile.path) {
|
||||
pathsChecked.push(`Settings: ${cliAuthIndicators.checks.settingsFile.path}`);
|
||||
}
|
||||
if (cliAuthIndicators.checks.statsCache.path) {
|
||||
pathsChecked.push(`Stats cache: ${cliAuthIndicators.checks.statsCache.path}`);
|
||||
}
|
||||
if (cliAuthIndicators.checks.projectsDir.path) {
|
||||
pathsChecked.push(`Projects dir: ${cliAuthIndicators.checks.projectsDir.path}`);
|
||||
}
|
||||
for (const credFile of cliAuthIndicators.checks.credentialFiles) {
|
||||
pathsChecked.push(`Credentials: ${credFile.path}`);
|
||||
}
|
||||
|
||||
if (pathsChecked.length > 0) {
|
||||
pathsCheckedInfo = `
|
||||
║ ║
|
||||
║ ${'Paths checked:'.padEnd(BOX_CONTENT_WIDTH)}║
|
||||
${pathsChecked.map((p) => `║ ${p.substring(0, BOX_CONTENT_WIDTH - 2).padEnd(BOX_CONTENT_WIDTH - 2)} ║`).join('\n')}`;
|
||||
}
|
||||
}
|
||||
|
||||
logger.warn(`
|
||||
╔═════════════════════════════════════════════════════════════════════╗
|
||||
║ ${wHeader}║
|
||||
@@ -169,7 +264,7 @@ const BOX_CONTENT_WIDTH = 67;
|
||||
║ ${w3}║
|
||||
║ ${w4}║
|
||||
║ ${w5}║
|
||||
║ ${w6}║
|
||||
║ ${w6}║${pathsCheckedInfo}
|
||||
║ ║
|
||||
╚═════════════════════════════════════════════════════════════════════╝
|
||||
`);
|
||||
|
||||
@@ -320,9 +320,28 @@ export function createVerifyClaudeAuthHandler() {
|
||||
authMethod,
|
||||
});
|
||||
|
||||
// Determine specific auth type for success messages
|
||||
let authType: 'oauth' | 'api_key' | 'cli' | undefined;
|
||||
if (authenticated) {
|
||||
if (authMethod === 'api_key') {
|
||||
authType = 'api_key';
|
||||
} else if (authMethod === 'cli') {
|
||||
// Check if CLI auth is via OAuth (Claude Code subscription) or generic CLI
|
||||
// OAuth tokens are stored in the credentials file by the Claude CLI
|
||||
const { getClaudeAuthIndicators } = await import('@automaker/platform');
|
||||
const indicators = await getClaudeAuthIndicators();
|
||||
if (indicators.credentials?.hasOAuthToken) {
|
||||
authType = 'oauth';
|
||||
} else {
|
||||
authType = 'cli';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
authenticated,
|
||||
authType,
|
||||
error: errorMessage || undefined,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user