# How Feynman Handles OAuth Credential Storage for the Workbench: Secure Token Architecture Explained

> Learn how Feynman securely stores OAuth credentials in a hidden JSON file using strict permissions and I/O wrappers. Protect your tokens with this robust architecture.

- Repository: [Advait Paliwal/feynman](https://github.com/advaitpaliwal/feynman)
- Tags: architecture
- Published: 2026-09-08

---

**Feynman persists OAuth tokens in a dedicated JSON file inside a hidden `.feynman` directory with strict filesystem permissions (0o700 for directories and 0o600 for files), using secure I/O wrappers to prevent symlink attacks and ensure owner-only access.**

Feynman, an open-source AI workbench for building data pipelines, implements a defense-in-depth approach to **OAuth credential storage** that isolates sensitive tokens from project source trees. According to the Feynman source code, the system persists access tokens in [`oauth-tokens.json`](https://github.com/advaitpaliwal/feynman/blob/main/oauth-tokens.json) under the application data root while enforcing granular filesystem controls and validation checks on every read and write operation. This architecture ensures that OAuth credentials remain accessible to the workbench process while remaining protected from accidental exposure or repository leakage.

## Architecture of the OAuth Token Store

At the core of Feynman's **OAuth credential storage** lies a three-component architecture that separates persistence, security enforcement, and data transformation.

### Token Store Path Resolution

The `tokenStorePath()` function in [`src/workbench/oauth-store.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-store.ts) (lines 75-78) constructs the absolute path to [`oauth-tokens.json`](https://github.com/advaitpaliwal/feynman/blob/main/oauth-tokens.json) within the Feynman app data root. Rather than storing credentials alongside project files, Feynman isolates sensitive data in a dedicated location returned by `migratedWorkbenchDataPath()`, ensuring that OAuth tokens never accidentally commit to version control.

### Secure Filesystem Operations

Every write operation passes through `secureSensitiveStorePath()` and `chmodSensitivePath()` (lines 83-90 and 12-18 in [`src/workbench/oauth-store.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-store.ts)). These functions enforce:

- **Directory permissions**: `0o700` (read, write, and execute for owner only)
- **File permissions**: `0o600` (read and write for owner only)
- **Symlink protection**: Validation that the target path is a regular file, not a symbolic link
- **Atomic permission setting**: Filesystem chmod calls occur immediately after file creation to eliminate race conditions

## The OAuth Credential Lifecycle

Feynman manages **OAuth credential storage** through a six-stage lifecycle that handles everything from initial authorization to API request signing.

### 1. Initiating the OAuth Flow

When a user begins authentication, `createWorkbenchOAuthStart()` (lines 61-99 in [`src/workbench/oauth-store.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-store.ts)) generates a one-time state parameter and stores a "pending" entry in [`oauth-pending.json`](https://github.com/advaitpaliwal/feynman/blob/main/oauth-pending.json). This temporary record maintains the correlation between the authorization request and the eventual callback, preventing cross-site request forgery (CSRF) attacks.

```typescript
// Start an OAuth flow for a custom connector
const { authorizationUrl, expiresAtMs, state } = createWorkbenchOAuthStart(
  "/my/workspace",
  { 
    id: "my-connector", 
    clientId: "abc", 
    oauthServerUrl: "https://auth.example.com" 
  },
  "http://localhost:3000/callback"
);

```

### 2. Handling the Callback and Token Persistence

Upon provider redirection, `finishWorkbenchOAuthCallback()` (lines 4-68 in [`src/workbench/oauth-store.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-store.ts)) validates the state parameter against the pending entry, exchanges the authorization code for tokens, and calls `upsertWorkbenchOAuthToken()` to persist the credentials. This function atomically writes to [`oauth-tokens.json`](https://github.com/advaitpaliwal/feynman/blob/main/oauth-tokens.json) using the secure I/O wrappers previously established.

```typescript
// Complete the flow after the provider redirects back
const token = await finishWorkbenchOAuthCallback(
  "/my/workspace",
  { 
    code: "authcode123", 
    state // extracted from query string
  }
);

```

### 3. Reading and Validating Stored Tokens

The `readWorkbenchOAuthTokens()` function (lines 48-56 in [`src/workbench/oauth-store.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-store.ts)) loads the JSON file, creating it if missing, and normalizes each entry into a `WorkbenchOAuthToken` object. Simultaneously, `isOAuthTokenExpired()` (lines 6-8) checks the `expiresAtMs` field to determine token validity, enabling the UI to display "connected," "expired," or "missing" statuses.

### 4. Generating Authorization Headers

When connectors need to make authenticated requests, `authorizationHeaderForConnector()` (lines 10-15 in [`src/workbench/oauth-store.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-store.ts)) retrieves the stored credential and returns a ready-to-use `Authorization` header:

```typescript
// Retrieve an auth header for downstream API calls
const headers = authorizationHeaderForConnector("/my/workspace", "my-connector");
// Returns: { Authorization: "Bearer <access_token>" }

```

### 5. UI Integration via the Token Ledger

The `buildWorkbenchOAuthTokenRecords()` function in [`src/workbench/oauth-token-ledger.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-token-ledger.ts) (lines 29-57) transforms raw token objects into `WorkbenchOAuthTokenRecord` instances. These records add metadata such as connection status and expiration timestamps, feeding into resource tables displayed in [`src/workbench/settings-resources.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/settings-resources.ts) (lines 94-105). The UI intentionally omits raw token values, displaying only presence indicators to prevent shoulder-surfing attacks.

## Security Mechanisms in OAuth Credential Storage

Feynman's approach to **OAuth credential storage** prioritizes defense-in-depth through multiple overlapping security controls.

### Filesystem Isolation

The token file resides exclusively within the application data root returned by `migratedWorkbenchDataPath()`, completely separated from the project workspace. This architectural decision prevents accidental credential leakage through file uploads or repository commits.

### Permission Enforcement

All sensitive storage operations utilize `secureSensitiveStorePath()` to guarantee:

- Parent directories are created with `0o700` permissions
- Token files are written with `0o600` permissions
- Path validation confirms regular file status (not symlinks)
- Permission changes occur atomically with file creation

### Memory and UI Safety

The workbench never logs raw token values to console output or displays them in the interface. As implemented in [`settings-resources.ts`](https://github.com/advaitpaliwal/feynman/blob/main/settings-resources.ts), the UI shows only connection states ("OAuth: connected") with the explicit note that "token value is stored locally and not displayed."

## Summary

- Feynman stores OAuth credentials in [`oauth-tokens.json`](https://github.com/advaitpaliwal/feynman/blob/main/oauth-tokens.json) within a hidden `.feynman` directory, isolated from project source trees.
- The `secureSensitiveStorePath()` function enforces `0o700` directory and `0o600` file permissions while blocking symbolic link attacks.
- `createWorkbenchOAuthStart()` and `finishWorkbenchOAuthCallback()` in [`src/workbench/oauth-store.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-store.ts) manage the complete OAuth flow lifecycle.
- `authorizationHeaderForConnector()` provides ready-to-use authentication headers for API requests without exposing tokens to UI logs.
- The token ledger in [`src/workbench/oauth-token-ledger.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-token-ledger.ts) transforms stored credentials into UI-ready records while maintaining security boundaries.

## Frequently Asked Questions

### Where does Feynman store OAuth tokens on disk?

Feynman persists OAuth tokens in a file named [`oauth-tokens.json`](https://github.com/advaitpaliwal/feynman/blob/main/oauth-tokens.json) located inside the Feynman app data root, specifically within a hidden `.feynman` directory. The `tokenStorePath()` function in [`src/workbench/oauth-store.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-store.ts) (lines 75-78) constructs this path using `migratedWorkbenchDataPath()` to ensure the location is separate from your project workspace.

### How does Feynman secure OAuth credentials against unauthorized access?

According to the source code in [`src/workbench/oauth-store.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-store.ts), Feynman implements multiple security layers: `secureSensitiveStorePath()` creates directories with `0o700` permissions and files with `0o600` permissions, validates that paths are regular files (not symlinks), and performs atomic permission changes immediately after file creation to prevent race conditions.

### Can I manually read the OAuth tokens stored by Feynman?

While the tokens are stored as JSON in [`oauth-tokens.json`](https://github.com/advaitpaliwal/feynman/blob/main/oauth-tokens.json), the file permissions restrict read access to the operating system user that created the file. The `readWorkbenchOAuthTokens()` function (lines 48-56 in [`src/workbench/oauth-store.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-store.ts)) is the intended interface for accessing these credentials, as it handles file creation, validation, and normalization of token entries.

### What happens when an OAuth token expires in Feynman?

Feynman tracks token expiration through the `isOAuthTokenExpired()` function (lines 6-8 in [`src/workbench/oauth-store.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/oauth-store.ts)), which checks the `expiresAtMs` timestamp. The UI in [`src/workbench/settings-resources.ts`](https://github.com/advaitpaliwal/feynman/blob/main/src/workbench/settings-resources.ts) displays appropriate status indicators ("expired," "connected," or "missing") and provides reconnect actions, though the workbench does not automatically refresh tokens without user initiation of a new OAuth flow.