26 lines
1.1 KiB
TypeScript
26 lines
1.1 KiB
TypeScript
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<string, unknown>;
|
|
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;
|
|
}
|