Skip to content

Commit bcd6c27

Browse files
authored
Merge pull request #8131 from continuedev/tingwai/con-4270-auth-cache-corrupt
fix: handle corrupted VSCode auth cache with automatic cleanup and recovery
2 parents 8dacb32 + a58149c commit bcd6c27

File tree

3 files changed

+56
-26
lines changed

3 files changed

+56
-26
lines changed

extensions/vscode/src/stubs/SecretStorage.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ export class SecretStorage {
6262
const key = await this.getOrCreateEncryptionKey();
6363
const data = fs.readFileSync(filePath);
6464

65+
// Validate minimum data size to detect corruption early
66+
const minSize = this.saltLength + this.ivLength + this.tagLength;
67+
if (data.length < minSize) {
68+
throw new Error(
69+
`Corrupted cache file: insufficient data (${data.length} bytes, expected at least ${minSize})`,
70+
);
71+
}
72+
6573
const salt = data.subarray(0, this.saltLength);
6674
const iv = data.subarray(this.saltLength, this.saltLength + this.ivLength);
6775
const tag = data.subarray(
@@ -105,4 +113,12 @@ export class SecretStorage {
105113
}
106114
return undefined;
107115
}
116+
117+
async delete(key: string): Promise<void> {
118+
const filePath = this.keyToFilepath(key);
119+
if (fs.existsSync(filePath)) {
120+
fs.unlinkSync(filePath);
121+
console.log(`Successfully deleted cache file: ${filePath}`);
122+
}
123+
}
108124
}

extensions/vscode/src/stubs/WorkOsAuthProvider.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -161,22 +161,34 @@ export class WorkOsAuthProvider implements AuthenticationProvider, Disposable {
161161
scopes?: string[],
162162
): Promise<ContinueAuthenticationSession[]> {
163163
// await this.hasAttemptedRefresh;
164-
const data = await this.secretStorage.get(SESSIONS_SECRET_KEY);
165-
if (!data) {
166-
return [];
167-
}
168-
169164
try {
165+
const data = await this.secretStorage.get(SESSIONS_SECRET_KEY);
166+
if (!data) {
167+
return [];
168+
}
169+
170170
const value = JSON.parse(data) as ContinueAuthenticationSession[];
171171
return value;
172172
} catch (e: any) {
173-
// Capture session file parsing errors to Sentry
173+
// Capture session decrypt and parsing errors to Sentry
174174
Logger.error(e, {
175-
context: "workOS_sessions_json_parse",
176-
dataLength: data.length,
175+
context: "workOS_sessions_retrieval",
176+
errorMessage: e.message,
177177
});
178178

179-
console.warn(`Error parsing sessions.json: ${e}`);
179+
console.warn(`Error retrieving or parsing sessions: ${e.message}`);
180+
181+
// Delete the corrupted cache file to allow fresh start on next attempt
182+
// This handles cases where decryption succeeded but JSON parsing failed
183+
try {
184+
await this.secretStorage.delete(SESSIONS_SECRET_KEY);
185+
} catch (deleteError: any) {
186+
console.error(
187+
`Failed to delete corrupted sessions cache:`,
188+
deleteError.message,
189+
);
190+
}
191+
180192
return [];
181193
}
182194
}

gui/src/context/Auth.tsx

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,27 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
5050
const currentOrg = useAppSelector(selectCurrentOrg);
5151
const selectedProfile = useAppSelector(selectSelectedProfile);
5252

53-
const login: AuthContextType["login"] = (useOnboarding: boolean) => {
54-
return new Promise(async (resolve) => {
55-
await ideMessenger
56-
.request("getControlPlaneSessionInfo", {
57-
silent: false,
58-
useOnboarding,
59-
})
60-
.then((result) => {
61-
if (result.status === "error") {
62-
resolve(false);
63-
return;
64-
}
53+
const login: AuthContextType["login"] = async (useOnboarding: boolean) => {
54+
try {
55+
const result = await ideMessenger.request("getControlPlaneSessionInfo", {
56+
silent: false,
57+
useOnboarding,
58+
});
59+
60+
if (result.status === "error") {
61+
console.error("Login failed:", result.error);
62+
return false;
63+
}
6564

66-
const session = result.content;
67-
setSession(session);
65+
const session = result.content;
66+
setSession(session);
6867

69-
resolve(true);
70-
});
71-
});
68+
return true;
69+
} catch (error: any) {
70+
console.error("Login request failed:", error);
71+
// Let the error propagate so the caller can handle it
72+
throw error;
73+
}
7274
};
7375

7476
const logout = () => {

0 commit comments

Comments
 (0)