Skip to content

Fix secret storage persistence in VS Code Server web#317333

Open
mutl3y wants to merge 1 commit into
microsoft:mainfrom
mutl3y:fix/secret-storage-persistence-vscode-server
Open

Fix secret storage persistence in VS Code Server web#317333
mutl3y wants to merge 1 commit into
microsoft:mainfrom
mutl3y:fix/secret-storage-persistence-vscode-server

Conversation

@mutl3y
Copy link
Copy Markdown

@mutl3y mutl3y commented May 19, 2026

Fixes #317329

Problem

When using VS Code Server (server-linux-x64-web) accessed via a browser, the secretStorageProvider is set to undefined when remoteAuthority is set and the vscode-secret-key-path cookie is absent. This causes all secrets (GitHub auth tokens, Copilot credentials, etc.) to be stored in-memory only — lost on every page refresh or folder switch.

The CLI (code serve-web) already implements the full mint-key protocol in cli/src/commands/serve_web.rs. The Node.js server binary never did.

Root cause

workbench.ts (lines 609-621) already has the correct logic:

const secretStorageCrypto = secretStorageKeyPath && ServerKeyedAESCrypto.supported()
    ? new ServerKeyedAESCrypto(secretStorageKeyPath) : new TransparentCrypto();

    ? undefined
    : new LocalStorageSecretStorageProvider(secretStorageCrypto),

It uses ServerKeyedAESCrypto (AES-256, keys survive page reload) when vscode-secret-key-path is set, falling back to TransparentCrypto otherwise. But the server never set that cookie, so the browser always fell back to in-memory storage.

Fix

Implement the same mint-key protocol in webClientServer.ts that the CLI already implements in Rust:

  1. Persistent server key — on first start, a random 32-byte server secret is generated and written to ${userDataPath}/serve-web-key-half (mode 0600). It is reloaded on every subsequent start so server restarts / VS Code updates do not invalidate stored secrets.

  2. Client key cookie — every root-page response sets vscode-cli-secret-half (HttpOnly, SameSite=Strict, Max-Age=30 days) carrying the client's 32-byte key half.

  3. Path cookie — every root-page response also sets vscode-secret-key-path pointing to /_vscode-server/mint-key, telling the workbench where to fetch the server's contribution.

  4. Mint endpointPOST /_vscode-server/mint-key returns SHA-256(serverKey || clientKeyHalf)[0:32] as application/octet-stream. The browser XORs this with its local half to produce the AES-256 key for ServerKeyedAESCrypto.

This is a direct port of get_server_key_half, get_client_key_half, handle_secret_mint, and append_secret_headers from cli/src/commands/serve_web.rs. The derivation, cookie names, and file name (serve-web-key-half) are identical.

Note: The mint path is /_vscode-server/mint-key (vs the CLI's _vscode-cli/mint-key) to avoid collision if both servers ever run on the same origin. workbench.ts reads the path from the cookie so this is transparent to the browser.

Changes

  • src/vs/server/node/webClientServer.ts — implement mint protocol (the only file changed)
  • src/vs/code/browser/workbench/workbench.ts — reverted to upstream; no changes needed

Testing

Verified with a Playwright end-to-end test against a running server-linux-x64-web instance (via an external mint-proxy that implements the same protocol):

  • Both secret cookies are set on page load
  • POST /_vscode-server/mint-key returns 32 bytes
  • Workbench loads with title containing "Visual Studio Code"
  • WebSocket connects successfully via wss://
  • No console errors

Copilot AI review requested due to automatic review settings May 19, 2026 16:02
@vs-code-engineering
Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

@deepak1556

Matched files:

  • src/vs/code/browser/workbench/workbench.ts

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes loss of GitHub/Copilot authentication state in VS Code Server web by ensuring the web workbench always provides a persisted secretStorageProvider (backed by browser localStorage) instead of falling back to the in-memory secret storage implementation when remoteAuthority is set and the vscode-secret-key-path cookie is missing.

Changes:

  • Always instantiate LocalStorageSecretStorageProvider for secret storage in the web workbench (removes the remote+no-cookie special case).
  • As a result, secrets persist across page refreshes and workspace/folder switches in server-web scenarios.

Comment on lines 616 to 620
settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined,
workspaceProvider: WorkspaceProvider.create(config),
urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute),
secretStorageProvider: config.remoteAuthority && !secretStorageKeyPath
? undefined /* with a remote without embedder-preferred storage, store on the remote */
: new LocalStorageSecretStorageProvider(secretStorageCrypto),
secretStorageProvider: new LocalStorageSecretStorageProvider(secretStorageCrypto),
});
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

mutl3y pushed a commit to mutl3y/vs-code_sandbox that referenced this pull request May 19, 2026
…patched in dockerfile but PR: microsoft/vscode#317333 has been raised separately
@mutl3y mutl3y marked this pull request as draft May 19, 2026 21:35
The web server never set the 'vscode-secret-key-path' or 'vscode-cli-secret-half'
cookies that workbench.ts requires to use ServerKeyedAESCrypto. Without them the
browser fell back to TransparentCrypto (no encryption) and in-memory storage,
so GitHub auth tokens and all other secrets were lost on every page refresh.

This mirrors the protocol already implemented in the CLI (cli/src/commands/serve_web.rs):

  * A random 32-byte server-secret key is generated on first start and persisted
    to ${userDataPath}/serve-web-key-half (mode 0600), so the key survives server
    restarts and VS Code updates.
  * A per-client 32-byte key half is carried in an HttpOnly cookie
    'vscode-cli-secret-half' (Max-Age 30 days).
  * POST /_vscode-server/mint-key returns SHA-256(serverKey || clientKeyHalf)[0:32]
    as application/octet-stream.  The browser XORs this with its local half to
    produce the AES-256 key used by ServerKeyedAESCrypto.
  * Both cookies are set on every root-page response (302 redirect and 200).

workbench.ts already contains the correct conditional to use ServerKeyedAESCrypto
when 'vscode-secret-key-path' is present, so no browser-side changes are needed.
Revert the earlier unconditional LocalStorageSecretStorageProvider override to
restore the original upstream logic.

Fixes: microsoft#317329
@mutl3y mutl3y force-pushed the fix/secret-storage-persistence-vscode-server branch from 04fe551 to 1779f51 Compare May 19, 2026 23:32
@mutl3y mutl3y marked this pull request as ready for review May 19, 2026 23:33
@mutl3y
Copy link
Copy Markdown
Author

mutl3y commented May 19, 2026

@microsoft-github-policy-service agree

@aeschli
Copy link
Copy Markdown
Contributor

aeschli commented May 21, 2026

@connor4312 Maybe you are more familiar with this...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VS Code Server web: GitHub auth tokens lost on page refresh/folder switch (in-memory only)

4 participants