Files
ccs/src/cliproxy/auth-token-manager.ts
T
kaitranntt c4f09168ff feat(cliproxy): add customizable auth token manager
- Add CLIProxyAuthConfig type with api_key and management_secret fields
- Implement auth-token-manager.ts with inheritance chain:
  variant auth → global auth → default constants
- Add secure token generation using crypto.randomBytes (256-bit entropy)
- Export auth functions from cliproxy barrel (index.ts)
- Bump UNIFIED_CONFIG_VERSION to 6
2025-12-25 14:35:43 -05:00

204 lines
5.4 KiB
TypeScript

/**
* Auth Token Manager for CLIProxyAPI
*
* Manages API key and management secret resolution with inheritance:
* - Per-variant override → Global config → Default constants
*
* Provides secure token generation for user customization.
*/
import { randomBytes } from 'crypto';
import { loadOrCreateUnifiedConfig, saveUnifiedConfig } from '../config/unified-config-loader';
import { CCS_INTERNAL_API_KEY, CCS_CONTROL_PANEL_SECRET } from './config-generator';
/**
* Generate a cryptographically secure token.
* Uses CSPRNG (crypto.randomBytes) for proper entropy.
*
* @param length - Number of random bytes (default: 32 = 256-bit entropy)
* @returns Base64URL-encoded token (43 chars for 32 bytes)
*/
export function generateSecureToken(length = 32): string {
return randomBytes(length).toString('base64url');
}
/**
* Mask a token for display.
* Shows first 4 and last 4 chars: "ccs_...4f2a"
* Used by CLI commands and API routes.
*/
export function maskToken(token: string): string {
if (token.length <= 8) return '****';
return `${token.slice(0, 4)}...${token.slice(-4)}`;
}
/**
* Get effective API key with inheritance chain.
* Priority: variant auth → global cliproxy.auth → default constant
*
* @param variantName - Optional variant name for per-variant override
* @returns Resolved API key
*/
export function getEffectiveApiKey(variantName?: string): string {
const config = loadOrCreateUnifiedConfig();
// Priority 1: Per-variant override
if (variantName) {
const variant = config.cliproxy.variants[variantName];
if (variant?.auth?.api_key) {
return variant.auth.api_key;
}
}
// Priority 2: Global cliproxy.auth
if (config.cliproxy.auth?.api_key) {
return config.cliproxy.auth.api_key;
}
// Priority 3: Default constant (backwards compatible)
return CCS_INTERNAL_API_KEY;
}
/**
* Get effective management secret.
* Priority: global cliproxy.auth → default constant
*
* Note: Management secret is global-only (no per-variant override)
* as it controls the Control Panel access for the entire CLIProxy instance.
*
* @returns Resolved management secret
*/
export function getEffectiveManagementSecret(): string {
const config = loadOrCreateUnifiedConfig();
// Priority 1: Global cliproxy.auth
if (config.cliproxy.auth?.management_secret) {
return config.cliproxy.auth.management_secret;
}
// Priority 2: Default constant (backwards compatible)
return CCS_CONTROL_PANEL_SECRET;
}
/**
* Set global API key.
* Updates cliproxy.auth.api_key in config.yaml.
*
* @param apiKey - New API key (or undefined to reset to default)
*/
export function setGlobalApiKey(apiKey: string | undefined): void {
const config = loadOrCreateUnifiedConfig();
if (!config.cliproxy.auth) {
config.cliproxy.auth = {};
}
if (apiKey === undefined) {
delete config.cliproxy.auth.api_key;
} else {
config.cliproxy.auth.api_key = apiKey;
}
saveUnifiedConfig(config);
}
/**
* Set global management secret.
* Updates cliproxy.auth.management_secret in config.yaml.
*
* @param secret - New management secret (or undefined to reset to default)
*/
export function setGlobalManagementSecret(secret: string | undefined): void {
const config = loadOrCreateUnifiedConfig();
if (!config.cliproxy.auth) {
config.cliproxy.auth = {};
}
if (secret === undefined) {
delete config.cliproxy.auth.management_secret;
} else {
config.cliproxy.auth.management_secret = secret;
}
saveUnifiedConfig(config);
}
/**
* Set per-variant API key override.
* Updates variants[variantName].auth.api_key in config.yaml.
*
* @param variantName - Variant name
* @param apiKey - New API key (or undefined to remove override)
*/
export function setVariantApiKey(variantName: string, apiKey: string | undefined): void {
const config = loadOrCreateUnifiedConfig();
const variant = config.cliproxy.variants[variantName];
if (!variant) {
throw new Error(`Variant '${variantName}' not found`);
}
if (!variant.auth) {
variant.auth = {};
}
if (apiKey === undefined) {
delete variant.auth.api_key;
// Clean up empty auth object
if (Object.keys(variant.auth).length === 0) {
delete variant.auth;
}
} else {
variant.auth.api_key = apiKey;
}
saveUnifiedConfig(config);
}
/**
* Reset all auth settings to defaults.
* Removes cliproxy.auth and all variant auth overrides.
*/
export function resetAuthToDefaults(): void {
const config = loadOrCreateUnifiedConfig();
// Remove global auth
delete config.cliproxy.auth;
// Remove all variant auth overrides
for (const variantName of Object.keys(config.cliproxy.variants)) {
const variant = config.cliproxy.variants[variantName];
if (variant.auth) {
delete variant.auth;
}
}
saveUnifiedConfig(config);
}
/**
* Get auth configuration summary for display.
* Returns current effective values and whether they're customized.
*/
export function getAuthSummary(): {
apiKey: { value: string; isCustom: boolean };
managementSecret: { value: string; isCustom: boolean };
} {
const config = loadOrCreateUnifiedConfig();
const customApiKey = config.cliproxy.auth?.api_key;
const customSecret = config.cliproxy.auth?.management_secret;
return {
apiKey: {
value: customApiKey || CCS_INTERNAL_API_KEY,
isCustom: !!customApiKey,
},
managementSecret: {
value: customSecret || CCS_CONTROL_PANEL_SECRET,
isCustom: !!customSecret,
},
};
}