File size: 1,509 Bytes
c7d34c1
 
b1cfe1b
c7d34c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32151b6
c7d34c1
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { AgentApiError } from './api-error-response';
import { verifyPasswordHash } from './server-runtime';
import crypto from 'crypto';

export function assertAgentAuthorized(headers: Headers, env: Record<string, string | undefined> = process.env): void {
    const configuredToken = env.AGENT_API_TOKEN?.trim();
    if (configuredToken) {
        const authorization = headers.get('authorization') || '';
        const expected = `Bearer ${configuredToken}`;
        if (!timingSafeStringEqual(authorization, expected)) {
            throw new AgentApiError({
                code: 'unauthorized',
                message: '未授权:Bearer token 无效或缺失。',
                status: 401,
                retryable: false
            });
        }
        return;
    }

    const appPassword = env.APP_PASSWORD?.trim();
    if (!appPassword) return;

    const passwordHash = headers.get('x-app-password-hash');
    if (!passwordHash || !verifyPasswordHash(passwordHash, appPassword)) {
        throw new AgentApiError({
            code: 'unauthorized',
            message: '未授权:访问码哈希无效或缺失。',
            status: 401,
            retryable: false
        });
    }
}

function timingSafeStringEqual(actual: string, expected: string): boolean {
    const actualHash = crypto.createHash('sha256').update(actual).digest();
    const expectedHash = crypto.createHash('sha256').update(expected).digest();
    return crypto.timingSafeEqual(actualHash, expectedHash);
}