Skip to main content

Overview

The FolderService manages folder organization for vault items. Folders provide a way to categorize and organize ciphers within a user’s personal vault. Source: libs/common/src/vault/services/folder/folder.service.ts

Key Features

  • Create, read, update, and delete folders
  • Encrypt and decrypt folder data
  • Observable-based folder state management
  • Automatic “None” folder for unorganized items
  • Key rotation support

Observables

folders$

Observable that emits encrypted folders for a user.
folders$(userId: UserId): Observable<Folder[]>
Parameters:
  • userId: UserId - The user ID to get folders for
Returns: Observable of encrypted folder domain objects Example:
const folders$ = folderService.folders$(userId);
folders$.subscribe(folders => {
  console.log(`User has ${folders.length} folders`);
});

folderViews$

Observable that emits decrypted folder views for a user.
folderViews$(userId: UserId): Observable<FolderView[]>
Parameters:
  • userId: UserId - The user ID to get folder views for
Returns: Observable of decrypted folder views, includes automatic “None” folder Example:
const folderViews$ = folderService.folderViews$(userId);
folderViews$.subscribe(folders => {
  folders.forEach(folder => {
    console.log(`Folder: ${folder.name}`);
  });
});

getDecrypted$

Observable for a single decrypted folder by ID.
getDecrypted$(id: string, userId: UserId): Observable<FolderView | undefined>
Parameters:
  • id: string - The folder ID
  • userId: UserId - The user ID
Returns: Observable that emits the folder view or undefined if not found

Core Methods

encrypt

Encrypts a folder view for storage.
encrypt(model: FolderView, key: SymmetricCryptoKey): Promise<Folder>
Parameters:
  • model: FolderView - The folder view to encrypt
  • key: SymmetricCryptoKey - The encryption key (typically user key)
Returns: Promise resolving to encrypted folder Example:
const folderView = new FolderView();
folderView.name = "Work Accounts";

const userKey = await keyService.getUserKey(userId);
const encrypted = await folderService.encrypt(folderView, userKey);

get

Retrieves a single encrypted folder by ID.
get(id: string, userId: UserId): Promise<Folder>
Parameters:
  • id: string - The folder ID
  • userId: UserId - The user ID
Returns: Promise resolving to the folder, or undefined if not found

getAllFromState

Retrieves all folders from state.
getAllFromState(userId: UserId): Promise<Folder[]>
Parameters:
  • userId: UserId - The user ID
Returns: Promise resolving to array of all folders

getAllDecryptedFromState

Retrieves all decrypted folders from state.
getAllDecryptedFromState(userId: UserId): Promise<FolderView[]>
Parameters:
  • userId: UserId - The user ID
Returns: Promise resolving to array of decrypted folder views
This method is deprecated and only recommended for CLI usage. Use folderViews$ observable instead for reactive updates.

CRUD Operations

upsert

Inserts or updates folder data in storage.
upsert(folderData: FolderData | FolderData[], userId: UserId): Promise<void>
Parameters:
  • folderData: FolderData | FolderData[] - Single folder or array of folders to upsert
  • userId: UserId - The user ID
Example:
const folderData = new FolderData();
folderData.id = "folder-id-123";
folderData.name = encryptedName;
folderData.revisionDate = new Date().toISOString();

await folderService.upsert(folderData, userId);

replace

Replaces all folders for a user.
replace(folders: { [id: string]: FolderData }, userId: UserId): Promise<void>
Parameters:
  • folders: { [id: string]: FolderData } - Object mapping folder IDs to folder data
  • userId: UserId - The user ID
Note: This is typically used during sync operations to replace all local folder data.

delete

Deletes one or more folders.
delete(id: string | string[], userId: UserId): Promise<any>
Parameters:
  • id: string | string[] - Folder ID or array of folder IDs to delete
  • userId: UserId - The user ID
Behavior:
  • Deletes the folder from storage
  • Automatically reassigns ciphers in the deleted folder to “No Folder”
  • Updates cipher data to set folderId to null
Example:
// Delete a single folder
await folderService.delete("folder-id-123", userId);

// Delete multiple folders
await folderService.delete(["folder-1", "folder-2"], userId);

clear

Clears all folder data for a user.
clear(userId: UserId): Promise<void>
Parameters:
  • userId: UserId - The user ID
Note: This is typically called during logout or account switching.

Key Rotation

getRotatedData

Generates re-encrypted folder data for key rotation.
getRotatedData(
  originalUserKey: UserKey,
  newUserKey: UserKey,
  userId: UserId,
): Promise<FolderWithIdRequest[]>
Parameters:
  • originalUserKey: UserKey - The original user key (not currently used in implementation)
  • newUserKey: UserKey - The new user key to encrypt with
  • userId: UserId - The user ID
Returns: Promise resolving to array of re-encrypted folders for server update Example:
const rotatedFolders = await folderService.getRotatedData(
  oldUserKey,
  newUserKey,
  userId
);

// Send rotated data to server
await apiService.postUserKeyUpdate({
  folders: rotatedFolders,
  // ... other rotated data
});

Cache Management

clearDecryptedFolderState

Clears the decrypted folder cache.
clearDecryptedFolderState(userId: UserId): Promise<void>
Parameters:
  • userId: UserId - The user ID
Note: This forces folders to be decrypted again on next access. Called automatically during upsert operations.

Folder Structure

FolderView

Decrypted folder view object.
class FolderView {
  id: string;
  name: string;
  revisionDate: Date;
}
Properties:
  • id: string - Unique folder identifier
  • name: string - Decrypted folder name
  • revisionDate: Date - Last modification date

Folder

Encrypted folder domain object.
class Folder {
  id: string;
  name: EncString;
  revisionDate: Date;
}
Properties:
  • id: string - Unique folder identifier
  • name: EncString - Encrypted folder name
  • revisionDate: Date - Last modification date

FolderData

Raw folder data for storage.
class FolderData {
  id: string;
  name: string; // Encrypted string representation
  revisionDate: string;
}

Special Folders

None Folder

The service automatically adds a “None” folder to the folder list:
const noneFolder = new FolderView();
noneFolder.name = this.i18nService.t("noneFolder");
// Automatically appended to folder list
Characteristics:
  • No ID (used for ciphers with folderId === null)
  • Localized name from i18n service
  • Always appears in folder lists
  • Cannot be deleted or modified
  • Represents unorganized items

Usage Examples

Creating a New Folder

import { FolderView } from '@bitwarden/common/vault/models/view/folder.view';
import { FolderData } from '@bitwarden/common/vault/models/data/folder.data';

// Create folder view
const folderView = new FolderView();
folderView.name = "Personal Accounts";

// Encrypt the folder
const userKey = await keyService.getUserKey(userId);
const encrypted = await folderService.encrypt(folderView, userKey);

// Save to storage
const folderData = encrypted.toFolderData();
await folderService.upsert(folderData, userId);

Listing All Folders

// Using observable (recommended)
folderService.folderViews$(userId).subscribe(folders => {
  folders.forEach(folder => {
    console.log(`${folder.id}: ${folder.name}`);
  });
});

// Using promise (CLI/scripts)
const folders = await folderService.getAllDecryptedFromState(userId);
folders.forEach(folder => {
  console.log(`${folder.id}: ${folder.name}`);
});

Moving Cipher to Folder

// Get the cipher
const cipher = await cipherService.get(cipherId, userId);

// Update folder assignment
cipher.folderId = targetFolderId;

// Save changes
await cipherService.upsert(cipher.toCipherData(), userId);

Deleting a Folder

// Delete folder (ciphers automatically moved to "None")
await folderService.delete(folderId, userId);

// Verify ciphers were reassigned
const ciphers = await cipherService.getAllDecryptedForGrouping(
  null, // null = "None" folder
  userId,
  true // folder = true
);
console.log(`${ciphers.length} items moved to None folder`);

Implementation Notes

Observable Caching

The service maintains a cache of folder view observables to prevent duplicate decryption:
private folderViewCache = new Map<UserId, Observable<FolderView[]>>();

Force Emission

The service uses subjects to force emission of empty arrays during cleanup:
private forceFolderViews: Record<UserId, Subject<FolderView[]>> = {};
This ensures that subscribers receive updates when folder data is cleared.
  • Cipher Service - Manages vault items that can be organized in folders
  • Key Service - Provides encryption keys for folder encryption

See Also

Build docs developers (and LLMs) love