Files
cfw-autumn/server/src/internal/dev/api-keys/apiKeyUtils.ts

104 lines
2.3 KiB
TypeScript

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,
};
};