Skip to content

feat(auth): decouple STS endpoint and make RFC 8693 token-type params configurable - #1181

Open
zruchi wants to merge 2 commits into
containers:mainfrom
zruchi:feat/sts-configurability
Open

feat(auth): decouple STS endpoint and make RFC 8693 token-type params configurable#1181
zruchi wants to merge 2 commits into
containers:mainfrom
zruchi:feat/sts-configurability

Conversation

@zruchi

@zruchi zruchi commented Jun 3, 2026

Copy link
Copy Markdown

Summary

Two related changes to pkg/tokenexchange / pkg/kubernetes, fully backwards-compatible:

  • RFC 8693 subject_token_type was sent empty when unset (violates RFC 8693 section 2.1) and requested_token_type was hardcoded. Both are now configurable via sts_subject_token_type / sts_requested_token_type, defaulting to access_token.
  • Add sts_token_url to point token exchange at a separate STS gateway, decoupled from the IDP at authorization_url. Unblocks cross-realm RFC 8693 and skip_jwt_verification=true + token exchange (which had no provider to discover an endpoint from).

New TOML keys

Key Default Purpose
sts_subject_token_type urn:ietf:params:oauth:token-type:access_token RFC 8693 subject_token_type form param
sts_requested_token_type urn:ietf:params:oauth:token-type:access_token RFC 8693 requested_token_type form param
sts_token_url (falls back to OIDC discovery) Explicit STS endpoint

Notable behavior changes

  • SecurityTokenService.IsEnabled() now keys off TokenURL != "" instead of Provider != nil — "is exchange usable?" is correctly "do I have an endpoint to POST to?". Required for the passthrough + gateway combo.
  • New resolveStsTokenURL helper: explicit sts_token_url wins, else OIDC-discovered endpoint.
  • sts_token_url validated as http/https, warns on http.

Example: separate gateway from issuer

authorization_url       = "https://idp.example.com/realms/users"     # validates user JWT
sts_token_url           = "https://sts-gateway.internal/oauth/token" # mints cluster token
token_exchange_strategy = "rfc8693"
sts_client_id           = "mcp-backend"
sts_client_secret       = "..."
sts_audience            = "kubernetes-api"

zruchi added 2 commits June 3, 2026 12:51
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>
@zruchi

zruchi commented Jun 9, 2026

Copy link
Copy Markdown
Author

/cc @Cali0707

@theJC

theJC commented Jul 28, 2026

Copy link
Copy Markdown

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?

@zruchi

zruchi commented Jul 28, 2026

Copy link
Copy Markdown
Author

#1173

make sense. Let me break this into two separate pr.

@theJC

theJC commented Jul 28, 2026

Copy link
Copy Markdown

Personally, I'd recommend perhaps start with one that is solely focused on fixing the subject_token_type - but thats me being selfish because thats the piece I need 😉

§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."

@Cali0707

Cali0707 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Hey yes, would be happy to review pieces of this incrementally.

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.

3 participants