From 1cb1c23ffe00564db364f07ae495ef35bb6af627 Mon Sep 17 00:00:00 2001 From: Manuel Raynaud Date: Tue, 4 Aug 2026 16:33:41 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7(dev)=20generate=20the=20JWT=20sign?= =?UTF-8?q?ing=20key=20when=20bootstrapping=20the=20dev=20stack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thw private key needed to generate a jwt token will be mandatory. In order to ease the development we want to automate its generation --- CHANGELOG.md | 1 + Makefile | 9 ++++++++- bin/_config.sh | 4 ++++ bin/generate-jwt-private-key.sh | 23 +++++++++++++++++++++++ compose.yml | 2 ++ env.d/development/common | 5 +++++ 6 files changed, 43 insertions(+), 1 deletion(-) create mode 100755 bin/generate-jwt-private-key.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 28dfa185a..7b3c6e939 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to - ✨(backend) add a service generating cached RS256 JWT tokens - ✨(backend) publish the JWT public key on a JWKS endpoint +- 🔧(dev) generate the JWT signing key when bootstrapping the dev stack - ♿️(frontend) restore skip to content link after header redesign #2510 - 🌐(i18n) rename cn_CN to zh_CN, add eo_PL and zh_TW locales #2486 - ✨(backend) conditional email notification in server to server api #2554 diff --git a/Makefile b/Makefile index fcac8eff2..6f0a033b2 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,11 @@ data/media: data/static: @mkdir -p data/static +# RSA key signing the JWT tokens the backend issues. Generated locally, never +# committed: "data/" is gitignored. Regenerate it by deleting the file. +data/jwt/private.pem: + @bin/generate-jwt-private-key.sh + # -- Project create-env-local-files: ## create env.local files in env.d/development @@ -81,7 +86,8 @@ create-env-local-files: .PHONY: create-env-local-files generate-secret-keys: -generate-secret-keys: ## generate secret keys to be stored in common.local +generate-secret-keys: ## generate the secret keys needed by the dev stack +generate-secret-keys: data/jwt/private.pem @bin/generate-oidc-store-refresh-token-key.sh .PHONY: generate-secret-keys @@ -237,6 +243,7 @@ logs: ## display app-dev logs (follow mode) run-backend: ## Start only the backend application and all needed services @$(MAKE) create-docker-network + @$(MAKE) data/jwt/private.pem @$(COMPOSE) up --force-recreate -d docspec @$(COMPOSE) up --force-recreate -d celery-dev @$(COMPOSE) up --force-recreate -d y-provider-development-converter diff --git a/bin/_config.sh b/bin/_config.sh index b317352f5..376de65ec 100644 --- a/bin/_config.sh +++ b/bin/_config.sh @@ -38,6 +38,10 @@ function _set_user() { # options: docker compose command options # ARGS : docker compose command arguments function _docker_compose() { + # The backend settings point at this key and the containers mount it, so it + # has to exist before any of them starts. + "${REPO_DIR}/bin/generate-jwt-private-key.sh" + # Set DOCKER_USER for Windows compatibility with MinIO if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || -n "${WSL_DISTRO_NAME:-}" ]]; then export DOCKER_USER="0:0" diff --git a/bin/generate-jwt-private-key.sh b/bin/generate-jwt-private-key.sh new file mode 100755 index 000000000..3d2963bd0 --- /dev/null +++ b/bin/generate-jwt-private-key.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# Generate the RSA private key signing the JWT tokens issued by the backend. +# +# Development only. The key is generated locally and never committed: it lands +# in "data/", which is gitignored. The dev stack mounts it in the backend +# containers, where JWT_PRIVATE_KEY_FILE points at it. +# +# Idempotent: an existing key is kept. Delete the file to roll the key. + +set -eo pipefail + +REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +KEY_PATH="${REPO_DIR}/data/jwt/private.pem" + +if [ -f "${KEY_PATH}" ]; then + exit 0 +fi + +mkdir -p "$(dirname "${KEY_PATH}")" +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "${KEY_PATH}" 2>/dev/null +chmod 600 "${KEY_PATH}" +echo "✓ JWT private key generated in ${KEY_PATH}" diff --git a/compose.yml b/compose.yml index b8ac0bf47..7f7b95f8b 100644 --- a/compose.yml +++ b/compose.yml @@ -82,6 +82,7 @@ services: volumes: - ./src/backend:/app - ./data/static:/data/static + - ./data/jwt:/data/jwt:ro - /app/.venv depends_on: postgresql: @@ -111,6 +112,7 @@ services: volumes: - ./src/backend:/app - ./data/static:/data/static + - ./data/jwt:/data/jwt:ro - /app/.venv depends_on: - app-dev diff --git a/env.d/development/common b/env.d/development/common index ef3a8010b..c1a438c74 100644 --- a/env.d/development/common +++ b/env.d/development/common @@ -22,6 +22,11 @@ DJANGO_EMAIL_LOGO_IMG="http://localhost:3000/assets/logo-suite-numerique.png" DJANGO_EMAIL_PORT=1025 DJANGO_EMAIL_URL_APP="http://localhost:3000" +# JWT +# The key itself is generated locally by "make generate-secret-keys", it is +# never committed. A PEM does not fit in an env var, hence the _FILE variant. +JWT_PRIVATE_KEY_FILE=/data/jwt/private.pem + # Backend url IMPRESS_BASE_URL="http://localhost:8072"