Skip to main content
Codebuff provides a secure system for managing API credentials and authentication tokens.

API Key Management

API keys are used to authenticate with the Codebuff backend and access model providers.

Storage Location

Credentials are stored in your home directory:
// From sdk/src/credentials.ts:53-60
export const getConfigDir = (clientEnv: ClientEnv = env): string => {
  const envSuffix =
    clientEnv.NEXT_PUBLIC_CB_ENVIRONMENT &&
    clientEnv.NEXT_PUBLIC_CB_ENVIRONMENT !== 'prod'
      ? `-${clientEnv.NEXT_PUBLIC_CB_ENVIRONMENT}`
      : ''
  return path.join(os.homedir(), '.config', `manicode${envSuffix}`)
}

export const getCredentialsPath = (clientEnv: ClientEnv = env): string => {
  return path.join(getConfigDir(clientEnv), 'credentials.json')
}
Default Path: ~/.config/manicode/credentials.json

Credentials File Format

The credentials file contains user authentication and OAuth tokens:
// From sdk/src/credentials.ts:29-32
const credentialsFileSchema = z.object({
  default: userSchema.optional(),
  claudeOAuth: claudeOAuthSchema.optional(),
})
Example credentials.json:
{
  "default": {
    "id": "user_abc123",
    "email": "[email protected]",
    "apiKey": "cb_..."
  },
  "claudeOAuth": {
    "accessToken": "oauth_token_...",
    "refreshToken": "refresh_token_...",
    "expiresAt": 1735689600000,
    "connectedAt": 1704153600000
  }
}

Authentication Flow

Reading User Credentials

// From sdk/src/credentials.ts:69-83
export const getUserCredentials = (clientEnv: ClientEnv = env): User | null => {
  const credentialsPath = getCredentialsPath(clientEnv)
  if (!fs.existsSync(credentialsPath)) {
    return null
  }

  try {
    const credentialsFile = fs.readFileSync(credentialsPath, 'utf8')
    const user = userFromJson(credentialsFile)
    return user || null
  } catch (error) {
    console.error('Error reading credentials', error)
    return null
  }
}

Validating Credentials

import { getUserCredentials } from '@codebuff/sdk'

const user = getUserCredentials()
if (!user) {
  console.error('Not authenticated. Please run: codebuff login')
  process.exit(1)
}

console.log('Authenticated as:', user.email)

Claude OAuth Credentials

For direct Anthropic API access, Codebuff manages Claude OAuth tokens.

OAuth Token Schema

// From sdk/src/credentials.ts:18-23
const claudeOAuthSchema = z.object({
  accessToken: z.string(),
  refreshToken: z.string(),
  expiresAt: z.number(),
  connectedAt: z.number(),
})

export interface ClaudeOAuthCredentials {
  accessToken: string
  refreshToken: string
  expiresAt: number // Unix timestamp in milliseconds
  connectedAt: number // Unix timestamp in milliseconds
}

Getting OAuth Credentials

// From sdk/src/credentials.ts:100-132
export const getClaudeOAuthCredentials = (
  clientEnv: ClientEnv = env,
): ClaudeOAuthCredentials | null => {
  // Check environment variable first
  const envToken = getClaudeOAuthTokenFromEnv()
  if (envToken) {
    return {
      accessToken: envToken,
      refreshToken: '',
      expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000, // 1 year
      connectedAt: Date.now(),
    }
  }

  const credentialsPath = getCredentialsPath(clientEnv)
  if (!fs.existsSync(credentialsPath)) {
    return null
  }

  try {
    const credentialsFile = fs.readFileSync(credentialsPath, 'utf8')
    const parsed = credentialsFileSchema.safeParse(JSON.parse(credentialsFile))
    if (!parsed.success || !parsed.data.claudeOAuth) {
      return null
    }
    return parsed.data.claudeOAuth
  } catch (error) {
    console.error('Error reading Claude OAuth credentials', error)
    return null
  }
}

Saving OAuth Credentials

// From sdk/src/credentials.ts:138-162
export const saveClaudeOAuthCredentials = (
  credentials: ClaudeOAuthCredentials,
  clientEnv: ClientEnv = env,
): void => {
  const configDir = getConfigDir(clientEnv)
  const credentialsPath = getCredentialsPath(clientEnv)

  ensureDirectoryExistsSync(configDir)

  let existingData: Record<string, unknown> = {}
  if (fs.existsSync(credentialsPath)) {
    try {
      existingData = JSON.parse(fs.readFileSync(credentialsPath, 'utf8'))
    } catch {
      // Ignore parse errors, start fresh
    }
  }

  const updatedData = {
    ...existingData,
    claudeOAuth: credentials,
  }

  fs.writeFileSync(credentialsPath, JSON.stringify(updatedData, null, 2))
}

Token Refresh

Codebuff automatically refreshes expired OAuth tokens:
// From sdk/src/credentials.ts:207-268
export const refreshClaudeOAuthToken = async (
  clientEnv: ClientEnv = env,
): Promise<ClaudeOAuthCredentials | null> => {
  // If a refresh is already in progress, wait for it
  if (refreshPromise) {
    return refreshPromise
  }

  const credentials = getClaudeOAuthCredentials(clientEnv)
  if (!credentials?.refreshToken) {
    return null
  }

  refreshPromise = (async () => {
    try {
      const response = await fetch(
        'https://console.anthropic.com/v1/oauth/token',
        {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            grant_type: 'refresh_token',
            refresh_token: credentials.refreshToken,
            client_id: CLAUDE_OAUTH_CLIENT_ID,
          }),
        },
      )

      if (!response.ok) {
        clearClaudeOAuthCredentials(clientEnv)
        return null
      }

      const data = await response.json()
      const newCredentials: ClaudeOAuthCredentials = {
        accessToken: data.access_token,
        refreshToken: data.refresh_token ?? credentials.refreshToken,
        expiresAt: Date.now() + data.expires_in * 1000,
        connectedAt: credentials.connectedAt,
      }

      saveClaudeOAuthCredentials(newCredentials, clientEnv)
      return newCredentials
    } catch {
      clearClaudeOAuthCredentials(clientEnv)
      return null
    } finally {
      refreshPromise = null
    }
  })()

  return refreshPromise
}

Getting Valid Credentials

Always use this function to get credentials - it handles refresh automatically:
// From sdk/src/credentials.ts:278-300
export const getValidClaudeOAuthCredentials = async (
  clientEnv: ClientEnv = env,
): Promise<ClaudeOAuthCredentials | null> => {
  const credentials = getClaudeOAuthCredentials(clientEnv)
  if (!credentials) {
    return null
  }

  // Check if token is from environment variable (no refresh needed)
  if (!credentials.refreshToken) {
    return credentials
  }

  // Check if token is valid with 5 minute buffer
  const bufferMs = 5 * 60 * 1000
  if (credentials.expiresAt > Date.now() + bufferMs) {
    return credentials
  }

  // Token is expired or expiring soon, try to refresh
  return refreshClaudeOAuthToken(clientEnv)
}

Environment Variables

Credentials can also be provided via environment variables:

Claude OAuth Token

export CLAUDE_OAUTH_TOKEN="your_token_here"
When set, this takes precedence over the credentials file:
// Environment variable takes precedence
const envToken = getClaudeOAuthTokenFromEnv()
if (envToken) {
  return {
    accessToken: envToken,
    refreshToken: '',
    expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000,
    connectedAt: Date.now(),
  }
}

Security Best Practices

File Permissions

Ensure credentials file has restrictive permissions:
chmod 600 ~/.config/manicode/credentials.json

Never Commit Credentials

Add to .gitignore:
# Codebuff credentials
.config/manicode/credentials.json

Environment-Specific Credentials

Codebuff supports environment-specific config directories:
// Development environment
const envSuffix = clientEnv.NEXT_PUBLIC_CB_ENVIRONMENT !== 'prod'
  ? `-${clientEnv.NEXT_PUBLIC_CB_ENVIRONMENT}`
  : ''

const configDir = path.join(os.homedir(), '.config', `manicode${envSuffix}`)
Examples:
  • Production: ~/.config/manicode/
  • Development: ~/.config/manicode-dev/
  • Staging: ~/.config/manicode-staging/

Token Expiry Handling

Always check token validity before use:
// From sdk/src/credentials.ts:189-197
export const isClaudeOAuthValid = (clientEnv: ClientEnv = env): boolean => {
  const credentials = getClaudeOAuthCredentials(clientEnv)
  if (!credentials) {
    return false
  }
  // Add 5 minute buffer before expiry
  const bufferMs = 5 * 60 * 1000
  return credentials.expiresAt > Date.now() + bufferMs
}

Clearing Credentials

// From sdk/src/credentials.ts:168-183
export const clearClaudeOAuthCredentials = (
  clientEnv: ClientEnv = env,
): void => {
  const credentialsPath = getCredentialsPath(clientEnv)
  if (!fs.existsSync(credentialsPath)) {
    return
  }

  try {
    const existingData = JSON.parse(fs.readFileSync(credentialsPath, 'utf8'))
    delete existingData.claudeOAuth
    fs.writeFileSync(credentialsPath, JSON.stringify(existingData, null, 2))
  } catch {
    // Ignore errors
  }
}

Next Steps

Build docs developers (and LLMs) love