From 36d1f2c0ec69e857ee1edfb36d26a137e75e7c71 Mon Sep 17 00:00:00 2001 From: "Andrey A." <56412611+aantti@users.noreply.github.com> Date: Thu, 14 May 2026 17:48:49 +0200 Subject: [PATCH] feat(self-hosted): do not require openssl or node for new auth (#45941) --- docker/utils/add-new-auth-keys.sh | 58 ++++++++++++++++++----------- docker/utils/rotate-new-api-keys.sh | 37 +++++++++++++++--- 2 files changed, 69 insertions(+), 26 deletions(-) diff --git a/docker/utils/add-new-auth-keys.sh b/docker/utils/add-new-auth-keys.sh index a48ac3eb9a1..67a28486441 100644 --- a/docker/utils/add-new-auth-keys.sh +++ b/docker/utils/add-new-auth-keys.sh @@ -14,20 +14,41 @@ # # Prerequisites: # - .env file with JWT_SECRET set (run generate-keys.sh first) -# - openssl -# - node >= 16 +# - node (>= 16) or docker # set -e -if ! command -v openssl >/dev/null 2>&1; then - echo "Error: openssl is required but not found." - exit 1 -fi +node_ok() { + command -v node >/dev/null 2>&1 || return 1 + major=$(node -v 2>/dev/null | sed 's/^v//' | cut -d. -f1) + [ -n "$major" ] && [ "$major" -ge 16 ] 2>/dev/null +} -if ! command -v node >/dev/null 2>&1; then - echo "Error: node (>= 16) is required but not found." - exit 1 +# Resolve how to run node: local install (>= 16) preferred, docker fallback. +if node_ok; then + node_runner="node" +else + if command -v node >/dev/null 2>&1; then + echo "Local node $(node -v) is too old (need >= 16), falling back to docker." + fi + + if ! command -v docker >/dev/null 2>&1; then + echo "Error: requires either node (>= 16) or docker." + exit 1 + fi + + if ! docker info >/dev/null 2>&1; then + echo "Error: docker is installed but the daemon is not running." + exit 1 + fi + + if ! docker image inspect node:22-alpine >/dev/null 2>&1; then + echo "Pulling node:22-alpine (first-run only)..." + docker pull node:22-alpine + fi + + node_runner="docker run --rm node:22-alpine node" fi # Read JWT_SECRET from .env @@ -36,7 +57,7 @@ if [ ! -f .env ]; then exit 1 fi -jwt_secret=$(grep '^JWT_SECRET=' .env | cut -d= -f2-) +jwt_secret=$(grep '^JWT_SECRET=' .env | cut -d= -f2- | tr -d '\r') if [ -z "$jwt_secret" ]; then echo "Error: JWT_SECRET not found in .env. Run generate-keys.sh first." exit 1 @@ -45,23 +66,18 @@ fi tmpdir=$(mktemp -d) trap 'rm -rf "$tmpdir"' EXIT -# Generate EC P-256 private key -openssl ecparam -name prime256v1 -genkey -noout -out "$tmpdir/ec_private.pem" 2>/dev/null - # Node.js does the crypto-heavy work: -# - PEM -> JWK conversion +# - EC P-256 keypair generation # - JWKS construction (with symmetric key included) # - ES256 JWT signing # - Opaque API key generation with checksum -node -e ' +$node_runner -e ' const crypto = require("crypto"); -const fs = require("fs"); -const pem = fs.readFileSync(process.argv[1]); -const jwtSecret = process.argv[2]; +const jwtSecret = process.argv[1]; -// EC key -> JWK -const privateKey = crypto.createPrivateKey(pem); +// Generate EC P-256 keypair and export as JWK +const { privateKey } = crypto.generateKeyPairSync("ec", { namedCurve: "P-256" }); const jwkPrivate = privateKey.export({ format: "jwk" }); const kid = crypto.randomUUID(); @@ -129,7 +145,7 @@ console.log("ANON_KEY_ASYMMETRIC=" + anonJwt); console.log("SERVICE_ROLE_KEY_ASYMMETRIC=" + serviceJwt); console.log("JWT_KEYS=" + JSON.stringify(jwksKeypair.keys)); console.log("JWT_JWKS=" + JSON.stringify(jwksPublic)); -' "$tmpdir/ec_private.pem" "$jwt_secret" > "$tmpdir/output" +' "$jwt_secret" > "$tmpdir/output" # Read generated values SUPABASE_PUBLISHABLE_KEY=$(grep '^SUPABASE_PUBLISHABLE_KEY=' "$tmpdir/output" | cut -d= -f2-) diff --git a/docker/utils/rotate-new-api-keys.sh b/docker/utils/rotate-new-api-keys.sh index 79fba406bc0..48fd9f02db8 100644 --- a/docker/utils/rotate-new-api-keys.sh +++ b/docker/utils/rotate-new-api-keys.sh @@ -12,14 +12,41 @@ # # Prerequisites: # - .env file (run generate-keys.sh and add-new-auth-keys.sh first) -# - node >= 16 +# - node (>= 16) or docker # set -e -if ! command -v node >/dev/null 2>&1; then - echo "Error: node (>= 16) is required but not found." - exit 1 +node_ok() { + command -v node >/dev/null 2>&1 || return 1 + major=$(node -v 2>/dev/null | sed 's/^v//' | cut -d. -f1) + [ -n "$major" ] && [ "$major" -ge 16 ] 2>/dev/null +} + +# Resolve how to run node: local install (>= 16) preferred, docker fallback. +if node_ok; then + node_runner="node" +else + if command -v node >/dev/null 2>&1; then + echo "Local node $(node -v) is too old (need >= 16), falling back to docker." + fi + + if ! command -v docker >/dev/null 2>&1; then + echo "Error: requires either node (>= 16) or docker." + exit 1 + fi + + if ! docker info >/dev/null 2>&1; then + echo "Error: docker is installed but the daemon is not running." + exit 1 + fi + + if ! docker image inspect node:22-alpine >/dev/null 2>&1; then + echo "Pulling node:22-alpine (first-run only)..." + docker pull node:22-alpine + fi + + node_runner="docker run --rm node:22-alpine node" fi if [ ! -f .env ]; then @@ -30,7 +57,7 @@ fi tmpdir=$(mktemp -d) trap 'rm -rf "$tmpdir"' EXIT -node -e ' +$node_runner -e ' const crypto = require("crypto"); const PROJECT_REF = "supabase-self-hosted";