import { generateId } from "@/utils/genUtils.js"; import { ApiKey, AppEnv } from "@autumn/shared"; import { SupabaseClient } from "@supabase/supabase-js"; import crypto from "crypto"; import { ApiKeyService, CachedKeyService } from "../ApiKeyService.js"; import { CacheType } from "@/external/caching/cacheActions.js"; import { getAPIKeyCache } from "@/external/caching/cacheUtils.js"; import { DrizzleCli } from "@/db/initDrizzle.js"; function generateApiKey(length = 32, prefix = "") { try { // Define allowed characters (alphanumeric only) const allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const array = new Uint8Array(length); crypto.getRandomValues(array); // Convert random bytes to alphanumeric string const key = Array.from(array) .map((byte) => allowedChars[byte % allowedChars.length]) .join(""); return prefix ? `${prefix}_${key}` : key; } catch (error) { console.error("Failed to generate API key:", error); throw new Error("Failed to generate secure API key"); } } export const hashApiKey = (apiKey: string) => { return crypto.createHash("sha256").update(apiKey).digest("hex"); }; export const createKey = async ({ db, env, name, orgId, prefix, meta, }: { db: DrizzleCli; env: AppEnv; name: string; orgId: string; prefix: string; meta: any; }) => { const apiKey = generateApiKey(42, prefix); const hashedKey = hashApiKey(apiKey); const apiKeyData: ApiKey = { id: generateId("key"), org_id: orgId, user_id: "", name, prefix: apiKey.substring(0, 14), created_at: Date.now(), env, hashed_key: hashedKey, meta, }; await ApiKeyService.insert({ db, apiKey: apiKeyData }); return apiKey; }; export const verifyKey = async ({ db, key, }: { db: DrizzleCli; key: string; }) => { const hashedKey = hashApiKey(key); const env = key.startsWith("am_sk_test") ? AppEnv.Sandbox : AppEnv.Live; const data = await getAPIKeyCache({ action: CacheType.SecretKey, key: hashedKey, fn: async () => await ApiKeyService.verifyAndFetch({ db, secretKey: key, hashedKey, env, }), }); if (!data) { return { valid: false, data: null, }; } return { valid: true, data: data, }; };