mirror of
https://github.com/bitwarden/browser
synced 2025-12-20 02:03:39 +00:00
fix(token-service) [PM-15333]: Portable App Is Not Portable (#17781)
* feat(token-service) [PM-15333]: Update Portable secure storage resolution to use disk. * feat(token-service) [PM-15333]: Move isWindowsPortable evaluation to preload with other platform evaluations.
This commit is contained in:
@@ -203,8 +203,16 @@ const safeProviders: SafeProvider[] = [
|
|||||||
// We manually override the value of SUPPORTS_SECURE_STORAGE here to avoid
|
// We manually override the value of SUPPORTS_SECURE_STORAGE here to avoid
|
||||||
// the TokenService having to inject the PlatformUtilsService which introduces a
|
// the TokenService having to inject the PlatformUtilsService which introduces a
|
||||||
// circular dependency on Desktop only.
|
// circular dependency on Desktop only.
|
||||||
|
//
|
||||||
|
// For Windows portable builds, we disable secure storage to ensure tokens are
|
||||||
|
// stored on disk (in bitwarden-appdata) rather than in Windows Credential
|
||||||
|
// Manager, making them portable across machines. This allows users to move the USB drive
|
||||||
|
// between computers while maintaining authentication.
|
||||||
|
//
|
||||||
|
// Note: Portable mode does not use secure storage for read/write/clear operations,
|
||||||
|
// preventing any collision with tokens from a regular desktop installation.
|
||||||
provide: SUPPORTS_SECURE_STORAGE,
|
provide: SUPPORTS_SECURE_STORAGE,
|
||||||
useValue: ELECTRON_SUPPORTS_SECURE_STORAGE,
|
useValue: ELECTRON_SUPPORTS_SECURE_STORAGE && !ipc.platform.isWindowsPortable,
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: DEFAULT_VAULT_TIMEOUT,
|
provide: DEFAULT_VAULT_TIMEOUT,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
isFlatpak,
|
isFlatpak,
|
||||||
isMacAppStore,
|
isMacAppStore,
|
||||||
isSnapStore,
|
isSnapStore,
|
||||||
|
isWindowsPortable,
|
||||||
isWindowsStore,
|
isWindowsStore,
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
|
|
||||||
@@ -133,6 +134,7 @@ export default {
|
|||||||
isDev: isDev(),
|
isDev: isDev(),
|
||||||
isMacAppStore: isMacAppStore(),
|
isMacAppStore: isMacAppStore(),
|
||||||
isWindowsStore: isWindowsStore(),
|
isWindowsStore: isWindowsStore(),
|
||||||
|
isWindowsPortable: isWindowsPortable(),
|
||||||
isFlatpak: isFlatpak(),
|
isFlatpak: isFlatpak(),
|
||||||
isSnapStore: isSnapStore(),
|
isSnapStore: isSnapStore(),
|
||||||
isAppImage: isAppImage(),
|
isAppImage: isAppImage(),
|
||||||
|
|||||||
@@ -445,13 +445,15 @@ export class TokenService implements TokenServiceAbstraction {
|
|||||||
// we can't determine storage location w/out vaultTimeoutAction and vaultTimeout
|
// we can't determine storage location w/out vaultTimeoutAction and vaultTimeout
|
||||||
// but we can simply clear all locations to avoid the need to require those parameters.
|
// but we can simply clear all locations to avoid the need to require those parameters.
|
||||||
|
|
||||||
|
// When secure storage is supported, clear the encryption key from secure storage.
|
||||||
|
// When not supported (e.g., portable builds), tokens are stored on disk and this step is skipped.
|
||||||
if (this.platformSupportsSecureStorage) {
|
if (this.platformSupportsSecureStorage) {
|
||||||
// Always clear the access token key when clearing the access token
|
// Always clear the access token key when clearing the access token.
|
||||||
// The next set of the access token will create a new access token key
|
// The next set of the access token will create a new access token key.
|
||||||
await this.clearAccessTokenKey(userId);
|
await this.clearAccessTokenKey(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Platform doesn't support secure storage, so use state provider implementation
|
// Clear tokens from disk storage (all platforms)
|
||||||
await this.singleUserStateProvider.get(userId, ACCESS_TOKEN_DISK).update((_) => null, {
|
await this.singleUserStateProvider.get(userId, ACCESS_TOKEN_DISK).update((_) => null, {
|
||||||
shouldUpdate: (previousValue) => previousValue !== null,
|
shouldUpdate: (previousValue) => previousValue !== null,
|
||||||
});
|
});
|
||||||
@@ -478,6 +480,9 @@ export class TokenService implements TokenServiceAbstraction {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When platformSupportsSecureStorage=true, tokens on disk are encrypted and require
|
||||||
|
// decryption keys from secure storage. When false (e.g., portable builds), tokens are
|
||||||
|
// stored on disk.
|
||||||
if (this.platformSupportsSecureStorage) {
|
if (this.platformSupportsSecureStorage) {
|
||||||
let accessTokenKey: AccessTokenKey;
|
let accessTokenKey: AccessTokenKey;
|
||||||
try {
|
try {
|
||||||
@@ -1118,6 +1123,9 @@ export class TokenService implements TokenServiceAbstraction {
|
|||||||
) {
|
) {
|
||||||
return TokenStorageLocation.Memory;
|
return TokenStorageLocation.Memory;
|
||||||
} else {
|
} else {
|
||||||
|
// Secure storage (e.g., OS credential manager) is preferred when available.
|
||||||
|
// Desktop portable builds set platformSupportsSecureStorage=false to store tokens
|
||||||
|
// on disk for portability across machines.
|
||||||
if (useSecureStorage && this.platformSupportsSecureStorage) {
|
if (useSecureStorage && this.platformSupportsSecureStorage) {
|
||||||
return TokenStorageLocation.SecureStorage;
|
return TokenStorageLocation.SecureStorage;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user