Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"dependencies": {
"@executor-js/api": "workspace:*",
"@executor-js/emulate": "^0.13.9",
"@executor-js/emulate": "^0.14.0",
"@executor-js/mcporter": "^0.11.4",
"@executor-js/plugin-graphql": "workspace:*",
"@executor-js/plugin-mcp": "workspace:*",
Expand Down
443 changes: 443 additions & 0 deletions e2e/selfhost/mcp-enterprise-managed-auth.test.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions packages/core/api/src/handlers/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ export const OAuthHandlers = HttpApiBuilder.group(ExecutorApi, "oauth", (handler
identityLabel: payload.identityLabel,
newConnection: payload.newConnection,
redirectUri: payload.redirectUri,
// Enterprise-managed authorization inputs. Ignored by every other
// grant, and REQUIRED by `id_jag` — the identity assertion is held
// by the caller, never by the server.
enterprise: payload.enterprise,
});
return startResultToResponse(result);
}),
Expand Down
6 changes: 6 additions & 0 deletions packages/core/api/src/integrations/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
import { Schema } from "effect";
import {
EnterpriseIdentityProviderDescriptorSchema,
HealthCheckCandidate,
HealthCheckSpec,
IntegrationDetectionResult,
Expand Down Expand Up @@ -54,6 +55,11 @@ const OAuthDescriptor = Schema.Struct({
registrationEndpoint: Schema.optional(Schema.String),
supportsDynamicRegistration: Schema.optional(Schema.Boolean),
supportsClientIdMetadataDocument: Schema.optional(Schema.Boolean),
/** MCP Enterprise-Managed Authorization: the registered OAuth app that mints
* this integration's identity assertions. Present only when the deployment
* declared one — the client names it on `oauth.start` alongside the
* assertion it holds. The interactive flow stays available regardless. */
enterpriseIdentityProvider: Schema.optional(EnterpriseIdentityProviderDescriptorSchema),
});

/** A single declared auth method — mirrors the SDK's `AuthMethodDescriptor`. */
Expand Down
11 changes: 9 additions & 2 deletions packages/core/api/src/oauth/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
AuthTemplateSlug,
ConnectionAddress,
ConnectionName,
EnterpriseManagedStartInputSchema,
IntegrationSlug,
InternalError,
OAuthClientSlug,
Expand Down Expand Up @@ -62,7 +63,7 @@ const CreateClientPayload = Schema.Struct({
slug: OAuthClientSlug,
authorizationUrl: Schema.String,
tokenUrl: Schema.String,
grant: Schema.Literals(["authorization_code", "client_credentials"]),
grant: Schema.Literals(["authorization_code", "client_credentials", "id_jag"]),
clientId: Schema.String,
clientSecret: Schema.String,
resource: Schema.optional(Schema.NullOr(Schema.String)),
Expand Down Expand Up @@ -110,7 +111,7 @@ const RegisterDynamicResponse = Schema.Struct({
const OAuthClientSummaryResponse = Schema.Struct({
owner: Owner,
slug: OAuthClientSlug,
grant: Schema.Literals(["authorization_code", "client_credentials"]),
grant: Schema.Literals(["authorization_code", "client_credentials", "id_jag"]),
authorizationUrl: Schema.String,
tokenUrl: Schema.String,
resource: Schema.optional(Schema.NullOr(Schema.String)),
Expand Down Expand Up @@ -173,6 +174,12 @@ const StartPayload = Schema.Struct({
* name server-side instead of re-minting the existing row. */
newConnection: Schema.optional(Schema.Boolean),
redirectUri: Schema.optional(Schema.NullOr(Schema.String)),
/** Enterprise-managed authorization inputs (MCP EMA profile). Required when
* the named client's grant is `id_jag`, ignored otherwise: the client's own
* id/secret authenticate at the MCP server's authorization server, while
* these name the SECOND registration at the enterprise identity provider and
* carry the identity assertion the user already holds from single sign-on. */
enterprise: Schema.optional(EnterpriseManagedStartInputSchema),
});

const StartResponse = Schema.Union([
Expand Down
6 changes: 6 additions & 0 deletions packages/core/sdk/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@ export class CredentialResolutionError extends Schema.TaggedErrorClass<Credentia
* `invalid_client` (rotated app secret, fleet-wide) surfaced as a vague
* "degraded" was only findable by grepping persisted message strings. */
oauthErrorCode: Schema.optional(Schema.String),
/** True when an enterprise identity provider declined to authorize this
* connection under administrator policy. Distinct from `reauthRequired`
* because signing in again cannot help, and — critically — the client must
* NOT offer the ordinary per-server OAuth flow as an alternative route:
* that would let the user walk around the policy the IdP just enforced. */
blockedByAdmin: Schema.optional(Schema.Boolean),
},
) {}

Expand Down
Loading
Loading