Skip to content

feat(auth): register the Findability Explorer as an OIDC client - #111

Merged
ExtraToast merged 2 commits into
mainfrom
feat/findability-explorer-client
Oct 23, 2026
Merged

ExtraToast merged 2 commits into
mainfrom
feat/findability-explorer-client

Conversation

@ExtraToast

@ExtraToast ExtraToast commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

What

Registers findability-explorer as a confidential OIDC client that also requires PKCE, for the Findability Explorer at digital-strategy.jorisjonkers.dev. The tribelt shape.

  • New ServicePermission.FINDABILITY_EXPLORER("digital-strategy"). ID tokens carry SERVICE_FINDABILITY_EXPLORER for grantees, and for ADMIN as usual.
  • buildFindabilityExplorerClient() (its own file, as ServiceRegisteredClients.kt is at detekt's function limit): authorization_code + refresh_token, client_secret_basic/post, PKCE required, no consent, default token settings (15 min access, 7 day refresh, no reuse), scopes openid profile email. Redirect https://digital-strategy.jorisjonkers.dev/auth/callback and post-logout redirect https://digital-strategy.jorisjonkers.dev/, plus the .test variants.
  • "findability-explorer" to ServicePermission.FINDABILITY_EXPLORER in DOWNSTREAM_CLIENT_PERMISSIONS.
  • No fallback secret, the grimoire shape. The secret comes from auth.clients.findability-explorer.secret, supplied as AUTH_CLIENTS_FINDABILITY_EXPLORER_SECRET, and it has no default. findabilityExplorerClientOrNull() registers the client only for a real secret: not for a blank one, and not for the <no value> a Vault template renders while the key is unprovisioned. Until then there is no findability-explorer client at all, rather than one with a secret anybody could guess. The integration test base supplies a secret, as it does for grimoire. Local runs get the client only when the property is set. UNPROVISIONED_SECRET is now internal so both clients share it.
  • The secret is a @Value on the registeredClientRepository bean, next to grimoire's. The dash-dropping AUTH_CLIENTS_FINDABILITYEXPLORER_SECRET does not resolve the placeholder. I checked both forms against a SystemEnvironmentPropertySource.
  • deploy/auth.project.yml lists the new key under secret/data/auth-api.

Why this shape

The explorer runs its own OIDC flow, so the grant is enforced at the authorize endpoint. Only admins and holders of FINDABILITY_EXPLORER get a code. The app treats ROLE_ADMIN in the roles claim as its admin role. It keeps the refresh token and renews often to re-read roles, and every issuance reads the user's role and grants from the database, so a change shows up at the next refresh. offline_access is not a registered scope here: like tribelt, the client gets a refresh token because it holds the refresh_token grant.

Mapping the digital-strategy subdomain means forward-auth, wherever it fronts that host, now also requires FINDABILITY_EXPLORER (or ADMIN). Before this, an unlisted host let every signed-in user through.

No migration: user_service_permissions.service is a VARCHAR(50) without a check constraint (FINDABILITY_EXPLORER is 20 characters), and the OpenAPI spec does not enumerate permissions.

Deploy prerequisites

  • Vault key auth.clients.findability-explorer.secret in secret/data/auth-api.
  • fleet-infra adds the AUTH_CLIENTS_FINDABILITY_EXPLORER_SECRET template line. Until the key is provisioned, the client is simply not registered, so this can be released before or after the secret is minted.
  • Grants are made from home-portal's admin page, which needs the companion PR there to list the permission.

Verification

  • ./gradlew :api:detektMain :api:detektTest :api:detektIntegrationTest :api:ktlintCheck :api:test :api:integrationTest :api:jacocoTestCoverageVerification :api:exportOpenApiSpec: BUILD SUCCESSFUL. Unit tests 223/223, integration tests 257/257 (Testcontainers), coverage verification passed, no OpenAPI drift.
  • Unit: the client is registered for a real secret and not for a blank or <no value> one.
  • New integration tests run the real flow to a code (query-string authorize, as the estate-dashboard tests do). A grantee gets ROLE_USER and SERVICE_FINDABILITY_EXPLORER and a refresh token, then sees ROLE_ADMIN in the refreshed ID token after a promotion. An admin with no explicit grant signs in. The client is added to the deny, admin-allow and confidential-token-endpoint lists, and digital-strategy is added to the forward-auth deny list.

The Findability Explorer at digital-strategy.jorisjonkers.dev signs its
users in against this authorization server. Adds the registered client,
a FINDABILITY_EXPLORER service permission, and the client_id ->
permission mapping that gates the authorize endpoint -- the tribelt shape.

The client is confidential (client_secret_basic and client_secret_post)
and also requires PKCE, with authorization_code and refresh_token and the
default token settings. Redirect is /auth/callback, post-logout redirect
is the site root, with the .test variants. The secret comes from
auth.clients.findability-explorer.secret, supplied as
AUTH_CLIENTS_FINDABILITY_EXPLORER_SECRET, defaulting to
findability-explorer-secret for local runs and tests.

The explorer reads ROLE_ADMIN from the roles claim and renews its tokens
to pick up role changes; the integration tests run the real flow to a
code and show a promotion made after sign-in in the refreshed ID token.

No migration: FINDABILITY_EXPLORER fits user_service_permissions.service
VARCHAR(50), which has no check constraint.
@ExtraToast ExtraToast added type: feature New user-facing or operator-facing capability. area: auth Authentication, authorization, sessions, or identity. component: api HTTP API, OpenAPI contract, or API client concern. labels Oct 23, 2026
…supplied

The grimoire shape: auth.clients.findability-explorer.secret has no
default, and the client is not registered for a blank secret or the
<no value> a Vault template renders while the key is unprovisioned, so
production never carries a client with a guessable secret. The
integration test base supplies a secret, as it does for grimoire.
@ExtraToast
ExtraToast merged commit 9b7f07e into main Oct 23, 2026
9 checks passed
@ExtraToast
ExtraToast deleted the feat/findability-explorer-client branch October 23, 2026 10:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: auth Authentication, authorization, sessions, or identity. component: api HTTP API, OpenAPI contract, or API client concern. type: feature New user-facing or operator-facing capability.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant