87 lines
2.3 KiB
TypeScript
87 lines
2.3 KiB
TypeScript
import crypto from "node:crypto";
|
|
import { eq } from "drizzle-orm";
|
|
import { db } from "~/server/db/db";
|
|
import { agentTokensTable } from "~/server/db/schema";
|
|
import { cryptoUtils } from "~/server/utils/crypto";
|
|
|
|
export const generateToken = () => {
|
|
return `zbk_${crypto.randomBytes(32).toString("hex")}`;
|
|
};
|
|
|
|
export const hashToken = (token: string) => {
|
|
return crypto.createHash("sha256").update(token).digest("hex");
|
|
};
|
|
|
|
export const deriveLocalAgentToken = async () => {
|
|
const derived = await cryptoUtils.deriveSecret("zerobyte:local-agent-token");
|
|
return `zbk_${derived}`;
|
|
};
|
|
|
|
export const createAgentToken = async ({
|
|
name,
|
|
agentId,
|
|
createdBy,
|
|
}: {
|
|
name: string;
|
|
agentId: string;
|
|
createdBy: string;
|
|
}) => {
|
|
const plaintext = generateToken();
|
|
const tokenHash = hashToken(plaintext);
|
|
const tokenPrefix = plaintext.slice(0, 12);
|
|
|
|
const id = Bun.randomUUIDv7();
|
|
await db.insert(agentTokensTable).values({
|
|
id,
|
|
name,
|
|
tokenHash,
|
|
tokenPrefix,
|
|
agentId,
|
|
createdBy,
|
|
});
|
|
|
|
return { id, name, tokenPrefix, plaintext };
|
|
};
|
|
|
|
export const validateAgentToken = async (token: string) => {
|
|
const localToken = await deriveLocalAgentToken();
|
|
if (token === localToken) {
|
|
return { agentId: "local", organizationId: null, agentName: "local" };
|
|
}
|
|
|
|
const tokenHash = hashToken(token);
|
|
|
|
const record = await db.query.agentTokensTable.findFirst({
|
|
where: { tokenHash, revokedAt: { isNull: true } },
|
|
with: { agent: true },
|
|
});
|
|
|
|
if (!record) return null;
|
|
|
|
await db.update(agentTokensTable).set({ lastUsedAt: Date.now() }).where(eq(agentTokensTable.id, record.id));
|
|
|
|
return {
|
|
agentId: record.agentId,
|
|
organizationId: record.agent.organizationId,
|
|
agentName: record.name,
|
|
};
|
|
};
|
|
|
|
export const revokeAgentToken = async (tokenId: string, agentId: string) => {
|
|
const token = await db.query.agentTokensTable.findFirst({
|
|
where: { id: tokenId, agentId, revokedAt: { isNull: true } },
|
|
});
|
|
|
|
if (!token) return false;
|
|
|
|
await db.update(agentTokensTable).set({ revokedAt: Date.now() }).where(eq(agentTokensTable.id, tokenId));
|
|
|
|
return true;
|
|
};
|
|
|
|
export const listAgentTokens = async (agentId: string) => {
|
|
return db.query.agentTokensTable.findMany({
|
|
where: { agentId },
|
|
columns: { id: true, name: true, tokenPrefix: true, lastUsedAt: true, revokedAt: true, createdAt: true },
|
|
});
|
|
};
|