How Feynman Handles OAuth Credential Storage for the Workbench: Secure Token Architecture Explained
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 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 (lines 75-78) constructs the absolute path to 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). 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) generates a one-time state parameter and stores a "pending" entry in oauth-pending.json. This temporary record maintains the correlation between the authorization request and the eventual callback, preventing cross-site request forgery (CSRF) attacks.
// 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) 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 using the secure I/O wrappers previously established.
// 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) 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) retrieves the stored credential and returns a ready-to-use Authorization header:
// 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 (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 (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
0o700permissions - Token files are written with
0o600permissions - 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, 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.jsonwithin a hidden.feynmandirectory, isolated from project source trees. - The
secureSensitiveStorePath()function enforces0o700directory and0o600file permissions while blocking symbolic link attacks. createWorkbenchOAuthStart()andfinishWorkbenchOAuthCallback()insrc/workbench/oauth-store.tsmanage 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.tstransforms 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 located inside the Feynman app data root, specifically within a hidden .feynman directory. The tokenStorePath() function in 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, 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, 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) 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), which checks the expiresAtMs timestamp. The UI in 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →