From 14385a740bd5955e5fc818515ad118fc8a3b7876 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 26 Aug 2026 16:02:52 +0200 Subject: [PATCH] feat: run local development against a mock identity provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dev environment disabled the main firewall outright, because AAK's OIDC setup has no redirect URI for itksites.local.itkdev.dk. That meant no OpenID Connect code path was exercised until it reached a server — a broken callback path, a renamed claim or a login loop all stayed invisible locally — and the setting is easy to forget, so an authentication problem could be debugged for a long time against a firewall that was never running. A mock provider gives the whole authorization code flow locally instead. The container name is the external hostname on purpose, so the browser and the application reach it by the same name and the issuer matches. .env.dev carries the settings, so an ordinary setup needs nothing in .env.local. AZURE_AZ_OIDC_ALLOW_HTTP is true there and must not be anywhere else: from itk-dev/openid-connect 5.1 it governs every endpoint the discovery document announces, not only the metadata URL. --- .env.dev | 19 +++++++++++++++++++ README.md | 29 ++++++++++++++++++++++++----- config/packages/security.yaml | 7 ------- docker-compose.override.yml | 31 +++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 .env.dev diff --git a/.env.dev b/.env.dev new file mode 100644 index 00000000..115e8ef6 --- /dev/null +++ b/.env.dev @@ -0,0 +1,19 @@ +# Committed defaults for the dev environment. +# +# Local development runs against the mock identity provider defined in +# docker-compose.override.yml, because the real one has no redirect URI +# registered for a developer machine. See README.md, "OpenID Connect". +# +# Override any of these in .env.local to point at a different provider. + +###> itk-dev/openid-connect-bundle ### +AZURE_AZ_OIDC_METADATA_URL=http://idp.itksites.local.itkdev.dk/.well-known/openid-configuration +# The mock accepts any client id and secret. +AZURE_AZ_OIDC_CLIENT_ID=client-id +AZURE_AZ_OIDC_CLIENT_SECRET=client-secret +AZURE_AZ_OIDC_REDIRECT_URI=https://itksites.local.itkdev.dk/openid-connect/generic +# The application reaches the mock over http inside the docker network. Never +# true anywhere else: since itk-dev/openid-connect 5.1 this governs every +# endpoint the discovery document announces, not only the metadata URL. +AZURE_AZ_OIDC_ALLOW_HTTP=true +###< itk-dev/openid-connect-bundle ### diff --git a/README.md b/README.md index 64b54161..5a50f68d 100644 --- a/README.md +++ b/README.md @@ -113,8 +113,22 @@ Then create a `.env.local` file to set secrets for your local setup. ### OpenID Connect -All users access is controlled by OpenID Connect. For local development you must -add the following to your `.env.local` file: +All user access is controlled by OpenID Connect. Locally the login runs against a +mock identity provider, defined as the `idp` service in `docker-compose.override.yml` +— the real provider has no redirect URI registered for a developer machine. + +Start it with the rest of the stack: + +```shell +docker compose up --detach +``` + +Then log in as `admin` or `editor`: the mock shows a form where you type the subject, +and hands back the claims for it. Both identities are defined in the compose file, and +their claims must include `name` and `upn`, which `AzureOIDCAuthenticator` reads. + +`.env.dev` carries the settings, so there is nothing to add to `.env.local` for an +ordinary setup. To develop against a real provider instead, override them there: ```dotenv ###> itk-dev/openid-connect-bundle ### @@ -122,13 +136,18 @@ AZURE_AZ_OIDC_METADATA_URL= AZURE_AZ_OIDC_CLIENT_ID= AZURE_AZ_OIDC_CLIENT_SECRET= AZURE_AZ_OIDC_REDIRECT_URI=https://itksites.local.itkdev.dk/openid-connect/generic +AZURE_AZ_OIDC_ALLOW_HTTP=false ###< itk-dev/openid-connect-bundle ### ``` > [!NOTE] -> In the `dev` environment the main firewall security is disabled -> (`security.yaml` → `when@dev`), so authentication is not required. -> This is because the current AAK OIDC setup doesn't support `itksites.local.itkdev.dk`. +> `AZURE_AZ_OIDC_ALLOW_HTTP=true` in `.env.dev` is what lets the application talk to +> the mock over http inside the docker network. It must never be true anywhere else: +> since `itk-dev/openid-connect` 5.1 it governs every endpoint the discovery document +> announces, not only the metadata URL. + +The mock accepts the PKCE challenge the bundle sends but does not verify it, so a +successful login here does not prove PKCE works against Azure. ### Fixtures diff --git a/config/packages/security.yaml b/config/packages/security.yaml index e89023b9..c730e4cc 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -58,13 +58,6 @@ security: - { path: ^/admin, roles: ROLE_ADMIN } # - { path: ^/profile, roles: ROLE_USER } -# Current AAK OIDC setup doesn't support `itksites.local.itkdev.dk` -when@dev: - security: - firewalls: - main: - security: false - when@test: security: password_hashers: diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 73f8b43f..be2dcc55 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -1,4 +1,35 @@ services: + # A mock identity provider, so the OpenID Connect login can be exercised locally. + # The real one has no redirect URI registered for a developer machine. + # + # https://github.com/geigerzaehler/oidc-provider-mock + # + # The container name is the external hostname on purpose: the browser and the + # application then reach the provider by the same name, so the issuer in the + # discovery document matches the one in the ID token. + # + # Claims must cover what AzureOIDCAuthenticator reads — `name` and `upn`. + idp: + image: ghcr.io/geigerzaehler/oidc-provider-mock:latest + container_name: idp.${COMPOSE_DOMAIN:?} + networks: + - app + - frontend + expose: + - "80" + labels: + - "traefik.enable=true" + - "traefik.docker.network=frontend" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}_idp.rule=Host(`idp.${COMPOSE_DOMAIN:?}`)" + - "traefik.http.services.${COMPOSE_PROJECT_NAME:?}_idp.loadbalancer.server.port=80" + command: + - "--port" + - "80" + - "--user-claims" + - '{"sub": "admin", "name": "Admin Jensen", "upn": "admin@example.org", "email": "admin@example.org"}' + - "--user-claims" + - '{"sub": "editor", "name": "Ed Editor", "upn": "editor@example.org", "email": "editor@example.org"}' + rabbit: image: rabbitmq:4-management-alpine networks: