/** * portal/commerce/opsauth.mjs — 운영자 콘솔(HAL)에서 들어오는 내부 호출(/internal/ops/*) 인증. * * 운영자 계정·세션은 HAL 에 있고(§25), HAL 이 포털의 내부 API 를 서비스 키로 호출한다(lib/serviceauth). * 비밀은 포털 보관소의 'service:' 에 있다. 허용 키 id 는 'hal' 뿐이다. * 호출 주체 운영자 식별(감사 기록용)은 HAL 이 X-Ops-Actor 헤더로 전달한다(이름만, 비밀 없음). * 테스트에서는 deps.opsAuthBypass=true 로 서명 없이 부를 수 있다(운영 코드 경로에서는 절대 켜지 않는다). */ import { HttpError } from '../../lib/http.mjs'; import { verifyCtx } from '../../lib/serviceauth.mjs'; const ALLOWED_KEYS = new Set(['hal']); /** 검증 후 { keyId, actor } 를 돌려준다. 실패는 401. */ export async function requireOps(ctx, deps) { const actor = String(ctx.headers['x-ops-actor'] || 'operator').slice(0, 80); if (deps.opsAuthBypass) return { keyId: 'test', actor }; const r = await verifyCtx(ctx, (id) => (ALLOWED_KEYS.has(id) ? deps.vault.get('service:' + id) : null)); if (!r.ok) throw new HttpError(401, 'service_auth_failed', `운영자 콘솔 호출 인증 실패(${r.reason})`); return { keyId: r.keyId, actor }; }