Files
ChatLab/apps/cli/src/http/auth.ts
T
digua 78e41a65b5 refactor: migrate CLI from packages/server to apps/cli
Move the CLI application from packages/server/ to apps/cli/ to align
with the monorepo convention (apps/ for deployable applications,
packages/ for shared libraries). Rename package to @openchatlab/chatlab.

Also fix a bug in ensureToken() that used raw string manipulation to
write config.toml, causing duplicate token fields that corrupted the
TOML file. This led to config parse failures and user_data_dir being
overwritten with defaults. Now uses writeConfigField() for safe writes.
2026-05-19 21:33:00 +08:00

48 lines
1.3 KiB
TypeScript

/**
* ChatLab HTTP API — Bearer Token authentication hook
*
* 从 electron/main/api/auth.ts 迁移,使用 @openchatlab/config 读取 token。
*/
import type { FastifyRequest, FastifyReply } from 'fastify'
import { timingSafeEqual } from 'crypto'
import { unauthorized, errorResponse } from './errors'
let cachedToken: string | null = null
/**
* 设置 auth hook 使用的 token(由 server 启动时注入)
*/
export function setAuthToken(token: string): void {
cachedToken = token
}
function safeTokenCompare(a: string, b: string): boolean {
const bufA = Buffer.from(a)
const bufB = Buffer.from(b)
if (bufA.length !== bufB.length) return false
return timingSafeEqual(bufA, bufB)
}
export async function authHook(request: FastifyRequest, reply: FastifyReply): Promise<void> {
if (!cachedToken) return
// /_web/ 内部 API 不需要认证(仅限同源 Web UI 使用)
if (request.url.startsWith('/_web/')) return
const authHeader = request.headers.authorization
if (!authHeader || !authHeader.startsWith('Bearer ')) {
const err = unauthorized()
reply.code(err.statusCode).send(errorResponse(err))
return
}
const token = authHeader.slice(7)
if (!safeTokenCompare(token, cachedToken)) {
const err = unauthorized()
reply.code(err.statusCode).send(errorResponse(err))
return
}
}