Skip to content

feat(oidc): an OIDCConfig without clientSecret is a public client - #104

Merged
braghettos merged 1 commit into
mainfrom
feat/oidc-public-client
Nov 7, 2026
Merged

braghettos merged 1 commit into
mainfrom
feat/oidc-public-client

Conversation

@braghettos

Copy link
Copy Markdown
Collaborator

Gate 1 signs each child portal in through the parent's Keycloak, and its rule is that no child stores a credential. So each child's Keycloak client is public, with PKCE. authn couldn't use a public client: the CRD required clientSecret, and the token exchange always sent client_secret, empty when the Secret had no value. Diego chose to make the secret optional rather than add a publicClient flag.

Behaviour

  • spec.clientSecret is optional, both in the CRD and in the authn-crds chart copy.
  • Omitted: the client is public. authn sends no client_secret, and a login without X-Code-Verifier is refused 400 MissingCodeVerifier before the IdP is called. This covers /oidc/login and /oidc/stepup.
  • Set: the Secret must hold a non-empty value under the key. Otherwise the login fails 500 ClientSecretLookupFailed; before, authn sent an empty secret. A broken reference never turns a confidential client into a public one.
  • Existing OIDCConfigs all name a secret, so their behaviour is unchanged, except a Secret with no value under the key now fails with that cause.

Tests

  • Route tests now send a PKCE verifier. Their fixture OIDCConfigs name no secret, which makes them public clients.
  • New public_test.go covers four things:
    • a public client without a verifier gets 400 and the IdP isn't called
    • client_secret is sent only by a confidential client
    • getConfig marks a missing clientSecret as public
    • a missing key or empty value fails ClientSecretLookupFailed

Docs

  • api.md: the CRD row, the route row, a new MissingCodeVerifier row, and updated ClientSecretLookupFailed and TokenExchangeFailed rows.
  • behavior.md: the field and the signature-check rationale. OIDC Core §3.1.3.7 rests on TLS to the token endpoint, not on the client secret.
  • log.md: a decision entry.

It's a feature, so it's intended as 0.36.0, and draft #91 moves to the next minor.

🤖 Generated with Claude Code

https://claude.ai/code/session_01N8HNbEHdUjLki5RpNhTsHR

Gate 1 signs child portals in through the parent's Keycloak with no
credential stored in the child, so each child's client is public, with
PKCE. The CRD required clientSecret and the token exchange always sent
client_secret, empty when the Secret had no value.

clientSecret is now optional. Omitted, authn sends no client_secret and
refuses a login without a PKCE verifier (400 MissingCodeVerifier) before
calling the IdP. A named Secret with no value under its key fails 500
ClientSecretLookupFailed instead of sending an empty secret, so a broken
reference never makes a confidential client public.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8HNbEHdUjLki5RpNhTsHR
@braghettos
braghettos merged commit 97168ac into main Nov 7, 2026
17 checks passed
@braghettos
braghettos deleted the feat/oidc-public-client branch November 7, 2026 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant