Files
claude-code/packages/acp-link/src/ws-auth.ts
2026-05-01 21:39:30 +08:00

69 lines
1.7 KiB
TypeScript

import { createHash, timingSafeEqual } from 'node:crypto'
const WS_AUTH_PROTOCOL_PREFIX = 'rcs.auth.'
function sha256(value: string): Buffer {
return createHash('sha256').update(value).digest()
}
export function encodeWebSocketAuthProtocol(token: string): string {
return `${WS_AUTH_PROTOCOL_PREFIX}${Buffer.from(token, 'utf8').toString('base64url')}`
}
export function decodeWebSocketAuthProtocol(
protocolHeader: string | undefined,
): string | undefined {
if (!protocolHeader) {
return undefined
}
for (const protocol of protocolHeader.split(',')) {
const trimmed = protocol.trim()
if (!trimmed.startsWith(WS_AUTH_PROTOCOL_PREFIX)) {
continue
}
const encoded = trimmed.slice(WS_AUTH_PROTOCOL_PREFIX.length)
if (!encoded) {
return undefined
}
try {
const token = Buffer.from(encoded, 'base64url').toString('utf8')
return token.length > 0 ? token : undefined
} catch {
return undefined
}
}
return undefined
}
export function extractBearerToken(
authorizationHeader: string | undefined,
): string | undefined {
return authorizationHeader?.startsWith('Bearer ')
? authorizationHeader.slice('Bearer '.length)
: undefined
}
export function extractWebSocketAuthToken(headers: {
authorization?: string
protocol?: string
}): string | undefined {
return (
extractBearerToken(headers.authorization) ??
decodeWebSocketAuthProtocol(headers.protocol)
)
}
export function authTokensEqual(
providedToken: string | undefined,
expectedToken: string | undefined,
): boolean {
if (!providedToken || !expectedToken) {
return false
}
return timingSafeEqual(sha256(providedToken), sha256(expectedToken))
}