feat(apps): Vanta connection for their MCP server - #1
Open
marcorivm wants to merge 1 commit into
Open
Conversation
Vanta's MCP server authorizes as an MCP resource, not through the REST API: its metadata advertises only authorization_code + refresh_token with token_endpoint_auth_methods_supported: ["none"], S256 PKCE, a dynamic registration endpoint, and its own scope namespace (mcp-api.*). There is no client-credentials shortcut and no secret an operator could configure, so BYOC (`configurable`) has nothing to hold — hence two new pieces of generic OAuth plumbing alongside the app itself. - PKCE (RFC 7636): oauth methods can set `pkce: true`; the connect route mints the pair, sends the challenge, and keeps the verifier in an httpOnly cookie scoped to the callback path — never in the state that travels to the provider. - Dynamic client registration (RFC 7591): `dynamicRegistration` apps get a public client minted on demand, cached in the project's AppConfig row keyed by (region, redirectUri) so a moved deployment origin re-registers. The callback never registers: the code is bound to the client that started the flow. Minting is audited as an app-config write. - Gateway: `vanta` over the three regional MCP hosts, each gated by `credential_host_field: "mcp_host"` so a token minted for one region can never be injected into another's host. api.vanta.com is deliberately absent — these tokens are MCP-scoped. Refresh runs through the existing refresh_token path (which persists rotated tokens) via a new PublicClient auth mode plus credential-sourced client id and token endpoint, since both are per-connection for a registered client. Regions: US by default; eu/aus resolve end to end from `?region=` on the connect URL, with no picker in the dashboard yet. No app-permissions catalog entry: every MCP call is POST /mcp, so the host/path/method tool model cannot separate reads from writes and an entry would author misleading "all reads" rules. Policy targets Vanta as a network host for now. Verified live: registration is accepted and returns a public client id; the authorize URL built from it is accepted with no parameter rejection; the token endpoint recognizes the client. The consent leg and first token exchange need a Vanta Admin login and are untested.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a Vanta connection so agents can use Vanta's MCP server through the gateway — connect once in the dashboard, then point any MCP client at
https://mcp.vanta.com/mcpwith no auth and let the gateway inject credentials.Why this needed new OAuth plumbing
Vanta's MCP server authorizes as an MCP resource, not through Vanta's REST API. Its live metadata:
So Vanta can't be a client-credentials import (MongoDB Atlas shape), and it can't use BYOC — there is no secret for an operator to bring, so
configurablehas nothing to hold. Two generic pieces were added alongside the app:PKCE (RFC 7636) — an oauth method can set
pkce: true. The connect route mints the pair, sends the challenge, and keeps the verifier in an httpOnly cookie scoped to the callback path — deliberately not in the signed state, which travels to the provider.Dynamic client registration (RFC 7591) — a
dynamicRegistrationapp gets a public client minted on demand, cached in the project's AppConfig row keyed by(region, redirectUri), so a moved deployment origin re-registers (a client id is only valid for its registered redirect URI). The callback leg never registers: the authorization code is bound to the client that started the flow, so a fresh client there could only produce a failed exchange. Minting writes config, so it emits anapp-config/createaudit event.Gateway
vantais registered over the three regional MCP hosts, each gated bycredential_host_field: "mcp_host", so a token minted for one region can never be injected into another region's host.api.vanta.comis deliberately not registered — these are MCP-scoped tokens the REST API does not accept.Refresh goes through the existing
refresh_tokenpath (the one that persists rotated refresh tokens) rather thantry_refresh_credentials, which cannot return a rotated token. That needed a newClientCredentialMethod::PublicClient(client_id in body, no secret anywhere) plus credential-sourced client id and token endpoint, since for a registered client both are per-connection rather than per-deployment.RefreshConfiggained two optional fields — EE builds defining their ownRefreshConfigliterals will need them added.Verified
Against Vanta's live endpoints, without a Vanta account:
client_idwith no secret.mcp-api.*scopes, RFC 8707resourceall accepted).Locally:
pnpm checkclean; API and gateway suites pass (20 new tests covering region→endpoint mapping, credential shape, PKCE enforcement, registration caching/re-registration, and the never-register-on-callback rule).Not verified: the consent leg, the first real token exchange, and refresh — these need a Vanta Admin login. Worth exercising before merge.
Known limits (deliberate)
eu/ausresolve end to end from?region=on the connect URL, but there's no picker in the dashboard yet, so an EU/AUS tenant can't self-serve.POST /mcp, so the host/path/method tool model cannot separate reads from writes — an entry would author misleading "all reads" rules. Policy targets Vanta as a network host (mcp.vanta.com) for now; per-tool rules would need body conditions, whose matching is EE-only.tryHandleOrgAuthorize) runs before this plumbing, so org-scoped Vanta connections aren't supported yet.Note
Verification created one anonymous, un-consented client registration at Vanta (
vci_a69fa253…, redirect URIhttps://api.onecli.sh/v1/apps/vanta/callback). Vanta returned no registration access token, so RFC 7592 self-deletion isn't available — only Vanta support can remove it. It grants nothing until an admin authorizes it.