Skip to content

feat(identitybroker): bump ToolHive to v0.61.0 and close the delegate's access-token path - #2675

Merged
ChrisJBurns merged 3 commits into
mainfrom
feat/identitybroker-toolhive-v0.61
Nov 3, 2026
Merged

ChrisJBurns merged 3 commits into
mainfrom
feat/identitybroker-toolhive-v0.61

Conversation

@ChrisJBurns

@ChrisJBurns ChrisJBurns commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Closes #2607

Problem

#2653 registered the identity broker's client, mecatl, at hosted AS-1. The broker can now convert a user's sign-in into a refresh token at session open, but ToolHive v0.60.0 left three gaps:

ToolHive v0.61.0 fixes all three. Its fix for the first gap also changes the delegate client's configuration in a way that breaks compatibility: api_audience is replaced by an input-only subject_token_audiences.

Solution

  • Bump ToolHive to v0.61.0 and run task tidy across all modules.
  • Reconfigure the delegate client (internal/identitybroker/adapter/as1server/delegate.go). mecatl now lists the Mecatl API audience only in token_exchange.subject_token_audiences (Separate delegate input audiences from output audiences toolhive#7037), and has no output audiences. It can still turn a user's Mecatl API token into an exchange-grant refresh token. It can no longer obtain a Mecatl API access token, because ToolHive refuses to start if an output audience covers an input one.
  • Recognise the new refusal reason. v0.61.0 refuses a token without offline_access with a reason mecatl didn't know, subject_token_offline_access, so the broker dropped it and reported a generic internal error. The broker now knows the reason and treats it as a configuration fault, like subject_token_audience: mecatui always requests offline_access. The design's reason tables list it too.
  • Refuse at startup a scope list without offline_access. mecatui requests an advertised --oidc-scopes list exactly. Before this change, such a deployment would have failed every session open with INTERNAL after the bump. With hosted AS-1, mecak8s now refuses to start if the list lacks offline_access. The operating guide states the requirement.
  • Make the tests enforce all three fixes, pinning the exact refusal for each instead of skipping it or accepting any refusal (see Testing).
  • Update implementation.md's ToolHive work list. One item stays "Partly": the accepted assertion aud still includes the token-endpoint URL, which #7028 left optional.

Testing

Changes to existing tests:

  • TestDelegateCannotObtainAPIAccessTokens now runs. It sends a plain RFC 8693 request for an access token, both with no audience and with audience set to AS-1's. Each gets 400 invalid_request. ToolHive gives this refusal no reason code, so its free-text description, which names the missing output audience, is logged rather than matched. A control subtest shows the same client, assertion and subject token convert with the session-open form, so the refusal comes from the policy. With v0.60.0's configuration the request succeeded.
  • TestDelegateRequiresOfflineAccess now runs and expects the reason subject_token_offline_access.
  • New TestDelegateRefusesAForeignAudienceSubjectToken: a subject token for another audience AS-1 issues is refused with subject_token_audience. This covers the move from api_audience to subject_token_audiences.
  • The reason classification tables in serverrefusal_test.go and sessionopen_test.go include subject_token_offline_access (session open answers INTERNAL).
  • TestIdentityBrokerAS1FlagRules has two new cases: --oidc-scopes=openid,profile refuses startup, and --oidc-scopes=openid,offline_access starts.
  • TestDelegateRefusesBadAssertions now expects 401 invalid_client in every case: expired, exp more than 5 minutes ahead, wrong audience, unregistered key, and reused jti.

Runs:

  • go test -race passes for ./internal/identitybroker/..., ./cmd/mecak8s/, ./internal/adapter/mcpbroker/... and ./internal/adapter/.
  • ./internal/app passes with the identity-broker tests selected.
  • go vet passes for e2e/k8s (with the kind_e2e tag), environment/microvm and integration/microvm.
  • task tidy leaves no changes.
  • task lint shows no new findings. It still stops at the existing darwin-only gosec finding in environment/microvm/launch_ownership_darwin.go:754.
  • task docs:check is clean.
  • CI passes, including the Kind e2e.

API compatibility

No wire change. The one externally visible difference is at AS-1's token endpoint: a token-exchange request from mecatl for an access token is now refused. Mecatl never makes that request.

ChrisJBurns and others added 3 commits November 3, 2026 18:14
…'s access-token path

The broker's delegate client at hosted AS-1 now names the Mecatl API
audience only as an input (subject_token_audiences) and has no output
audience, so it can never obtain a Mecatl API access token. Un-skip the
access-token and offline_access refusal tests, and expect 401
invalid_client for every failed client assertion.

Closes #2607

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

ToolHive v0.61.0 refuses a subject token issued without offline_access
with subject_token_offline_access. The broker now knows that reason and
treats it as a configuration fault, as for subject_token_audience; the
design's reason tables list it.

The delegate tests now pin each refusal: an access-token request gets
400 invalid_request naming the missing output audience, next to a
passing control; a token without offline_access gets
subject_token_offline_access; and a new test refuses a subject token
for another audience with subject_token_audience.

Part of #2607

Co-authored-by: Mecatl <noreply@mecatl.dev>
mecatui requests an advertised --oidc-scopes list exactly, and since
ToolHive v0.61.0 AS-1 converts at session open only an access token
issued with offline_access. A list without it now refuses startup
instead of failing every session open with INTERNAL.

The access-token refusal test pins 400 invalid_request and only logs
ToolHive's free-text description, which has no reason code.

Part of #2607

Co-authored-by: Mecatl <noreply@mecatl.dev>
@ChrisJBurns
ChrisJBurns merged commit dc3b065 into main Nov 3, 2026
45 checks passed
@ChrisJBurns
ChrisJBurns deleted the feat/identitybroker-toolhive-v0.61 branch November 3, 2026 20:52

This branch was successfully deployed

1 active deployment
Preview — dfaf07c7 Deployed Nov 3, 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.

Register the broker client at AS-1 and point the broker at it

2 participants