Files
ccs/src/utils/bar-auth-token.ts
T

56 lines
1.8 KiB
TypeScript

import crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import { getCcsDir } from '../config/config-loader-facade';
export const BAR_AUTH_TOKEN_HEADER = 'x-ccs-bar-token';
export const BAR_AUTH_NONCE_HEADER = 'x-ccs-bar-nonce';
const TOKEN_BYTE_LENGTH = 32;
const NONCE_MIN_LENGTH = 16;
export function createBarAuthNonce(): string {
return crypto.randomBytes(TOKEN_BYTE_LENGTH).toString('hex');
}
export function isValidBarAuthNonce(nonce: string): boolean {
return /^[a-f0-9]+$/i.test(nonce) && nonce.length >= NONCE_MIN_LENGTH && nonce.length <= 128;
}
export function createBarAuthProof(token: string, nonce: string): string {
return crypto.createHmac('sha256', token).update(nonce).digest('hex');
}
export function isMatchingBarAuthProof(token: string, nonce: string, proof: string): boolean {
if (!isValidBarAuthNonce(nonce) || !/^[a-f0-9]{64}$/i.test(proof)) {
return false;
}
const expected = createBarAuthProof(token, nonce);
return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(proof, 'hex'));
}
export function getBarAuthTokenPath(ccsDir = getCcsDir()): string {
return path.join(ccsDir, 'bar', '.auth-token');
}
function isValidToken(token: string): boolean {
return /^[a-f0-9]{64}$/i.test(token);
}
export function getOrCreateBarAuthToken(ccsDir = getCcsDir()): string {
const tokenPath = getBarAuthTokenPath(ccsDir);
try {
const token = fs.readFileSync(tokenPath, 'utf8').trim();
if (isValidToken(token)) {
return token;
}
} catch {
// Missing or unreadable tokens are regenerated below.
}
const token = crypto.randomBytes(TOKEN_BYTE_LENGTH).toString('hex');
fs.mkdirSync(path.dirname(tokenPath), { recursive: true });
fs.writeFileSync(tokenPath, `${token}\n`, { mode: 0o600 });
return token;
}