From a8954f525c8afbbeee83616e4af94b64613fd629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4rlocher?= Date: Tue, 2 Jun 2026 17:05:44 +0200 Subject: [PATCH] fix(opnform): align FRONT_API_SECRET across api and ui SSR path The api service now also receives FRONT_API_SECRET so AuthenticateJWT accepts the UI's server-side JWT forwards instead of blacklisting them on UA mismatch. On the ui service the var is renamed FRONT_API_SECRET -> NUXT_API_SECRET so Nuxt's runtimeConfig.apiSecret is actually populated (NUXT_ convention) and injected as x-api-secret, short-circuiting the UA-fingerprint check that otherwise 401s every reload. --- roles/opnform/templates/docker-compose.yml.j2 | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/roles/opnform/templates/docker-compose.yml.j2 b/roles/opnform/templates/docker-compose.yml.j2 index 6b5866c..24ef00c 100644 --- a/roles/opnform/templates/docker-compose.yml.j2 +++ b/roles/opnform/templates/docker-compose.yml.j2 @@ -60,6 +60,14 @@ services: JWT_TTL: "1440" JWT_SECRET: "{{ opnform_jwt_secret }}" + # Shared secret for trusted SSR requests from the Nuxt UI. The UI + # forwards JWTs server-side with its own user agent; without this + # secret the API's AuthenticateJWT middleware would reject those + # requests (UA mismatch -> token blacklisted -> the next genuine + # browser request 401s). Must match FRONT_API_SECRET on the ui + # service. + FRONT_API_SECRET: "{{ opnform_front_api_secret }}" + PHP_MEMORY_LIMIT: "{{ opnform_php_memory_limit }}" PHP_MAX_EXECUTION_TIME: "{{ opnform_php_max_execution_time }}" PHP_UPLOAD_MAX_FILESIZE: "{{ opnform_php_upload_max_filesize }}" @@ -114,7 +122,13 @@ services: NUXT_PUBLIC_API_BASE: "/api" NUXT_PRIVATE_API_BASE: "http://ingress/api" NUXT_PUBLIC_ENV: production - FRONT_API_SECRET: "{{ opnform_front_api_secret }}" + # Nuxt runtimeConfig.apiSecret is fed by NUXT_API_SECRET (Nuxt + # convention: NUXT_ populates runtimeConfig.). The UI + # injects this as `x-api-secret` on SSR-side forwards to Laravel, + # which then short-circuits the UA-fingerprint check in + # AuthenticateJWT — without it every reload would invalidate the + # JWT (UA `node` vs UA at issue time) and 401. + NUXT_API_SECRET: "{{ opnform_front_api_secret }}" depends_on: api: condition: service_healthy