Custom contexts are still in beta and may be incompatible with some versions of Chrome.

Browser cookies are stored in the user data directory. By default, Browserbase creates a new user data directory for each Session.

For some use cases (such as authentication), it can be helpful to preserve the user data directory and reuse it across sessions. With Contexts, it’s easy.

Sharing Contexts across Sessions

One benefit of using Contexts is that all the data generated during one session can be used in the next. This is especially useful to improve load times, as the network cache is also preserved.

Contexts are configured by creating a Context via the Contexts API.

Then, you’ll have to pass the Context’s ID to every Create Sessions API call.

Make sure to set context.persist to true when creating a Session if you want the Context to be updated with the Session’s state.

Here’s the steps:

To share Contexts across Sessions, follow these steps:

  1. Use the Contexts API to create a new context. You’ll need the context ID for the next step.
  2. Use the Sessions API to create a new session. You’ll need to add "browserSettings": { "context": { "id": YOUR_CONTEXT_ID, "persist": true } } to the body.

context.persist isn’t required for each Session. One workflow might involve having it enabled for the first Session (to fill the cache), and then disabling it on subsequent ones.

Context data can include stored credentials and other sensitive browsing data. Because of this, Contexts are uniquely encrypted at rest.

Uploading your own custom user data directory

In some scenarios, it may make sense to upload a custom user data directory. That’s why the Create Contexts API returns an authenticated uploadUrl that enables this. However, the file that’s uploaded must to follow a specific pattern in order to work with the Sessions.

  1. The uploaded context must have a filename of ${context.id}.zip
  2. The ZIP must be encrypted using details from the Context API response

Below is some sample code showing how to compress and encrypt a user data directory.

import fs from "node:fs";
import AdmZip from "adm-zip";
import axios from "axios";

// 1. Compress the user data directory
zip("path/to/user/data", "output.zip");

// 2. Encrypt the ZIP file
// `publicKey`, `cipherAlgorithm`, and `initializationVectorSize` are from the Context API response
encryptFile("output.zip", "encrypted.zip", {
  publicKey,
  cipherAlgorithm,
  initializationVectorSize,
});

// 3. Upload the encrypted ZIP file
// `uploadUrl` is from the Context API response
uploadFile("encrypted.zip", uploadUrl);

async function zip(inputFolder: string, outputZipPath: string) {
  const z = new AdmZip();
  z.addLocalFolder(inputFolder);
  await z.writeZipPromise(outputZipPath);
}

interface SigningMaterial {
  publicKey: string;
  cipherAlgorithm: string;
  initializationVectorSize: number;
}

function encryptFile(
  inputPath: fs.PathLike,
  outputPath: fs.PathLike,
  { publicKey, cipherAlgorithm, initializationVectorSize }: SigningMaterial,
) {
  const keySize = 32; // a reasonable default for AES-256
  const aesKey = crypto.randomBytes(keySize);
  const iv = crypto.randomBytes(initializationVectorSize);

  // Encrypt AES key with RSA public key
  const encryptedAesKey = crypto.publicEncrypt(publicKey, aesKey);

  const cipher = crypto.createCipheriv(
    cipherAlgorithm.toLowerCase(),
    aesKey,
    iv,
  );
  const input = fs.createReadStream(inputPath);
  const output = fs.createWriteStream(outputPath);

  return new Promise<void>((resolve, reject) => {
    output.write(Buffer.concat([encryptedAesKey, iv]));

    input.pipe(cipher).pipe(output);

    output.on("finish", resolve);
  });
}

function uploadFile(filePath: fs.PathLike, uploadUrl: string) {
  const fileStream = fs.createReadStream(filePath);

  await axios.put(uploadUrl, fileStream, {
    headers: { "Content-Type": "application/zip" },
  });
}

Handling Authentication

Once you set up Contexts, follow our authentication guide to easily log into websites.