diff --git a/apps/web/src/lib/oauth/secret-store.ts b/apps/web/src/lib/oauth/secret-store.ts index ec435c6..b6b326b 100644 --- a/apps/web/src/lib/oauth/secret-store.ts +++ b/apps/web/src/lib/oauth/secret-store.ts @@ -5,6 +5,7 @@ import { randomBytes, } from "node:crypto"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { homedir } from "node:os"; import { join } from "node:path"; import { deleteSecret, @@ -43,13 +44,33 @@ function parseConfiguredKey(value: string): Buffer | null { return createHash("sha256").update(trimmed).digest(); } +function isLocalInstall(): boolean { + return process.env.SECOND_LOCAL_INSTALL === "1"; +} + +function getGeneratedLocalSecretDir(): string { + const configured = process.env.SECOND_LOCAL_SECRET_DIR?.trim(); + if (configured) return configured; + + if (isLocalInstall()) { + return join(homedir(), ".second", "secrets"); + } + + return join(process.cwd(), LOCAL_SECRET_DIR); +} + function getGeneratedLocalSecretKey(): Buffer { - const path = join(process.cwd(), LOCAL_SECRET_DIR, LOCAL_SECRET_KEY_FILE); + const secretDir = getGeneratedLocalSecretDir(); + const path = join(secretDir, LOCAL_SECRET_KEY_FILE); if (existsSync(path)) { - return Buffer.from(readFileSync(path, "utf-8").trim(), "base64"); + const key = Buffer.from(readFileSync(path, "utf-8").trim(), "base64"); + if (key.length !== 32) { + throw new Error("Invalid local OAuth secret-store key."); + } + return key; } - mkdirSync(join(process.cwd(), LOCAL_SECRET_DIR), { recursive: true }); + mkdirSync(secretDir, { recursive: true, mode: 0o700 }); const key = randomBytes(32); writeFileSync(path, `${key.toString("base64")}\n`, { mode: 0o600 }); return key; @@ -60,7 +81,7 @@ function getLocalEncryptionKey(): Buffer { const configuredKey = configured ? parseConfiguredKey(configured) : null; if (configuredKey) return configuredKey; - if (process.env.NODE_ENV === "production") { + if (process.env.NODE_ENV === "production" && !isLocalInstall()) { throw new Error( "SECOND_TOKEN_ENCRYPTION_KEY is required when WorkOS Vault is not configured in production.", ); diff --git a/packages/cli-local-darwin-arm64/bin/second-local.js b/packages/cli-local-darwin-arm64/bin/second-local.js index 1d476e3..01f4e64 100755 --- a/packages/cli-local-darwin-arm64/bin/second-local.js +++ b/packages/cli-local-darwin-arm64/bin/second-local.js @@ -931,6 +931,7 @@ async function startWeb({ WORKER_URL: workerUrl, REDIS_URL: redisUrl, SECOND_LOCAL_INSTALL: "1", + SECOND_LOCAL_SECRET_DIR: SECRETS_DIR, SECOND_RELEASE_RUNTIME: "native", SECOND_RELEASE_VERSION: packageMetadata.version, SECOND_RELEASE_PACKAGE: packageMetadata.name,