feat(auth): decouple STS endpoint and make RFC 8693 token-type params configurable - #1181
feat(auth): decouple STS endpoint and make RFC 8693 token-type params configurable#1181zruchi wants to merge 2 commits into
Conversation
The rfc8693 exchanger previously hardcoded requested_token_type to access_token and read subject_token_type from a config field that defaulted to empty — sending an empty subject_token_type form value when unset, in violation of RFC 8693 section 2.1 which mandates the field. Add two new optional TOML keys, sts_subject_token_type and sts_requested_token_type, both defaulting to access_token (the canonical value for an inbound OAuth access token). Cross-realm deployments can override either to token-type:jwt when the STS expects a JWT subject or must mint a fresh signed JWT rather than echo the subject token shape. Defaults match the spec, so deployments that previously sent the correct values are unaffected. Deployments that previously sent an empty subject_token_type now send access_token — restoring spec compliance. Signed-off-by: Rowel Uchi <105178941+zruchi@users.noreply.github.com>
Token exchange previously derived its endpoint from the OIDC discovery document at authorization_url, conflating two distinct trust boundaries: who validates the user's bearer token and who mints the delegated cluster credential. Cross-realm RFC 8693 deployments need these to be independently configurable, and pure passthrough mode (skip_jwt_verification=true with no authorization_url) had no way to configure exchange at all because there was no provider to discover an endpoint from. Add an optional sts_token_url TOML key. The resolution rule is: explicit sts_token_url wins; otherwise fall back to the OIDC provider's discovered token endpoint. This keeps existing single-issuer deployments unchanged while letting operators point exchange at a separate STS gateway. A small consequence: SecurityTokenService.IsEnabled() now keys off TokenURL instead of Provider, since "is exchange usable" is correctly expressed as "do I have an endpoint to POST to," and this is what unblocks the passthrough+gateway combination. Add a two-server integration test (TestAuthorizationStsTokenURLDecouplesIssuerFromGateway) that wires an IDP to authorization_url and a separate STS gateway to sts_token_url, proves the IDP /token endpoint is never hit, and proves the backend receives the gateway-minted token. This is the regression test for the trust-boundary split. Signed-off-by: Rowel Uchi <105178941+zruchi@users.noreply.github.com>
|
/cc @Cali0707 |
|
We need one piece of this fix as well to properly integrate with Okta and use it as our AS. How can we move this forward? It appears this ticket and #1173 have overlap in parts of what they changed ( the sts_token_url and sts_subject_token_type part) Should we we break out to smaller updates instead of being grouped together in order to hopefully get them incrementally approved since each would be better scoped? |
|
make sense. Let me break this into two separate pr. |
|
Personally, I'd recommend perhaps start with one that is solely focused on fixing the §2.1 made subject_token_type REQUIRED; the global exchange path sends it empty. access_token is the correct identifier for the token this path actually carries (a validated OAuth access token from the Authorization header), with sts_subject_token_type to override when it isn't. I feel from a spec compliance standpoint, this is the most important to fix to ensure the REQUIRED field is set. The OPTIONAL one would be a bit less important IMHO, also it might require a little discussion because the requested_token_type comment in this PRs code says "Per RFC 8693 section 2.1 requested_token_type defaults to access_token when omitted." That one is actually wrong, no? — §2.1 says "If the requested type is unspecified, the issued token type is at the discretion of the authorization server." |
|
Hey yes, would be happy to review pieces of this incrementally. |
Summary
Two related changes to
pkg/tokenexchange/pkg/kubernetes, fully backwards-compatible:subject_token_typewas sent empty when unset (violates RFC 8693 section 2.1) andrequested_token_typewas hardcoded. Both are now configurable viasts_subject_token_type/sts_requested_token_type, defaulting toaccess_token.sts_token_urlto point token exchange at a separate STS gateway, decoupled from the IDP atauthorization_url. Unblocks cross-realm RFC 8693 andskip_jwt_verification=true+ token exchange (which had no provider to discover an endpoint from).New TOML keys
sts_subject_token_typeurn:ietf:params:oauth:token-type:access_tokensubject_token_typeform paramsts_requested_token_typeurn:ietf:params:oauth:token-type:access_tokenrequested_token_typeform paramsts_token_urlNotable behavior changes
SecurityTokenService.IsEnabled()now keys offTokenURL != ""instead ofProvider != nil— "is exchange usable?" is correctly "do I have an endpoint to POST to?". Required for the passthrough + gateway combo.resolveStsTokenURLhelper: explicitsts_token_urlwins, else OIDC-discovered endpoint.sts_token_urlvalidated ashttp/https, warns onhttp.Example: separate gateway from issuer