How Streambert Implements Secure TMDB API Key Storage Using the OS Keychain

Streambert stores the TMDB Read-Access Token in the operating system's native credential store using Electron's safeStorage API, ensuring the JWT token is encrypted and never persisted in plain text.

Streambert is an open-source streaming application that securely manages TMDB API credentials without exposing sensitive tokens to plain text storage. By leveraging the OS keychain through Electron's secure storage mechanisms, the truelockmc/streambert repository implements a robust pattern for protecting user API keys across macOS, Windows, and Linux platforms.

Understanding the Secure Storage Architecture

Key Registration and Constants

In src/utils/storage.js, the application defines STORAGE_KEYS.API_KEY (lines 34-38) to uniquely identify the TMDB token within the credential store. This constant provides a consistent lookup key for all storage operations, ensuring the application references the same secure entry regardless of platform.

The Secure Storage Wrapper

The secureStorage object defined in src/utils/storage.js (lines 114-126) abstracts the underlying platform-specific implementation. It provides get and set methods that forward calls to window.electron.secureGet and window.electron.secureSet. The wrapper includes a no-op fallback for development environments outside Electron, preventing crashes when the secure storage bridge is unavailable while maintaining API consistency.

Electron IPC Bridge Implementation

The actual encryption and OS keychain interaction occurs in src/ipc/storage.js, which bridges the renderer process to Electron's safeStorage API. This layer handles platform-specific storage mechanisms—macOS Keychain, Windows DPAPI, or Linux libsecret—ensuring the renderer never accesses raw secrets directly and the encryption keys remain within the main process.

How Streambert Retrieves and Stores the TMDB Token

Loading the Token at Application Startup

When Streambert initializes, src/App.jsx (lines 35-36) asynchronously retrieves the stored token using secureStorage.get(STORAGE_KEYS.API_KEY). This ensures the TMDB JWT is available for API calls immediately after launch while maintaining encryption at rest. The asynchronous pattern prevents blocking the UI during keychain access, which may require user authentication on some platforms.

User Input and Persistence Flow

The Settings UI in src/pages/SettingsPage.jsx (lines 1609-1664) provides an interface for users to paste their TMDB JWT token. Upon confirmation, the application calls secureStorage.set(STORAGE_KEYS.API_KEY, token) to encrypt and persist the credential to the OS keychain. This design ensures the token survives application restarts without exposure to plain text storage mechanisms like localStorage or plain JSON files.

Secure Storage Implementation Examples

Retrieve the TMDB token for API authentication:

// Retrieve the TMDB token (used by any API call)
import { secureStorage, STORAGE_KEYS } from '@/utils/storage';

async function loadTmdbToken() {
  const token = await secureStorage.get(STORAGE_KEYS.API_KEY);
  if (!token) throw new Error('TMDB token not set');
  return token;
}

Store the TMDB token after user input:

// Store the TMDB token after the user pastes it in Settings
import { secureStorage, STORAGE_KEYS } from '@/utils/storage';

async function saveTmdbToken(userInput) {
  const trimmed = userInput.trim();
  await secureStorage.set(STORAGE_KEYS.API_KEY, trimmed);
  // Token is now safely persisted in the OS keychain
}

Make authenticated requests using the securely stored token:

// Making an authenticated TMDB request (uses the stored token)
import { getApiKey } from '@/utils/storage';
import { TMDB_BASE } from '@/utils/api';

export async function fetchTmdb(path) {
  const token = await getApiKey(); // reads from secure storage
  const res = await fetch(`${TMDB_BASE}${path}`, {
    headers: { Authorization: `Bearer ${token}` },
  });
  if (!res.ok) throw new Error(`TMDB ${res.status}`);
  return res.json();
}

Summary

  • Constant Registration: STORAGE_KEYS.API_KEY in src/utils/storage.js provides the unique identifier for the TMDB token in the OS credential store.
  • Abstraction Layer: The secureStorage wrapper forwards calls to Electron's safeStorage while providing fallbacks for non-Electron environments.
  • Platform Integration: src/ipc/storage.js handles platform-specific encryption via macOS Keychain, Windows DPAPI, or Linux libsecret without exposing secrets to the renderer.
  • Lifecycle Management: The token loads asynchronously at startup in src/App.jsx and persists through the settings interface in src/pages/SettingsPage.jsx.
  • Security Guarantee: This architecture ensures the TMDB JWT never appears in plain text storage or unencrypted memory dumps.

Frequently Asked Questions

Where does Streambert store the TMDB API key?

Streambert stores the TMDB Read-Access Token in the operating system's native credential store—macOS Keychain on Apple devices, Windows DPAPI on Windows, or Linux libsecret on Linux distributions—via Electron's safeStorage API as implemented in src/ipc/storage.js. This prevents the token from being written to disk in plain text.

Is the TMDB token encrypted when stored?

Yes, the token is encrypted by the OS-provided keystore before being written to disk. Electron's safeStorage encrypts the data using platform-specific mechanisms, ensuring the JWT token is never persisted in plain text or accessible to other applications without proper system authentication.

What happens if the OS keychain is unavailable?

The secureStorage wrapper in src/utils/storage.js (lines 114-126) includes a no-op fallback mechanism that returns empty values rather than throwing errors when running outside Electron or when the secure storage bridge is inaccessible. However, the token will not persist between sessions in such development scenarios.

How does the renderer process access the secure storage?

The renderer process communicates with the main process through the IPC bridge defined in src/ipc/storage.js. The renderer calls window.electron.secureGet and window.electron.secureSet, which are exposed via context isolation, delegating to Electron's safeStorage methods without exposing encryption keys or raw credentials to the frontend code.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →