UNPKG

codebrag

Version:

CLI tool for tracking Claude Code usage and leaderboard participation

119 lines (91 loc) 3.27 kB
import { createCipheriv, createDecipheriv, randomBytes, createHash } from 'crypto'; import { loadConfig, saveConfig } from '../utils/config.js'; // Derive a proper encryption key from the environment variable const ENCRYPTION_PASSWORD = process.env.ENCRYPTION_KEY || 'default_key_change_in_production'; const ALGORITHM = 'aes-256-gcm'; const SALT = 'codebrag-salt'; // Derive a 32-byte key from the password function deriveKey(password) { return createHash('sha256').update(password + SALT).digest(); } function encryptToken(token) { try { const key = deriveKey(ENCRYPTION_PASSWORD); const iv = randomBytes(16); const cipher = createCipheriv(ALGORITHM, key, iv); let encrypted = cipher.update(token, 'utf8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag(); // Combine IV, authTag, and encrypted data return iv.toString('hex') + ':' + authTag.toString('hex') + ':' + encrypted; } catch (error) { // If encryption fails, return token as-is (not recommended for production) console.warn('Token encryption failed, storing as plain text'); return token; } } function decryptToken(encryptedToken) { try { // Check if token is in encrypted format if (!encryptedToken.includes(':')) { // Assume it's plain text (for backward compatibility) return encryptedToken; } const [ivHex, authTagHex, encrypted] = encryptedToken.split(':'); const key = deriveKey(ENCRYPTION_PASSWORD); const iv = Buffer.from(ivHex, 'hex'); const authTag = Buffer.from(authTagHex, 'hex'); const decipher = createDecipheriv(ALGORITHM, key, iv); decipher.setAuthTag(authTag); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } catch (error) { // If decryption fails, assume token is plain text return encryptedToken; } } // Store OAuth 1.0a tokens export async function storeOAuth1aTokens(oauthToken, oauthTokenSecret) { const config = await loadConfig(); config.oauth_token = encryptToken(oauthToken); config.oauth_token_secret = encryptToken(oauthTokenSecret); config.oauthVersion = '1.0a'; await saveConfig(config); } export async function getTokens() { const config = await loadConfig(); // Check for OAuth 1.0a tokens if (!config.oauth_token || !config.oauth_token_secret) { return null; } return { oauthVersion: '1.0a', oauth_token: decryptToken(config.oauth_token), oauth_token_secret: decryptToken(config.oauth_token_secret) }; } export async function isTokenExpired() { const tokens = await getTokens(); if (!tokens) { return true; } // OAuth 1.0a tokens never expire return false; } export async function refreshAccessToken() { const tokens = await getTokens(); if (!tokens) { throw new Error('No tokens available'); } // OAuth 1.0a tokens don't need refreshing return tokens; } export async function getValidAccessToken() { const tokens = await getTokens(); if (!tokens) { throw new Error('No tokens available - please authenticate first'); } // OAuth 1.0a tokens are always valid (unless revoked) return tokens; }