import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from 'fs'; import { join } from 'path'; import { generateKeyPairSync, randomUUID } from 'crypto'; import { logger } from '../../logger.js'; export interface Jwks { keys: object[]; } /** data/secrets/a2a-jwks.json に署名鍵(RSA)を永続。session-secret.key と同じ運用。 */ export function loadOrCreateJwks(secretsDir: string): Jwks { const file = join(secretsDir, 'a2a-jwks.json'); if (existsSync(file)) { return JSON.parse(readFileSync(file, 'utf-8')) as Jwks; } mkdirSync(secretsDir, { recursive: true }); const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 }); const jwk = privateKey.export({ format: 'jwk' }) as Record; jwk.kid = randomUUID(); jwk.use = 'sig'; jwk.alg = 'RS256'; const jwks: Jwks = { keys: [jwk] }; writeFileSync(file, JSON.stringify(jwks), { mode: 0o600 }); try { chmodSync(file, 0o600); } catch { /* best-effort */ } logger.info(`[a2a-oidc] generated jwks file=${file} kid=${jwk.kid}`); return jwks; }