Repository navigation
Add configured delegate client metadata - #6952
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6952 +/- ##
==========================================
+ Coverage 80.19% 80.23% +0.04%
==========================================
Files 846 847 +1
Lines 85962 86097 +135
==========================================
+ Hits 68933 69080 +147
+ Misses 17024 17012 -12
Partials 5 5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
c9c6017 to
01b420a
Compare
ChrisJBurns
left a comment
There was a problem hiding this comment.
-
high · security — private_key_jwt-only delegate clients now allow a non-loopback cleartext HTTP issuer (
pkg/authserver/config.go,pkg/authserver/server/provider.go)No secret is sent, but client assertions, auth codes (now that authorization_code is allowed) and issued tokens still cross cleartext HTTP. Captured assertions can be replayed until they expire. The CRD path still requires HTTPS, so the two paths now disagree.
-
medium · correctness — Auth method fingerprint is read from the getter for live clients but the raw field for Redis rows (
pkg/authserver/storage/redis.go,pkg/authserver/storage/types.go)fingerprintOfClient calls GetTokenEndpointAuthMethod(), and fosite's DefaultOpenIDConnectClient may return client_secret_basic when the field is empty. Redis storedClient uses the raw field. Any OIDC-typed row with an empty method could then show a spurious policy change or reserved-row conflict.
tgrunnagle
left a comment
There was a problem hiding this comment.
I went through the validator matrix, and coverage is thorough: every combination of auth method, secret and key state; key contents (private, symmetric, mismatched type); grant rules; redirect rules; transport at the RunConfig, Config and direct-params layers; and reconcile warnings on both backends.
@ChrisJBurns I looked into both of your findings. My notes are inline on config.go:2289 and types.go:1258. In short, the first is a fair policy question that only bites once the CRD gains an auth-method field, and the second doesn't hold for fosite v0.49.0.
Test gaps worth filling: a test that pins GetTokenEndpointAuthMethod() == "" for an empty-method OIDC client, and a reconcile test for a redirect URI change (see the policyChanged comment).
01b420a to
d0aa163
Compare
|
Thanks for both of these. On the cleartext HTTP one, you were right, and I went back to strict: every delegate client counts as confidential again, private_key_jwt ones included. The replay risk is smaller than it sounds, since jtis are reserved atomically and assertions are capped at 5 minutes. But a delegate client can hold authorization_code and refresh_token, while a DCR private_key_jwt client is token-exchange-only. So codes and refresh tokens would have crossed plain HTTP, and that's not where I want the default to be. It also keeps the RunConfig path matching the CRD rules. There's a comment at the call site explaining why, so it can be relaxed later if we do need an in-cluster HTTP issuer. The errors also now say "allow_confidential_client_registration or delegate clients", so a key-only config doesn't get told it's sending secrets. The fingerprint one doesn't hold for fosite v0.49.0. |
d0aa163 to
018aae1
Compare
Delegate clients could only be secret-authenticated, token-exchange-only clients. Issue #6893 needs a pre-provisioned broker that keeps its client_id across key rotation, so the config now accepts an auth method, a JWKS file, grant types and redirect URIs. One validator in the registration package checks these fields and shares the DCR private_key_jwt key check. It runs on RunConfig (keys not yet loaded), on the resolved Config (keys loaded) before any client is registered, and when the client is built. Redirect URIs follow the confidential-client rule: https, not loopback. refresh_token requires authorization_code, as in DCR. The constructor keeps today's bare client for default inputs and never marks the result as DCR-issued. Every delegate client still counts as confidential for the cleartext-transport check, private_key_jwt ones included. They send no reusable secret, but unlike token-exchange-only DCR private_key_jwt clients they can hold authorization_code and refresh_token, so codes and tokens would cross plain HTTP. This matches the CRD rules and can be relaxed later if an in-cluster HTTP issuer is needed. The transport errors now name both triggers instead of only confidential registration. Replacing a configured client now also logs the WARN from #6918 when its grant types, redirect URIs or auth method change. A JWKS change is key rotation and stays silent. The runner does not load the JWKS file or pass the new fields yet; the next change in the stack wires them, and the two are meant to land together. Co-authored-by: Mecatl <noreply@mecatl.dev>
018aae1 to
fb1e6a6
Compare
Summary
client_idacross key rotation, so the configuration has to accept an auth method, a JWKS file, grant types and redirect URIs.delegate_clients[]acceptstoken_endpoint_auth_method,token_endpoint_auth_signing_alg,jwks_file,grant_typesandredirect_uris. All are optional.registrationpackage checks them and shares the DCRprivate_key_jwtkey check. It runs on RunConfig (keys not loaded yet), on the resolved Config (keys loaded) before any client is registered, and when the client is built.refresh_tokenrequiresauthorization_code, as in DCR.private_key_jwtones included (see the notes below). The transport errors now name both triggers (confidential registration or delegate clients) instead of only confidential registration.Refs #6893
Type of change
Test plan
task test) — run on the packages this PR touches, not the whole repository (see below)task lint-fix) —golangci-linton the same packages, no findingsTests, lint and vet were run on the touched packages at the tip of the stack, which includes this PR. The generated docs and SDK files were regenerated and
task docs-verifyreports no drift. The validator has table-driven tests for each rule on the RunConfig, resolved Config and constructor paths. After review,go test ./pkg/authserver/...and the operator unit tests were rerun on this commit.Does this introduce a user-facing change?
Yes, to the RunConfig schema and to two error and log messages.
private_key_jwtwith a client secret, orrefresh_tokenwithoutauthorization_code) now fail validation at startup.grant_types,redirect_urisand an auth method pin are dropped (the client stays token-exchange-only), and aprivate_key_jwtclient fails at startup withresolved client secret is empty. No configuration ends up with more access than it asked for. The operator CRD does not expose these fields.confidential clients (allow_confidential_client_registration or delegate clients) ...instead ofallow_confidential_client_registration ....old_/new_grant_types,old_/new_token_endpoint_auth_methodandold_/new_redirect_uris.Special notes for reviewers
private_key_jwtdelegates. These clients send no reusable secret, but they still count as confidential. Unlike DCRprivate_key_jwtclients, which can only use token exchange, a delegate client can holdauthorization_codeandrefresh_token, so codes and tokens would cross plain HTTP.jtireplay checks and the 5-minute assertion cap already limit what a captured assertion is worth, so this is a conservative choice, not a known hole. It also matches the CRD CEL rules. It can be relaxed later if an in-cluster HTTP issuer is needed.registration.NewStaticDelegateClientnow returnsfosite.Clientinstead of*fosite.DefaultClient, because it returns aDefaultOpenIDConnectClientwhen an auth method is pinned. This is intended. Code that used the concrete type has to change; nothing in Stacklok Enterprise Platform calls it.🤖 Generated with Claude Code