Phase 12: Initial Setup Wizard #22
@@ -193,10 +193,17 @@ setupRouter.post('/validate/oidc', async (c) => {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// POST /api/setup/validate/vapid
|
// POST /api/setup/validate/vapid
|
||||||
//
|
//
|
||||||
// Validates VAPID key pair structure by calling webpush.setVapidDetails().
|
// Validates the operator-entered VAPID public key (app_config.vapid_public_key)
|
||||||
// Reads BOTH keys ONLY from process.env — NEVER from app_config (T-12-06 / D-01 / SC-3).
|
// against the configured key pair:
|
||||||
// VAPID_PRIVATE_KEY is never returned in any response.
|
// 1. Equality (gap 2): the submitted public key MUST equal process.env.VAPID_PUBLIC_KEY.
|
||||||
// Returns 200 { ok: true } on structural validity, 400 { ok: false } on failure.
|
// A wrong/typoed key (e.g. "BH123") now fails the row and gates Continue — push
|
||||||
|
// would silently break in production otherwise (SETUP-02).
|
||||||
|
// 2. Structural: webpush.setVapidDetails() validates the env pair's byte structure.
|
||||||
|
//
|
||||||
|
// VAPID_PRIVATE_KEY is read ONLY from process.env — NEVER from app_config or returned
|
||||||
|
// (T-12-06 / D-01 / SC-3). The equality check compares the submitted PUBLIC key to the
|
||||||
|
// env PUBLIC key only — the private key is never compared or echoed.
|
||||||
|
// Returns 200 { ok: true } on success, 400 { ok: false } on any failure.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
setupRouter.post('/validate/vapid', async (c) => {
|
setupRouter.post('/validate/vapid', async (c) => {
|
||||||
@@ -210,6 +217,23 @@ setupRouter.post('/validate/vapid', async (c) => {
|
|||||||
return c.json({ ok: false, error: 'VAPID_PRIVATE_KEY and VAPID_PUBLIC_KEY env vars must be set' }, 400);
|
return c.json({ ok: false, error: 'VAPID_PRIVATE_KEY and VAPID_PUBLIC_KEY env vars must be set' }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gap 2: assert the operator-submitted public key matches the env public key BEFORE
|
||||||
|
// the structural check. Read the submitted key from app_config (same idiom as validate/oidc).
|
||||||
|
const [submittedRow] = await db
|
||||||
|
.select({ value: appConfig.value })
|
||||||
|
.from(appConfig)
|
||||||
|
.where(eq(appConfig.key, 'vapid_public_key'))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
const submittedPublicKey = submittedRow?.value;
|
||||||
|
if (!submittedPublicKey || submittedPublicKey !== publicKey) {
|
||||||
|
return c.json({
|
||||||
|
ok: false,
|
||||||
|
error:
|
||||||
|
'VAPID public key does not match the configured key pair. Paste the exact VAPID_PUBLIC_KEY printed by `npm run generate-secrets`.',
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// setVapidDetails runs validatePrivateKey (32-byte check) and validatePublicKey (65-byte check)
|
// setVapidDetails runs validatePrivateKey (32-byte check) and validatePublicKey (65-byte check)
|
||||||
// internally — this is the library's own structural validation (Pattern 7).
|
// internally — this is the library's own structural validation (Pattern 7).
|
||||||
|
|||||||
Reference in New Issue
Block a user