Skip to content

[pull] main from UsefulSoftwareCo:main - #52

Merged
pull[bot] merged 3 commits into
midego1:mainfrom
UsefulSoftwareCo:main
Aug 27, 2026
Merged

pull[bot] merged 3 commits into
midego1:mainfrom
UsefulSoftwareCo:main

Conversation

@pull

@pull pull Bot commented Aug 27, 2026 •

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

* Add ID-JAG discovery, token exchange, and jwt-bearer redemption helpers

* Wire the enterprise-managed grant through connect and credential refresh

* Add ID-JAG protocol conformance fixtures and tests

* Cover the enterprise-managed credential lifecycle through the executor

* Declare the enterprise identity provider on MCP servers and carry it to connect

An MCP oauth2 method may now name the registered OAuth app that plays its
enterprise identity provider. The catalog projects that pointer so a client
knows which app to name on oauth.start, and the start handler forwards the
enterprise inputs it was already accepting. Declaring a provider only asks the
connect path to try the ID-JAG grant; the server still has to advertise the
profile, so an ordinary server keeps the interactive flow.

* Match enterprise-managed failures with tagged handlers

The refresh path switched on _tag by hand and the fallback rule lived in an
exported predicate nothing called. Use catchTags for the mapping, assert the
tags directly in the tests, and drop the predicate: the connect path's single
catchTag is where that rule is actually enforced.

* Cover enterprise-managed authorization against the Okta and MCP emulators

A selfhost scenario runs the whole ID-JAG chain through the product: Okta
issues the ID token, executor exchanges it for an ID-JAG and redeems it at the
MCP server, and a tool call rides the result with no consent step. Seeding one
DENY policy then proves a refusal surfaces as blocked-by-admin and does not
fall back to the interactive flow. Both emulator ledgers carry the assertions.
Needs emulate 0.14.0 for the Okta token exchange and its policy table.

* Classify token-endpoint error bodies in one place

The ID-JAG exchange had its own error-body machinery beside the existing
one, and it only understood a conform RFC 6749 envelope. An IdP answering
{"errors":["invalid_grant - ..."]} therefore read as a transport failure
and was retried forever instead of asking for a fresh sign-on.

Route it through toOAuth2ErrorWithHttpSummary, which now tries the conform
envelope first and falls back to the closed-set non-conform recovery. The
recovery only runs on 4xx, so a 5xx carrying an error code stays a
retryable transport verdict rather than becoming a permanent refusal.

Also hoist the body-reading helpers: a body-read failure on the exchange
response now fails distinctly instead of rendering as "did not match RFC
8693 2.2.1", and the read is passed as a thunk so a clone() on a consumed
body is caught rather than escaping as a defect.

* Give OAuthStartError its verdict fields, and declare the payloads once

A start failure carried only prose, so a console could not tell an
administrator refusal from a credential problem — and the two demand
opposite behaviour: blocked-by-admin must NOT offer the interactive
per-server flow, because that walks the user around the policy the
identity provider just enforced. Add blockedByAdmin and oauthErrorCode
alongside the pattern OAuthCompleteError already sets with
restartRequired.

Same file, second concern: { client, clientOwner } was written three times
(SDK interface, API Schema.Struct, MCP plugin Schema.Struct) and the
enterprise connect input twice. Each is now one Schema in the SDK that the
API and the MCP plugin reference.

SUBJECT_TOKEN_TYPES goes back to module-local: it is only ever an argument
to Schema.Literals.

* Keep the enterprise-managed taxonomy structural through connect and refresh

The connect boundary flattened every EMA failure to a message string, so
the taxonomy the module argues for at length stopped at the service edge.
Translate per tag instead: a policy denial reaches the caller as
blockedByAdmin with the identity providers own error code.

Split the error union so minting cannot claim a verdict it never reaches.
mintEnterpriseManagedAccessToken never inspects metadata, so
EmaGrantProfileUnsupported belongs only to the discovery entry point; the
refresh path loses its unreachable arm.

Also: the connect paths authorization-server discovery was unbounded,
because it was copied from scope discovery without the timeout. Both now
share one capped, bounded probe loop. The inert Effect.provide on the EMA
chain is gone — oauth4webapi drives the configured fetch, not HttpClient,
and the layer read as a claim the chain did not honour.

* Assert the enterprise-managed verdict, not its wording

Substring-matching the rendered prose let the tests pass on any error that
happened to say the right thing, and left blockedByAdmin with no coverage
at all. Assert the tag and the fields instead, at the unit, lifecycle, and
e2e tiers.

Cover the case that only exists over time: an administrator withdrawing
access AFTER a connection was made, which the credential-refresh path
meets with no user present. That is the only producer of blockedByAdmin on
a credential failure, and it was untested. The OAuth fixture grows a
policy control for it, since a denial that is fixed at construction cannot
express the change.

Decode the fixtures metadata through the production schema, so a decoder
that stopped retaining authorization_grant_profiles_supported would fail
the gate that reads it.
* Gate enterprise-managed authorization behind a host-owned rollout seam

* Carry the rollout gate through to the cloud executor

* Evaluate the enterprise-managed rollout flag in PostHog

* Re-run CI after rebase onto main
@pull pull Bot locked and limited conversation to collaborators Aug 27, 2026
@pull pull Bot added the ⤵️ pull label Aug 27, 2026
@pull
pull Bot merged commit 2c75d7a into midego1:main Aug 27, 2026

This branch was successfully deployed

No deployments
production — 2c75d7ab Deployed Aug 27, 2026 by pull[bot] via Migrate database #26
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant