Skip to content

Commit 9125872

Browse files
fix: malformed SSO cache (#4569)
Problem: With some good debugging from Lei, it was discovered that we were getting errors during auth but that they were being lost. The errors we now see indicate that during a read of the SSO cache, the JSON is malformed. Solution: A guess is that multiple writes to a file can happen at the same time. To mitigate this, when writing cache we will make it more atomic by writing all the content to a temp file, then renaming the temp file to the actual target. Now, if there is a race condition each write will either fully make it in or be overwritten by the following write. We should now avoid the malformed JSON. Other findings: If we do multiple token refreshes in rapid succession, both refreshed access tokens will be valid by the looks of it. I guess there isn't a max of 1 active token. So if we have a race condition for the last access token to be saved to cache it doesn't really matter. Signed-off-by: Nikolas Komonen <[email protected]>
1 parent 17ef78a commit 9125872

File tree

1 file changed

+14
-1
lines changed

1 file changed

+14
-1
lines changed

packages/core/src/shared/utilities/cacheUtils.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import * as vscode from 'vscode'
77
import { dirname } from 'path'
88
import { ToolkitError, isFileNotFoundError } from '../errors'
99
import { SystemUtilities } from '../systemUtilities'
10+
import { promises as fsPromises } from 'fs'
11+
import { isWeb } from '../../common/webUtils'
12+
import crypto from 'crypto'
1013

1114
// TODO(sijaden): further generalize this concept over async references (maybe create a library?)
1215
// It's pretty clear that this interface (and VSC's `Memento`) reduce down to what is essentially
@@ -124,7 +127,17 @@ export function createDiskCache<T, K>(
124127

125128
try {
126129
await SystemUtilities.createDirectory(dirname(target))
127-
await SystemUtilities.writeFile(target, JSON.stringify(data), { mode: 0o600 })
130+
if (isWeb()) {
131+
// There is no web-compatible rename() method. So do a regular write.
132+
await SystemUtilities.writeFile(target, JSON.stringify(data), { mode: 0o600 })
133+
} else {
134+
// With SSO cache we noticed malformed JSON on read. A guess is that multiple writes
135+
// are occuring at the same time. The following is a bandaid that ensures an all-or-nothing
136+
// write, though there can still be race conditions with which version remains after overwrites.
137+
const tempFile = `${target}.tmp-${crypto.randomBytes(4).toString('hex')}`
138+
await SystemUtilities.writeFile(tempFile, JSON.stringify(data), { mode: 0o600 })
139+
await fsPromises.rename(tempFile, target)
140+
}
128141
} catch (error) {
129142
throw ToolkitError.chain(error, `Failed to save "${target}"`, {
130143
code: 'FSWriteFailed',

0 commit comments

Comments
 (0)