Skip to content

test(e2e): sign in through hosted AS-1 with Dex, plus a local Kind profile - #2622

Merged
jhrozek merged 5 commits into
mainfrom
test/identitybroker-as1-kind-e2e
Nov 1, 2026
Merged

jhrozek merged 5 commits into
mainfrom
test/identitybroker-as1-kind-e2e

Conversation

@ChrisJBurns

@ChrisJBurns ChrisJBurns commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Follows #2613 (merged). Closes #2621.

Summary

  • Private IdP settings: --identity-broker-as1-upstream-ca-file (a PEM CA bundle trusted in addition to system roots) and --identity-broker-as1-upstream-allow-private-addresses, both refused without --identity-broker-as1-issuer. Admitting private addresses requires the CA file, as --oidc-allow-private-https-issuer requires --oidc-ca-cert-file. It applies to every request AS-1 makes to the upstream IdP (discovery, discovered endpoints, redirects), because ToolHive enforces it in its HTTP client's dialer; the flag help and values say so. Helm: identityBroker.as1.upstream.caSecret.{secretName,key} (mounted read-only) and identityBroker.as1.upstream.allowPrivateAddresses. Moved here from Operator configuration, keys, and deployment #2538; the kind/Keycloak demo uses them too.
  • Audience check: AS-1 (ToolHive) accepts only an absolute http or https URL with a host and no fragment as its audience and otherwise fails at startup. mecak8s now refuses any other --oidc-audience when --identity-broker-as1-issuer is set, the chart refuses it at render time, and the production identity-broker CI fixture uses https://mecatl.example.com/api instead of mecatl.
  • Kind e2e spec (e2e/k8s/hosted_as1_test.go, build tag kind_e2e): in its own namespace mecatl-as1, so the suite's two-replica release and existing Dex are untouched:
    • a test CA and certificates generated in memory; Dex v2.44.0 (the suite's image) on HTTPS with alice and bob and a static client for AS-1, without a service-account token;
    • a second single-replica mecak8s release with TLS, --no-shell, OIDC sub_id ownership, the broker, AS-1 at https://mecak8s-as1.mecatl-as1.svc.cluster.local:8081/as1, a signing-key Secret, and the new CA and private-address values;
    • the real clientauth.Login from the test process, through port-forwards (the client maps in-cluster host names to them; no /etc/hosts), with a browser that fills Dex's login form and approves AS-1's consent page (detection follows ToolHive's consentPageTemplate).
  • Local dev profile (deploy/mecak8s-idbroker/, root include mecak8s-idbroker:): task mecak8s-idbroker:kind-setup creates a separate mecatl-idbroker Kind cluster (host ports 18090, 18091, 5557, so it runs alongside mecatl-dev), builds and loads the image, installs cert-manager and a fixture CA, deploys Dex over HTTPS with a dev-only user dev@example.com, and installs mecak8s with the broker and AS-1 (values-kind-idbroker.yaml). Then hosts-add, login, connect; also image-refresh, status, hosts-remove (with its own /etc/hosts backup), and destroy. A fixture test keeps ports, host names, the Dex redirect, the audience URL, and the login and connect tasks consistent. Verified live: full sign-in through AS-1 and Dex, sessions over HTTPS and gRPC, per-user isolation, image-refresh keeping the signing key, and destroy removing only its own cluster. Added to Test hosted AS-1 in the Kind e2e suite with Dex, and add a local Kind profile #2621's scope.
  • CI: k8s-e2e.yml path filters add internal/identitybroker/**, internal/app/identitybroker.go, internal/adapter/clientauth/**, internal/adapter/server/** (replacing the individual files), authn/oidc/**, and mcp/oauthlogin/**.

What the spec asserts

  1. Protected-resource metadata lists only AS-1, with the TUI client, audience, and the broker sign-in signal.
  2. Sign-in succeeds; the token has typ: at+jwt, AS-1's iss, client_id, aud, and a sub_id (iss_sub) naming Dex's issuer and subject.
  3. A session created with it is owned by that issuer and subject.
  4. Bob, signed in through AS-1 and Dex, gets a different sub_id; his session list leaves out alice's session, opening hers returns 404, and alice still sees hers but not his.
  5. Access tokens signed with AS-1's own key but with the wrong audience, or without sub_id, get 401; a control token minted the same way with the correct claims is accepted, so the refusals come from the claim checks.
  6. Refresh returns a new token for the same sub_id, rotates the refresh token, and opens a session.
  7. After the pod is deleted and replaced, a pre-restart access token still opens a session (signing key from a Secret), and the pre-restart refresh token gets invalid_grant (AS-1's in-memory state until Durable broker state and recovery #2563, which flips this assertion).

Observed Dex behaviour: AS-1's consent page comes first, then Dex's login form; Dex v2.44 shows no approval screen for prompt=consent with skipApprovalScreen (the browser approves one if a later Dex shows it). The spec shows that AS-1 requests offline_access with prompt=consent; that Dex issued an upstream refresh token is not asserted (that would need Dex's gRPC API).

Testing

  • Focused: GOWORK=off go test -tags kind_e2e -count=1 -timeout 40m ./e2e/k8s/ -ginkgo.focus='hosted AS-1' passes 7/7 (85 s).
  • CI's Kind job covers the full suite.
  • Unit: go test ./cmd/mecak8s/... ./deploy/helm/mecak8s/ ./deploy/mecak8s-idbroker/, helm-unittest, kubeconform, docs:check, and site:build pass; lint clean on touched packages.

API compatibility

  • Assessment: additive, plus one tightening. Two new opt-in mecak8s flags and Helm values, accepted only with --identity-broker-as1-issuer; no wire change. With AS-1 enabled, a non-URL --oidc-audience (oidc.audience) is now refused at flag parsing and at chart render time; this moves an existing AS-1 startup failure earlier with a clear message.
  • Major / features: none.
  • Upgrade: none required. A deployment with AS-1 enabled already needed a URL audience to start.

@ChrisJBurns
ChrisJBurns force-pushed the test/identitybroker-as1-kind-e2e branch from 06a683d to 503165f Compare October 30, 2026 13:31
@ChrisJBurns
ChrisJBurns force-pushed the feat/identitybroker-host-as1 branch from 99fe64e to 65a2a75 Compare October 30, 2026 13:39
@ChrisJBurns
ChrisJBurns force-pushed the test/identitybroker-as1-kind-e2e branch from 503165f to e04ca1d Compare October 30, 2026 13:40
Base automatically changed from feat/identitybroker-host-as1 to main October 30, 2026 14:05
@ChrisJBurns
ChrisJBurns force-pushed the test/identitybroker-as1-kind-e2e branch from e04ca1d to 39f3cdc Compare October 30, 2026 14:14
@ChrisJBurns ChrisJBurns changed the title test(e2e): sign in through hosted AS-1 with Dex in the Kind suite test(e2e): sign in through hosted AS-1 with Dex, plus a local Kind profile Oct 30, 2026
@ChrisJBurns
ChrisJBurns requested a review from jhrozek October 30, 2026 17:34
ChrisJBurns added a commit that referenced this pull request Oct 30, 2026
… admission

Review follow-ups for #2622:
- k8s-e2e path filter: internal/adapter/server/**, authn/oidc/**, and
  mcp/oauthlogin/**, so changes to code the spec exercises run the suite.
- --identity-broker-as1-upstream-allow-private-addresses now requires
  --identity-broker-as1-upstream-ca-file, like
  --oidc-allow-private-https-issuer requires --oidc-ca-cert-file; the
  chart refuses the same. Help and docs say it applies to every request
  AS-1 makes to the upstream IdP, including discovered endpoints and
  redirects.
- Kind spec: bob, signed in through AS-1 and Dex, does not see alice's
  session and gets 404 opening it; AS-1-signed tokens with the wrong
  audience or without sub_id get 401, with a correctly minted control
  token accepted; the restart spec names #2563.
- Smaller fixes: Dex pod without a service-account token, a helm-unittest
  case for a CA key without a Secret, a profile-specific /etc/hosts
  backup in hosts-remove, and a comment naming ToolHive's consent
  template.

Refs #2621

Co-authored-by: Mecatl <noreply@mecatl.dev>
ChrisJBurns and others added 5 commits November 1, 2026 17:57
Adds a Kind e2e spec that deploys a second, single-replica mecak8s
release with the identity broker and AS-1 on, TLS, and an HTTPS Dex as
AS-1's upstream, all in their own namespace. The real mecatui login
signs in through AS-1 and Dex; the spec checks the protected-resource
metadata, the at+jwt token and its sub_id, sub_id session ownership,
refresh rotation, and that after a pod restart a pre-restart access
token still passes while AS-1's earlier refresh token is refused.

Also adds --identity-broker-as1-upstream-ca-file and
--identity-broker-as1-upstream-allow-private-addresses, with Helm values,
so AS-1 can use an IdP behind a private CA or on a private address, and
extends the k8s-e2e path filter to the identity broker code.

Closes #2621

Co-authored-by: Mecatl <noreply@mecatl.dev>
task mecak8s-idbroker:kind-setup creates a separate mecatl-idbroker Kind
cluster (host ports 18090, 18091, 5557, so it runs alongside mecatl-dev),
builds and loads the mecak8s image, installs cert-manager and a fixture
CA, deploys Dex over HTTPS with two dev-only users, and installs mecak8s
with the identity broker and hosted AS-1 using Dex as its upstream.
hosts-add, login, connect, image-refresh, status, and destroy cover the
rest of the loop. A fixture test keeps ports, host names, the Dex client
redirect, the audience URL, and the login and connect tasks consistent.

Refs #2621

Co-authored-by: Mecatl <noreply@mecatl.dev>
… profile

Co-authored-by: Mecatl <noreply@mecatl.dev>
AS-1 (ToolHive) accepts only an absolute http or https URI with a host
and no fragment as an allowed audience and fails at startup otherwise.
mecak8s and the chart now refuse any other --oidc-audience when
--identity-broker-as1-issuer is set, and the production identity-broker
CI fixture uses a URL audience.

Refs #2490

Co-authored-by: Mecatl <noreply@mecatl.dev>
… admission

Review follow-ups for #2622:
- k8s-e2e path filter: internal/adapter/server/**, authn/oidc/**, and
  mcp/oauthlogin/**, so changes to code the spec exercises run the suite.
- --identity-broker-as1-upstream-allow-private-addresses now requires
  --identity-broker-as1-upstream-ca-file, like
  --oidc-allow-private-https-issuer requires --oidc-ca-cert-file; the
  chart refuses the same. Help and docs say it applies to every request
  AS-1 makes to the upstream IdP, including discovered endpoints and
  redirects.
- Kind spec: bob, signed in through AS-1 and Dex, does not see alice's
  session and gets 404 opening it; AS-1-signed tokens with the wrong
  audience or without sub_id get 401, with a correctly minted control
  token accepted; the restart spec names #2563.
- Smaller fixes: Dex pod without a service-account token, a helm-unittest
  case for a CA key without a Secret, a profile-specific /etc/hosts
  backup in hosts-remove, and a comment naming ToolHive's consent
  template.

Refs #2621

Co-authored-by: Mecatl <noreply@mecatl.dev>
@jhrozek
jhrozek merged commit 868334a into main Nov 1, 2026
49 of 50 checks passed
@jhrozek
jhrozek deleted the test/identitybroker-as1-kind-e2e branch November 1, 2026 19:27

This branch was successfully deployed

1 active deployment
Preview — 2903e19d Deployed Nov 1, 2026 by vercel[bot]
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.

Test hosted AS-1 in the Kind e2e suite with Dex, and add a local Kind profile

2 participants