import { createHash, randomBytes, randomInt } from 'node:crypto'; /** * Server-only helpers for the OAuth2 device-authorization-grant style "device * pairing" flow (Home Assistant add-on). Dependency-free (node:crypto only). * * Opaque secrets (device_code, device token) are high-entropy random values, * so a fast SHA-256 hash is sufficient for at-rest storage — we never persist * or log the plaintext. Mirrors the hashing style in lib/auth/recovery.ts. */ /** Stable hash used both when storing and when looking up an opaque secret. */ export function hashToken(raw: string): string { return createHash('sha256').update(raw).digest('hex'); } /** Opaque device_code (the secret the device polls with). */ export function generateDeviceCode(): string { return randomBytes(32).toString('hex'); } /** Opaque long-lived device bearer token. */ export function generateDeviceToken(): string { return randomBytes(32).toString('hex'); } // Unambiguous alphabet: no 0/O/1/I to avoid transcription errors. const USER_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; /** Short human code, formatted `XXXX-XXXX` (uppercase, unbiased selection). */ export function generateUserCode(): string { let out = ''; for (let i = 0; i < 8; i++) { out += USER_CODE_ALPHABET[randomInt(USER_CODE_ALPHABET.length)]; } return `${out.slice(0, 4)}-${out.slice(4, 8)}`; } /** * Normalise a user-typed code: uppercase, strip non-alphanumerics, re-insert * the dash as `XXXX-XXXX`. Returns '' if the result is not exactly 8 alnum * characters. */ export function normalizeUserCode(raw: string): string { const cleaned = raw.toUpperCase().replace(/[^A-Z0-9]/g, ''); if (cleaned.length !== 8) return ''; return `${cleaned.slice(0, 4)}-${cleaned.slice(4, 8)}`; } /** Device-code lifetime before it must be re-requested. */ export const DEVICE_CODE_TTL_SECONDS = 600; /** Recommended polling interval the device should respect. */ export const DEVICE_POLL_INTERVAL_SECONDS = 5; /** frp server the add-on connects back to. */ export const FRP_SERVER_ADDR = 'linumiq.net'; export const FRP_SERVER_PORT = 7000;