How to Set Up OAuth Authentication with Keyring Storage in Kimi Code

Kimi Code implements a modular OAuth subsystem that securely stores access tokens in your operating system's native keyring—such as macOS Keychain, Windows Credential Manager, or Linux Secret Service—or falls back to hardened file storage with strict 0600 permissions.

The MoonshotAI/kimi-code repository provides a TypeScript-based OAuth implementation designed for secure credential management across platforms. Whether you are building CLI tools or IDE extensions, understanding how to configure OAuth authentication with keyring storage in Kimi Code ensures your users' access tokens remain encrypted at rest and protected from unauthorized access.

Core Components of the OAuth System

The OAuth implementation in Kimi Code centers on five primary components that coordinate authentication flows and secure storage. These modules handle everything from browser-based login initiation to atomic token persistence.

The OAuth Facade

The OAuth class in packages/oauth/src/oauth.ts serves as the high-level entry point. It initiates browser-based login flows, exchanges authorization codes for tokens, and exposes a simplified API with three main methods: login(scopes), refresh(name), and revoke(name).

Token Storage Implementations

Kimi Code provides two interchangeable storage backends implementing the TokenStorage interface:

  • FileTokenStorage (packages/oauth/src/storage.ts): Persists tokens as JSON files under ~/.kimi-code/credentials/ with atomic writes and 0600 file permissions.
  • KeyringTokenStorage (packages/oauth/src/keyring.ts): Stores the same JSON payload in the OS-native keyring via the KeyringAdapter, avoiding plain-file exposure entirely.

Lifecycle and State Management

The OAuthManager class (packages/oauth/src/oauth-manager.ts) coordinates token lifecycles, manages in-memory caching, and handles concurrent refresh operations across processes. It leverages TokenState (packages/oauth/src/token-state.ts) to track expiry windows and determine when background refreshes are necessary using the isExpired() and needsRefresh() helpers.

Setting Up Basic OAuth with File Storage

For development environments or systems without a native keyring, configure the OAuth instance with FileTokenStorage. This implementation guarantees atomic writes and strict directory permissions.

import { OAuth, FileTokenStorage } from '@moonshot-ai/kimi-code/oauth';

const credDir = `${process.env.HOME}/.kimi-code/credentials`;
const storage = new FileTokenStorage(credDir);

const oauth = new OAuth({
  clientId: 'YOUR_CLIENT_ID',
  clientSecret: 'YOUR_CLIENT_SECRET',
  redirectUri: 'http://localhost:3000/callback',
  storage,
});

await oauth.login(['read', 'write']);

The FileTokenStorage constructor accepts a directory path where it creates individual JSON files for each token set, ensuring data is never written partially to disk.

Configuring OS Keyring Storage

To store credentials in the operating system's secure keyring instead of plain files, instantiate KeyringTokenStorage. This adapter automatically detects and uses macOS Keychain, Windows Credential Manager, or Linux Secret Service.

import { OAuth, KeyringTokenStorage } from '@moonshot-ai/kimi-code/oauth';

const storage = new KeyringTokenStorage();
const oauth = new OAuth({
  clientId: 'YOUR_CLIENT_ID',
  clientSecret: 'YOUR_CLIENT_SECRET',
  redirectUri: 'http://localhost:3000/callback',
  storage,
});

await oauth.login(['read']);

When the platform supports a native keyring, this configuration prevents tokens from ever touching the filesystem unencrypted, satisfying strict security compliance requirements.

Token Lifecycle Management

Automatic Retrieval and Refresh

After authentication, retrieve tokens using oauth.getToken(name). The OAuthManager automatically checks TokenState.needsRefresh() before returning the token, triggering background refreshes when the access token approaches expiry.

async function callKimiAPI() {
  const token = await oauth.getToken('default');
  if (!token) throw new Error('User not authenticated');

  const response = await fetch('https://api.kimi.ai/v1/resource', {
    headers: { Authorization: `Bearer ${token.accessToken}` },
  });
  return response.json();
}

Concurrent Safety Mechanisms

Because multiple processes may attempt simultaneous token refreshes, OAuthManager implements a file-based locking strategy. It creates a <name>.lock file in the credentials directory. If a process cannot obtain the lock, it waits for the active refresh to complete, preventing race conditions and token invalidation storms.

Manual Refresh and Revocation

Force an immediate token refresh—useful when you know the refresh window has passed:

await oauth.refresh('default');

To invalidate tokens both locally and on the authorization server:

await oauth.revoke('default');

The revoke() method contacts the Kimi Code token endpoint to invalidate the credentials, then calls storage.remove(name) to delete the local keyring entry or JSON file.

Data Validation and Type Safety

All token interactions use Zod-validated contracts defined in packages/oauth/src/types.ts. The TokenInfoWire schema ensures that corrupted data never propagates into the runtime, with conversion helpers tokenFromWire() and tokenToWire() handling serialization between the storage layer and the internal TokenState model.

Summary

  • Kimi Code provides a dual-storage OAuth system supporting both OS keyrings and hardened file storage via the TokenStorage interface.
  • Use KeyringTokenStorage (packages/oauth/src/keyring.ts) for production deployments requiring OS-level encryption, or FileTokenStorage (packages/oauth/src/storage.ts) with 0600 permissions for universal compatibility.
  • The OAuthManager handles concurrent refreshes safely using lock files and validates token state using TokenState.needsRefresh().
  • All data passing through the system is validated against TokenInfoWire schemas to prevent corruption.

Frequently Asked Questions

What happens if the OS keyring is unavailable?

If KeyringTokenStorage cannot access the native keyring service, the constructor will throw an initialization error. For environments without keyring support, fall back to FileTokenStorage, which stores tokens under ~/.kimi-code/credentials/ with atomic writes and strict 0600 permissions, ensuring credentials remain readable only by the current user.

How does Kimi Code prevent race conditions during token refresh?

The OAuthManager class implements a cross-process locking mechanism using lock files in the credentials directory. When one process initiates a refresh, it acquires <name>.lock; subsequent processes detecting this lock wait for the operation to complete rather than triggering duplicate refresh requests, preventing token invalidation.

Can I migrate existing tokens from file storage to keyring storage?

Migration is supported by instantiating both storage classes and transferring the token data. Read the existing token from FileTokenStorage, then persist it using KeyringTokenStorage.save(name, token). Once verified, remove the file-based credential with FileTokenStorage.remove(name) to complete the migration.

What permissions does the file-based storage use?

FileTokenStorage in packages/oauth/src/storage.ts creates directories with 0700 permissions and token files with 0600 permissions, ensuring that only the owning user can read or write credential files. All write operations are atomic, using temporary files and renames to prevent corruption during system crashes.

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 →