PRD: Select the OIDC issuer with otomi.issuer
- Dominant language
- Go Template
- Stars
- 2.3k
- Forks
- 186
- Avg merge
- 3d 11h
- Merged PRs (30d)
- 66
Description
## Problem Statement
Dex can be installed but nothing uses it. Selecting it as the issuer is not simply a matter of changing a URL, because Keycloak and Dex differ in ways that reach into several consumers.
Their endpoint paths have nothing in common, and Dex has no realm. Keycloak attaches group membership through a protocol mapper whatever the client asks for, while Dex emits a claim only when its scope is requested — so a consumer that asks for too little authenticates successfully and receives no authorization at all. Keycloak accepts a logout redirect from the browser, while Dex requires the user's ID token, which a redirect cannot carry. And oauth2-proxy has a Keycloak-specific provider that is wrong for Dex.
Each of these fails quietly. None produces an error that names the cause.
## Solution
Make the issuer selectable with a single value — a new `otomi.issuer` field (`dex` | `keycloak`) — and confine the difference to the derived layer. This is deliberately not `apps.dex.enabled`/`apps.keycloak.enabled`: tying the issuer to whichever app is installed would rule out running both at once, which #3538 depends on.
Because the previous change moved every issuer-specific value behind derived values, selecting the issuer becomes a matter of computing those values differently. Consumers are not touched, with three exceptions where the difference is genuinely in the consumer: oauth2-proxy's provider type, its backchannel logout call, and its approval-prompt behaviour.
Logout is the one flow that changes shape. Dex refuses a post-logout redirect without an ID token hint, and a bare call only renders a confirmation page without ending the session. A browser redirect cannot supply the token, so logout is driven from oauth2-proxy's backchannel logout, which substitutes the token itself. The browser then goes straight to the console.
## Acceptance Criteria
1. **GIVEN** the issuer is Keycloak, **WHEN** the platform is rendered, **THEN** the output is identical to before this change.
2. **GIVEN** the issuer is Dex, **WHEN** the platform is rendered, **THEN** every consumer's issuer, endpoints, JWKS location, client identifier and client secret reference point at Dex, and no Keycloak realm path or endpoint path remains anywhere in the output.
3. **GIVEN** the issuer is Dex, **WHEN** any consumer requests scopes, **THEN** the request includes the scope that carries group membership.
4. **GIVEN** the issuer is Keycloak, **WHEN** any consumer requests scopes, **THEN** it does not request a scope the Keycloak realm does not define, since an unknown scope is rejected outright.
5. **GIVEN** the issuer is Dex, **WHEN** a user authenticates through oauth2-proxy, **THEN** the token carries the group claim and the platform API grants the role that membership implies.
6. **GIVEN** the issuer is Dex, **WHEN** a user logs out, **THEN** the session at the issuer is ended and the browser lands on the console without a confirmation step.
7. **GIVEN** the issuer is Dex, **WHEN** a user authenticates to a second application in the same browser session, **THEN** no second credential prompt appears.
8. **GIVEN** the issuer is Dex, **WHEN** a user authenticates, **THEN** no consent screen appears.
9. **GIVEN** the issuer is Dex, **WHEN** oauth2-proxy starts, **THEN** it waits for the issuer's discovery document rather than the issuer root, which Dex does not serve.
10. **GIVEN** either issuer, **WHEN** the platform is rendered, **THEN** template and values validation pass.
11. **GIVEN** `otomi.issuer` is set independently of `apps.dex.enabled`/`apps.keycloak.enabled`, **WHEN** the platform is rendered, **THEN** both apps can be enabled at once, with only `otomi.issuer` determining which one every consumer points at.
## Testing
- Render the full platform with each issuer selected and compare. The Keycloak render must be unchanged from the previous release; the Dex render must contain no Keycloak-shaped URL.
- Criteria 5 to 8 require a cluster and a real browser session, because they concern behaviour across redirects rather than rendered output.
- Criterion 7 is worth explicit attention. Single sign-on is not on by default in Dex; the setting that shares a session across clients defaults to off, and without it every application prompts again.
## Out of Scope
- Making Keycloak optional or changing which issuer is the default. Both issuers remain available and Keycloak remains the default until a later change.
- Migrating any existing cluster.
- The platform operators in `apl-tasks`, which take their configuration from ConfigMaps and are handled separately.
- Applications that hold accounts keyed on the token subject. Those are affected only when a cluster actually changes issuer, which this change does not do on its own.
## Further Notes
- The claim behaviour is worth stating precisely, because it caused two separate failures during investigation. Dex assigns each claim only inside the branch for its scope, so an unrequested claim is absent rather than empty. A consumer that reads it sees nothing and treats the user as unprivileged.
- oauth2-proxy sends a forced approval prompt by default. Keycloak ignores that parameter; Dex honours it, and it takes precedence over the setting that would otherwise skip the consent screen.
## Dependencies
Depends on:
- #3534 — the issuer switch is only small because every issuer-specific value already sits behind a derived value.
- #3536 — there is nothing to switch to until Dex is installable.
- #3541 — without it, making Dex the issuer locks out every user except the platform administrator.
Blocks #3538 and linode/apl-tasks#332.
Contributor guide
Research direction
Start with the derived issuer values and the consumer configurations described in the issue, then render the full platform with Keycloak and Dex selected. Compare the Keycloak output with the previous release and verify that the Dex output has correct endpoints, scopes, logout behavior, discovery readiness, and no Keycloak paths. Cluster and browser testing are required for authentication, logout, consent, and single sign-on criteria.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- kubernetes
- Domain
- authentication, infrastructure
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100