feat(spa): opt-in 'https' nx serve config for dev-server TLS (#263)
## Summary
Add an opt-in `https` configuration to the SPA dev-servers so the ADR-0030 dockerised dev mode can be reached over a hostname registered in Entra. Entra refuses `http:` redirect URIs for anything other than `localhost`, which made the hostname-based access pattern (`apf-portal.dev-jg.local`, `apf-portal.dev.local`, …) — the only stable way to share a VM-based dev stack with another developer — impossible to wire to OIDC. This PR closes that gap without touching the WSL-native + localhost flow.
## What lands
| File | Change |
| --- | --- |
| `apps/portal-shell/project.json`, `apps/portal-admin/project.json` | New `https` Nx serve configuration: inherits the `development` build, sets `ssl: true` + `sslKey: .secrets/dev-tls.key` + `sslCert: .secrets/dev-tls.pem`. `defaultConfiguration` stays `development`; the `https` config is purely opt-in. |
| `infra/local/dev.compose.yml` | The `portal-shell` and `portal-admin` commands now end with `--configuration=${NX_SERVE_CONFIGURATION:-development}`. Compose interpolates the value at YAML parse time from `infra/local/.env`. Default is `development` (no SSL), so behaviour is unchanged for anyone who doesn't opt in. |
| `infra/local/.env.example` | New commented-out `NX_SERVE_CONFIGURATION=https` block with the rationale + pointer to the mkcert setup. |
| `apps/portal-bff/.env.example` | Comment block above the `ENTRA_*_REDIRECT_URI` defaults shows the HTTPS hostname-based override pattern (the four URIs that go with the `apps` profile when accessing via a hostname) and reminds that each override must be registered Entra-side. |
| `infra/README.md` | New "HTTPS dev-server setup — remote-browser access via a hostname" subsection: mkcert install / `mkcert -install`, cert generation, `.secrets/dev-tls.{key,pem}` convention, Entra registration reminder, `NX_SERVE_CONFIGURATION=https` opt-in. Notes that WSL-native is unaffected and that the cert path stays the same when the corp CA eventually replaces mkcert. |
## Design notes
- **Convention `.secrets/dev-tls.{key,pem}` at repo root.** Matches the existing `apps/portal-bff/.secrets/jwks.pem` pattern (gitignored via `*.pem` + `*.key`). Workspace-relative path means project.json can hardcode it and each dev drops their per-host cert there.
- **Hardcoded path, per-dev cert content.** Each developer generates a cert for **their own** hostname; the cert sits at the same fixed path on every machine. Nothing dev-specific in `project.json`.
- **`https` is opt-in, not default.** Native `nx serve` keeps booting on HTTP (`localhost:4200`) without SSL key files, exactly as before. Compose default is also `development` — only setting `NX_SERVE_CONFIGURATION=https` in `infra/local/.env` switches it on, gated by the dev having actually run the mkcert step.
- **BFF stays plain HTTP.** Only the SPA dev-server terminates TLS — the proxy then hits `http://portal-bff:3000` on the internal Compose network. Entra still gets HTTPS at the browser-facing origin, which is what its policy enforces.
## What this PR deliberately does NOT do
- It does **not** force-enable HTTPS. Devs who don't care about hostname access continue working as before.
- It does **not** touch the BFF code, the Entra config helpers, or the auth flow itself. The whole change is config (project.json + compose + env-examples) + docs.
- It does **not** ship the shared VM cert story. That needs a corp-CA-signed cert (or a shared mkcert CA distributed across workstations); flagged in the README section as a follow-up.
## Test plan
- [x] Both `project.json` files parse as JSON; `nx show project` exposes the new `https` configuration with the expected SSL options.
- [x] `docker compose -f dev.compose.yml --profile apps config` validates with `NX_SERVE_CONFIGURATION=https` (resolves to `--configuration=https`) and without it (resolves to `--configuration=development`).
- [ ] **On vm-jg**: `mkcert -install` on the workstation, `mkcert` against `apf-portal.dev-jg.local`, copy `.secrets/dev-tls.{key,pem}` to the VM, set `NX_SERVE_CONFIGURATION=https` in `infra/local/.env`, register the four `https://apf-portal.dev-jg.local:*` URIs in Entra, restart `dev.sh up apps`, then open `https://apf-portal.dev-jg.local:4200/` from the workstation → SPA loads, no cert warning, sign-in completes and returns to the SPA via the OIDC callback.
- [ ] Native WSL flow unchanged: `nx serve portal-shell` still boots on `http://localhost:4200/`, OIDC against the existing `http://localhost:3000/...` Entra URIs still works.
## Related
- [ADR-0030](docs/decisions/0030-dockerised-dev-mode.md) — the dockerised dev mode this completes for the hostname-access case.
- [ADR-0018](docs/decisions/0018-environment-configuration-strategy.md) — per-environment SPA config strategy; `https` is a new Nx serve _configuration_, not a new `environment.ts` sibling, so 0018's build-time replacement story is unchanged.
- [ADR-0009](docs/decisions/0009-auth-flow-oidc-pkce-msal-node.md) — OIDC flow; no behaviour change, only the redirect-URI strings.
---------
Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #263
This commit was merged in pull request #263.
This commit is contained in:
@@ -93,6 +93,12 @@
|
|||||||
},
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"buildTarget": "portal-admin:build:development"
|
"buildTarget": "portal-admin:build:development"
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"buildTarget": "portal-admin:build:development",
|
||||||
|
"ssl": true,
|
||||||
|
"sslKey": ".secrets/dev-tls.key",
|
||||||
|
"sslCert": ".secrets/dev-tls.pem"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "development"
|
"defaultConfiguration": "development"
|
||||||
|
|||||||
@@ -60,6 +60,25 @@ ENTRA_CLIENT_SECRET=replace_with_real_value
|
|||||||
# User portal — `/api/auth/callback` is the OIDC return URL; the
|
# User portal — `/api/auth/callback` is the OIDC return URL; the
|
||||||
# post-logout URL is where Entra sends the browser after RP-initiated
|
# post-logout URL is where Entra sends the browser after RP-initiated
|
||||||
# logout (typically the SPA landing page).
|
# logout (typically the SPA landing page).
|
||||||
|
#
|
||||||
|
# The default values below match WSL-native dev (browser on the same
|
||||||
|
# host as the BFF, accessing http://localhost:4200/). For the
|
||||||
|
# ADR-0030 dockerised dev mode accessed via a hostname (e.g.
|
||||||
|
# https://apf-portal.dev-jg.local:4200/), override these to point at
|
||||||
|
# the SPA dev-server origin instead — `/api/auth/callback` is then
|
||||||
|
# proxied to the BFF (see apps/portal-shell/proxy.conf.js):
|
||||||
|
#
|
||||||
|
# ENTRA_REDIRECT_URI=https://apf-portal.dev-jg.local:4200/api/auth/callback
|
||||||
|
# ENTRA_POST_LOGOUT_REDIRECT_URI=https://apf-portal.dev-jg.local:4200/
|
||||||
|
# ENTRA_ADMIN_REDIRECT_URI=https://apf-portal.dev-jg.local:4300/api/admin/auth/callback
|
||||||
|
# ENTRA_ADMIN_POST_LOGOUT_REDIRECT_URI=https://apf-portal.dev-jg.local:4300/
|
||||||
|
#
|
||||||
|
# The `https:` scheme is mandatory for non-`localhost` hosts — Entra
|
||||||
|
# rejects `http:` for anything else. See the "HTTPS dev-server setup"
|
||||||
|
# section of infra/README.md for the dev-server TLS / mkcert setup.
|
||||||
|
# Each override URI must also be registered in the Entra app
|
||||||
|
# registration's Redirect URIs list (the BFF sends *one* of them per
|
||||||
|
# auth request; Entra validates it is allowed).
|
||||||
ENTRA_REDIRECT_URI=http://localhost:3000/api/auth/callback
|
ENTRA_REDIRECT_URI=http://localhost:3000/api/auth/callback
|
||||||
ENTRA_POST_LOGOUT_REDIRECT_URI=http://localhost:4200/
|
ENTRA_POST_LOGOUT_REDIRECT_URI=http://localhost:4200/
|
||||||
# Admin portal — distinct callback per ADR-0020 §"Sessions — distinct
|
# Admin portal — distinct callback per ADR-0020 §"Sessions — distinct
|
||||||
|
|||||||
@@ -92,6 +92,12 @@
|
|||||||
},
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"buildTarget": "portal-shell:build:development"
|
"buildTarget": "portal-shell:build:development"
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"buildTarget": "portal-shell:build:development",
|
||||||
|
"ssl": true,
|
||||||
|
"sslKey": ".secrets/dev-tls.key",
|
||||||
|
"sslCert": ".secrets/dev-tls.pem"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "development"
|
"defaultConfiguration": "development"
|
||||||
|
|||||||
@@ -226,6 +226,50 @@ How it works (see [ADR-0030](../docs/decisions/0030-dockerised-dev-mode.md)):
|
|||||||
|
|
||||||
The three dev modes (native `nx serve`, devcontainer, this `apps` profile) and when to use each are summarised in [docs/setup/01-dev-debian-vm-setup.md](../docs/setup/01-dev-debian-vm-setup.md).
|
The three dev modes (native `nx serve`, devcontainer, this `apps` profile) and when to use each are summarised in [docs/setup/01-dev-debian-vm-setup.md](../docs/setup/01-dev-debian-vm-setup.md).
|
||||||
|
|
||||||
|
### HTTPS dev-server setup — remote-browser access via a hostname
|
||||||
|
|
||||||
|
By default the dev-servers serve plain HTTP — fine when the browser is on the same host as the BFF (`http://localhost:4200/`), which is also the only HTTP origin Entra accepts as a redirect URI. The moment you access the SPA over a **hostname** (e.g. `apf-portal.dev.local`, useful when the browser sits on a workstation and the stack runs on a shared / per-dev VM), Entra refuses the `http:` redirect URI and the dev-servers must terminate TLS.
|
||||||
|
|
||||||
|
The setup is one-time per dev:
|
||||||
|
|
||||||
|
1. **Install [mkcert](https://github.com/FiloSottile/mkcert)** on your workstation (the machine where the browser runs) and bootstrap its local CA:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Debian / WSL Ubuntu:
|
||||||
|
sudo apt install -y libnss3-tools
|
||||||
|
# macOS:
|
||||||
|
# brew install mkcert nss
|
||||||
|
# Windows (PowerShell, choco):
|
||||||
|
# choco install mkcert
|
||||||
|
mkcert -install
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Generate the cert** for the hostname you registered in your `/etc/hosts` and in Entra. From the repo root on your workstation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p .secrets
|
||||||
|
mkcert -key-file .secrets/dev-tls.key -cert-file .secrets/dev-tls.pem \
|
||||||
|
apf-portal.dev-jg.local # ← replace with YOUR hostname
|
||||||
|
```
|
||||||
|
|
||||||
|
`.secrets/` is git-ignored; the bind-mount in the `apps` profile (the repo at `/workspace`) makes the files visible inside the containers at the path the `https` configuration expects.
|
||||||
|
|
||||||
|
3. **Update `apps/portal-bff/.env`** so the BFF tells Entra the matching HTTPS URIs — see the redirect-URI block in [`apps/portal-bff/.env.example`](../apps/portal-bff/.env.example) for the override pattern. The same URIs must be registered in your Entra app registration's "Redirect URIs" list (the BFF only sends one of them per auth request; Entra validates it is on the list).
|
||||||
|
|
||||||
|
4. **Enable the `https` Nx serve configuration** for the compose dev-servers by adding to `infra/local/.env`:
|
||||||
|
|
||||||
|
```env
|
||||||
|
NX_SERVE_CONFIGURATION=https
|
||||||
|
```
|
||||||
|
|
||||||
|
The compose command resolves `--configuration=${NX_SERVE_CONFIGURATION:-development}` at parse time, so the SPAs pick up the `https` config defined in `apps/portal-shell/project.json` and `apps/portal-admin/project.json`. The BFF stays HTTP behind the proxy — only the public origin is HTTPS.
|
||||||
|
|
||||||
|
5. `./infra/local/dev.sh up apps` → browser opens `https://apf-portal.dev-jg.local:4200/`. No cert warning (mkcert's CA is trusted by the workstation after step 1).
|
||||||
|
|
||||||
|
Native `nx serve` (WSL / localhost) is **unaffected** — it keeps using the `development` configuration by default, no SSL required, and the `localhost` URIs registered in Entra still work.
|
||||||
|
|
||||||
|
When real DNS + corp-CA-signed certs arrive, the hostname can be reused as-is (Entra registrations are literal strings — they don't care who signs the cert). Drop the cert files back into `.secrets/` and remove the mkcert step.
|
||||||
|
|
||||||
### Service endpoints (defaults)
|
### Service endpoints (defaults)
|
||||||
|
|
||||||
| Service | Host port | Purpose |
|
| Service | Host port | Purpose |
|
||||||
|
|||||||
@@ -35,3 +35,20 @@ OTEL_HTTP_PORT=4318
|
|||||||
PGWEB_PORT=8081
|
PGWEB_PORT=8081
|
||||||
JAEGER_UI_PORT=16686
|
JAEGER_UI_PORT=16686
|
||||||
SERVE_STATIC_PORT=4200
|
SERVE_STATIC_PORT=4200
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- Apps (`--profile apps`)
|
||||||
|
# Nx serve configuration for the SPA dev-servers. Two values matter:
|
||||||
|
# - `development` (default) — plain HTTP. Use with `localhost`
|
||||||
|
# browser access; Entra accepts
|
||||||
|
# `http://localhost:*` redirect URIs.
|
||||||
|
# - `https` — TLS-terminating dev-server using the
|
||||||
|
# cert at `.secrets/dev-tls.{key,pem}`.
|
||||||
|
# Required when accessing the SPA via a
|
||||||
|
# hostname (e.g. `apf-portal.dev.local`)
|
||||||
|
# since Entra rejects `http:` for any
|
||||||
|
# non-`localhost` redirect URI. See the
|
||||||
|
# "HTTPS dev-server setup" section of
|
||||||
|
# infra/README.md for the mkcert
|
||||||
|
# procedure and how to wire the
|
||||||
|
# matching `ENTRA_REDIRECT_URI` value.
|
||||||
|
# NX_SERVE_CONFIGURATION=https
|
||||||
|
|||||||
@@ -266,7 +266,27 @@ services:
|
|||||||
# /api proxy at the BFF container by name (Compose DNS). Native
|
# /api proxy at the BFF container by name (Compose DNS). Native
|
||||||
# `nx serve` leaves BFF_TARGET unset and falls back to localhost.
|
# `nx serve` leaves BFF_TARGET unset and falls back to localhost.
|
||||||
BFF_TARGET: http://portal-bff:3000
|
BFF_TARGET: http://portal-bff:3000
|
||||||
command: ['pnpm', 'exec', 'nx', 'serve', 'portal-shell', '--host', '0.0.0.0', '--port', '4200']
|
# `--configuration=${NX_SERVE_CONFIGURATION:-development}` is
|
||||||
|
# interpolated by Compose at YAML parse time from
|
||||||
|
# `infra/local/.env`. Set `NX_SERVE_CONFIGURATION=https` there to
|
||||||
|
# serve over TLS — required when accessing via a hostname (Entra
|
||||||
|
# rejects `http:` for non-`localhost` redirect URIs). See the
|
||||||
|
# `https` configuration in apps/portal-shell/project.json + the
|
||||||
|
# "HTTPS dev-server setup" section in infra/README.md for the
|
||||||
|
# mkcert procedure.
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
'pnpm',
|
||||||
|
'exec',
|
||||||
|
'nx',
|
||||||
|
'serve',
|
||||||
|
'portal-shell',
|
||||||
|
'--host',
|
||||||
|
'0.0.0.0',
|
||||||
|
'--port',
|
||||||
|
'4200',
|
||||||
|
'--configuration=${NX_SERVE_CONFIGURATION:-development}',
|
||||||
|
]
|
||||||
ports:
|
ports:
|
||||||
- '${SHELL_PORT:-4200}:4200'
|
- '${SHELL_PORT:-4200}:4200'
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -279,7 +299,20 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
# See portal-shell — same proxy target for the admin SPA.
|
# See portal-shell — same proxy target for the admin SPA.
|
||||||
BFF_TARGET: http://portal-bff:3000
|
BFF_TARGET: http://portal-bff:3000
|
||||||
command: ['pnpm', 'exec', 'nx', 'serve', 'portal-admin', '--host', '0.0.0.0', '--port', '4300']
|
# See portal-shell for the NX_SERVE_CONFIGURATION rationale.
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
'pnpm',
|
||||||
|
'exec',
|
||||||
|
'nx',
|
||||||
|
'serve',
|
||||||
|
'portal-admin',
|
||||||
|
'--host',
|
||||||
|
'0.0.0.0',
|
||||||
|
'--port',
|
||||||
|
'4300',
|
||||||
|
'--configuration=${NX_SERVE_CONFIGURATION:-development}',
|
||||||
|
]
|
||||||
ports:
|
ports:
|
||||||
- '${ADMIN_PORT:-4300}:4300'
|
- '${ADMIN_PORT:-4300}:4300'
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
Reference in New Issue
Block a user