Skip to content

Commit ca6f12b

Browse files
fix(auth): resolve auth token discovery mismatch between CLI and server (#3409)
Closes #3340. CLI resolveAuthToken() now falls back to loadConfig() clientAuthToken. Server AuthManager and encryption key also check clientAuthToken.
1 parent a2ffa50 commit ca6f12b

2 files changed

Lines changed: 20 additions & 5 deletions

File tree

src/commands/run.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,25 @@ function writeLine(stream: NodeJS.WritableStream, text: string = ''): void {
3030
stream.write(`${text}\n`);
3131
}
3232

33-
function resolveAuthToken(): string | undefined {
33+
async function resolveAuthToken(): Promise<string | undefined> {
3434
const envToken = process.env.AEGIS_AUTH_TOKEN || process.env.AEGIS_TOKEN;
3535
if (envToken) return envToken;
3636

3737
// #3369: Check ~/.aegis/auth-token file as fallback
3838
try {
3939
const tokenPath = join(homedir(), '.aegis', 'auth-token');
4040
return readFileSync(tokenPath, 'utf-8').trim() || undefined;
41+
} catch {
42+
// File doesn't exist — continue to config search
43+
}
44+
45+
// #3340: Search config files for clientAuthToken/authToken as last resort.
46+
// Covers the case where ag init wrote the token to a config but the
47+
// auth-token file is missing (e.g., partial state, file deleted).
48+
try {
49+
const { loadConfig } = await import('../config.js');
50+
const config = await loadConfig();
51+
return config.clientAuthToken || config.authToken || undefined;
4152
} catch {
4253
return undefined;
4354
}
@@ -262,7 +273,7 @@ export async function handleRun(args: string[], io: CliIO): Promise<number> {
262273
: getConfiguredBaseUrl(config);
263274

264275
// Check if server is running
265-
let authToken = resolveAuthToken() || config.authToken || config.clientAuthToken || undefined;
276+
let authToken = await resolveAuthToken() || config.authToken || config.clientAuthToken || undefined;
266277
let serverRunning = await isServerHealthy(baseUrl, authToken);
267278

268279
if (!serverRunning) {

src/server.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -833,8 +833,10 @@ async function main(): Promise<void> {
833833

834834
const container = new ServiceContainer();
835835
// #1644: Derive hook-secret encryption key from master auth token (non-empty only)
836-
if (config.authToken) {
837-
sessions.setEncryptionKey(config.authToken);
836+
// #3340: Also check clientAuthToken
837+
const encryptionKey = config.authToken || config.clientAuthToken;
838+
if (encryptionKey) {
839+
sessions.setEncryptionKey(encryptionKey);
838840
}
839841

840842
// Issue #3143: Wire ACP event store into session transcript reader
@@ -861,7 +863,9 @@ async function main(): Promise<void> {
861863
registerChannels(config);
862864

863865
// Setup auth (Issue #39: multi-key + backward compat)
864-
auth = new AuthManager(path.join(config.stateDir, 'keys.json'), config.authToken, config.defaultTenantId);
866+
// #3340: Fall back to clientAuthToken when authToken is not set
867+
const masterToken = config.authToken || config.clientAuthToken || undefined;
868+
auth = new AuthManager(path.join(config.stateDir, 'keys.json'), masterToken, config.defaultTenantId);
865869
auth.setHost(config.host); // #1080: needed for auth bypass security check
866870

867871
// #1419: Initialize audit logger and wire into auth

0 commit comments

Comments
 (0)