Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
8d7f70c
Add opt-in Enterprise-Managed Authorization (MCP ID-JAG extension)
claude Jul 4, 2026
bc4e880
Annotate fake test API keys for gitleaks
claude Jul 4, 2026
2f9e08b
Annotate remaining fake signing key for gitleaks
claude Jul 4, 2026
1bf441e
Address code review: discovery timeout, /token rate limit, OAuth erro…
claude Jul 4, 2026
1af5c4a
Enforce enterprise token scopes per tool; harden /token and discovery
claude Jul 4, 2026
15539f0
Fix empty-scope fail-open; add HTTP integration tests; polish
claude Jul 4, 2026
afd7aeb
Case-insensitive X-Skyflow-Authorization prefix; conformance polish
claude Jul 4, 2026
1d2b830
Shorten default token TTL to 15m; louder operational logging
claude Jul 4, 2026
8e22dc5
Comma-split repeated X-Forwarded-For headers; note per-instance limits
claude Jul 4, 2026
6b11ea2
Require exp/iat/sub/jti claims on ID-JAGs; strict numeric env parsing
claude Jul 4, 2026
0f62a0a
Simplify unreachable exp fallback; pin no-credentials 401 path
claude Jul 4, 2026
0eba532
Don't demote enterprise requests with placeholder vault params
claude Jul 4, 2026
f9d3505
Document scope-naming assumption to verify against live IdPs
claude Jul 4, 2026
7f0df24
Required mode never demotes to anonymous mode
claude Jul 4, 2026
ad0ccea
Surface serverless best-effort caveat in README; consistent 404 body
claude Jul 4, 2026
1ae5946
Document discovery-cache lifetime; e2e test for required-mode apiKey …
claude Jul 4, 2026
aca737e
Pair env vault fallback only with the service credential
claude Jul 4, 2026
fa8dff2
Ignore OIDC identity scopes in tool enforcement; validate PRM path su…
claude Jul 4, 2026
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@

### Added

- **Enterprise-Managed Authorization (opt-in)** — Implements the MCP `io.modelcontextprotocol/enterprise-managed-authorization` extension (ID-JAG profile). When `ENTERPRISE_AUTH_ENABLED=true`, the server acts as its own Resource Authorization Server: it validates Identity Assertion JWT Authorization Grants issued by an enterprise IdP (Okta, Entra, any OIDC IdP) and issues short-lived, audience-restricted access tokens that gate `/mcp`.
- New endpoints: `POST /token` (RFC 7523 jwt-bearer grant, rate-limited per client IP via `ENTERPRISE_TOKEN_RATE_LIMIT_*`), `GET /.well-known/oauth-authorization-server` (RFC 8414, advertises `urn:ietf:params:oauth:grant-profile:id-jag`), `GET /.well-known/oauth-protected-resource[/mcp]` (RFC 9728). All 404 when the feature is disabled.
- New middleware gates `/mcp` in `required` or `optional` mode; 401 responses carry a `WWW-Authenticate: Bearer resource_metadata="..."` challenge for client discovery.
- Skyflow vault credentials under enterprise auth resolve from the `X-Skyflow-Authorization` header, then the new `SKYFLOW_API_KEY` service-credential env var, then existing fallbacks (`apiKey` query param, anonymous mode).
- ID-JAG validation covers `typ: oauth-id-jag+jwt`, IdP JWKS signature (explicit URI or OIDC discovery with a 5s timeout and https-only `jwks_uri`), issuer/audience/expiry, `resource` claim matching, optional `client_id` allowlist, and best-effort `jti` replay detection. Misconfiguration fails closed.
- Tool-level scope enforcement: when the enterprise access token carries a `scope` claim, each scope names a permitted tool (`de-identify`, `re-identify`); other tools return an `insufficient_scope` error result. Tokens without a scope claim are unrestricted; an empty scope claim (`""`) is preserved end-to-end and denies all tools.
- HTTP integration tests (`tests/integration/`) exercising the real Express app over the wire: discovery → `/token` → gated `/mcp`, scope enforcement, replay rejection, and optional-mode fall-through, against a mock OIDC IdP.
- New env vars: `ENTERPRISE_AUTH_ENABLED`, `ENTERPRISE_AUTH_ISSUER`, `ENTERPRISE_IDP_ISSUER`, `ENTERPRISE_AUTH_SIGNING_KEY`, `ENTERPRISE_AUTH_MODE`, `ENTERPRISE_IDP_JWKS_URI`, `ENTERPRISE_IDP_AUDIENCE`, `ENTERPRISE_MCP_RESOURCE`, `ENTERPRISE_ALLOWED_CLIENT_IDS`, `ENTERPRISE_TOKEN_TTL_SECONDS`, `SKYFLOW_API_KEY`.
- 82 new unit tests (`tests/unit/auth/`, `tests/unit/middleware/enterpriseAuth.test.ts`) and a setup guide in `docs/enterprise-managed-auth.md`.
- Added `jose` dependency for JWT/JWKS handling.

- **MCP Apps UI for all three tools** — Each tool (`dehydrate`, `rehydrate`, `dehydrate_file`) now has an interactive vanilla TypeScript UI that renders inline in MCP Apps-capable hosts. Text-only hosts continue to receive JSON responses as before.
- **Dehydrate UI**: Side-by-side before/after text panels with color-coded entity highlights, confidence scores, and an entity breakdown table. Shows anonymous mode banner when applicable.
- **Rehydrate UI**: Token-to-original mapping display with color-matched highlights across before/after panels.
Expand Down
21 changes: 20 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ curl -X POST "http://localhost:3000/mcp?vaultId={vault_id}&vaultUrl={vault_url}"
- Accepts query parameters: `vaultId`, `vaultUrl`, `apiKey` (optional)
- Uses credentials extraction middleware to validate either Authorization header or apiKey query parameter
- Configured with 5MB JSON payload limit to support base64-encoded files
- Optionally exposes enterprise-managed authorization endpoints (see below)

**Enterprise-Managed Authorization Layer** (`src/lib/auth/`, opt-in)
- Implements the MCP `io.modelcontextprotocol/enterprise-managed-authorization` extension (ID-JAG profile: RFC 8693 token exchange at the IdP + RFC 7523 jwt-bearer grant at this server)
- When `ENTERPRISE_AUTH_ENABLED=true`, the server acts as its own Resource Authorization Server: it validates ID-JAGs issued by an enterprise IdP (Okta, Entra, any OIDC IdP) and issues short-lived HS256 access tokens audience-restricted to the MCP resource identifier
- `src/lib/auth/config.ts` — env var loading/validation (fails closed on misconfiguration)
- `src/lib/auth/idJag.ts` — ID-JAG validation: `typ: oauth-id-jag+jwt` header, IdP JWKS signature (explicit URI or OIDC discovery), issuer/audience/expiry, `resource` claim, client allowlist, best-effort in-memory `jti` replay detection
- `src/lib/auth/accessTokens.ts` — issue/verify enterprise access tokens (`typ: at+jwt`)
- `src/lib/auth/routes.ts` — `POST /token` plus RFC 8414 (`/.well-known/oauth-authorization-server`) and RFC 9728 (`/.well-known/oauth-protected-resource[/mcp]`) metadata; all return 404 when disabled
- `src/lib/middleware/enterpriseAuth.ts` — gates `/mcp` with issued tokens (`required` or `optional` mode); 401 responses carry a `WWW-Authenticate: Bearer resource_metadata="..."` challenge. After verifying the enterprise token it resolves Skyflow credentials from the `X-Skyflow-Authorization` header → `SKYFLOW_API_KEY` env var → existing fallbacks, and exposes the enterprise identity as `req.enterpriseAuth`
- `src/lib/auth/scopes.ts` — tool-level scope enforcement: when the enterprise access token carries a `scope` claim, each scope names a permitted tool (`de-identify`, `re-identify`); tokens without a scope claim are unrestricted. Denied calls return an `insufficient_scope` isError tool result
- Full setup guide (Skyflow-hosted-with-Okta and self-hosted-with-customer-IdP scenarios): `docs/enterprise-managed-auth.md`

**MCP Server Instance**
- Registers two active tools: `de-identify` and `re-identify`
Expand Down Expand Up @@ -127,14 +139,19 @@ This ensures type safety and provides clear error messages for invalid inputs.
1. **Bearer token via header** (JWT): Clients provide their Skyflow bearer token via `Authorization: Bearer <jwt>` header. The server auto-detects JWTs by their format (3 dot-separated base64url parts).
2. **API key via header**: Clients can also pass a Skyflow API key via `Authorization: Bearer <api-key>` header. If the value doesn't look like a JWT, it's treated as an API key.
3. **API key via query parameter** (fallback): Clients can pass a Skyflow API key via `apiKey` query parameter
4. **Enterprise-managed authorization** (opt-in): the `Authorization` header carries an enterprise access token issued by this server's `/token` endpoint after an ID-JAG exchange with the org's IdP; Skyflow credentials then come from the `X-Skyflow-Authorization` header, `SKYFLOW_API_KEY` env var, or existing fallbacks. See `docs/enterprise-managed-auth.md`.

Optional fallback variables in `.env.local`:
- `VAULT_ID`: Your Skyflow vault identifier (can be overridden via query parameter)
- `VAULT_URL`: Full vault URL (e.g., `https://ebfc9bee4242.vault.skyflowapis.com`) (can be overridden via query parameter)
- `PORT`: Server port (default: 3000)

**Enterprise-managed authorization variables** (all optional; feature off unless `ENTERPRISE_AUTH_ENABLED=true`):
- `ENTERPRISE_AUTH_ENABLED`, `ENTERPRISE_AUTH_ISSUER`, `ENTERPRISE_IDP_ISSUER`, `ENTERPRISE_AUTH_SIGNING_KEY` (required when enabled)
- `ENTERPRISE_AUTH_MODE` (`required`|`optional`), `ENTERPRISE_IDP_JWKS_URI`, `ENTERPRISE_IDP_AUDIENCE`, `ENTERPRISE_MCP_RESOURCE`, `ENTERPRISE_ALLOWED_CLIENT_IDS`, `ENTERPRISE_TOKEN_TTL_SECONDS`, `ENTERPRISE_TOKEN_RATE_LIMIT_REQUESTS`, `ENTERPRISE_TOKEN_RATE_LIMIT_WINDOW_MS`
- `SKYFLOW_API_KEY`: server-side Skyflow service credential, used only for requests authenticated via enterprise auth

**Removed variables** (no longer used):
- `SKYFLOW_API_KEY`: No longer needed - credentials are passed from client
- `REQUIRED_BEARER_TOKEN`: No longer needed - all valid credentials are accepted and forwarded to Skyflow
- `ACCOUNT_ID` / `WORKSPACE_ID`: Never consumed by Skyflow SDK - removed

Expand Down Expand Up @@ -283,6 +300,7 @@ The `isError` property is set to `true` when a tool returns an error condition (
- `skyflow-node`: Skyflow SDK for deidentification (v2.0.0+)
- `express`: Web framework (v5.1.0+)
- `zod`: Schema validation for tool inputs/outputs
- `jose`: JWT signing/verification and JWKS handling for enterprise-managed authorization
- `dotenv`: Environment variable management
- `tsx`: TypeScript execution (via npx)
- `vite` + `vite-plugin-singlefile`: UI build pipeline (dev dependencies)
Expand Down Expand Up @@ -329,3 +347,4 @@ All of the above, plus:
6. **AsyncLocalStorage context** - Tools must run within the request context to access Skyflow instance via `getCurrentSkyflow()` and `isAnonymousMode()`
7. **Anonymous mode limitations** - Only the `de-identify` tool works in anonymous mode; `re-identify` returns an error with setup instructions
8. **Keep schemas in sync** - When modifying tool inputs or return values, always update the corresponding `inputSchema` and `outputSchema` in the tool registration. The schemas must match the actual implementation.
9. **Enterprise auth is opt-in and fails closed** - Nothing changes unless `ENTERPRISE_AUTH_ENABLED=true`. When enabled but misconfigured, `/mcp` returns 500 rather than skipping authorization. In `required` mode the Authorization header must carry an enterprise access token, so Skyflow credentials move to `X-Skyflow-Authorization`, `SKYFLOW_API_KEY`, or the `apiKey` query parameter — direct Skyflow tokens in the Authorization header get 401. Keep middleware order on `/mcp`: enterprise auth → authenticateBearer → rate limiter.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ See [Integration with Claude Desktop](#integration-with-claude-desktop) for one

When self-hosting, `VAULT_ID` and `VAULT_URL` can be set as environment variables in `.env.local` — query parameters override them per request. Useful for pinning a single vault without rewriting client URLs. See [Environment Variables](#environment-variables).

## Enterprise-Managed Authorization (SSO)

The server supports the MCP [Enterprise-Managed Authorization extension](https://modelcontextprotocol.io/extensions/enterprise-managed-authorization): organizations can gate access to `/mcp` through their identity provider (Okta, Entra, any OIDC IdP). Employees sign in to their MCP client with corporate SSO, the client exchanges the resulting identity assertion for an ID-JAG at the IdP, and this server's built-in `/token` endpoint turns the ID-JAG into a short-lived access token for `/mcp` — no per-user Skyflow credentials or per-server authorization prompts needed.

The feature is off by default and enabled via `ENTERPRISE_AUTH_ENABLED=true`. It supports a `required` mode (every request needs SSO-derived auth — for self-hosted enterprise deployments) and an `optional` mode (SSO tokens accepted alongside ordinary Skyflow credentials — for shared endpoints). See [docs/enterprise-managed-auth.md](docs/enterprise-managed-auth.md) for the full flow, environment variable reference, and IdP setup for both deployment scenarios.

> **Serverless note:** ID-JAG replay detection and `/token` rate limiting use in-memory, per-instance state. On horizontally scaled or serverless platforms (Vercel included) they are best-effort only — if your threat model requires strict single-use grants or hard rate limits, back them with a shared store or enforce at your edge. Details in the [security notes](docs/enterprise-managed-auth.md#security-notes).

## Installation

```bash
Expand Down Expand Up @@ -159,7 +167,9 @@ Create a `.env.local` file with optional fallback values:
- `VAULT_URL`: Your Skyflow vault URL (optional - can be provided via query parameter, e.g., `https://ebfc9bee4242.vault.skyflowapis.com`)
- `PORT`: Server port (default: 3000)

**Note**: `SKYFLOW_API_KEY`, `REQUIRED_BEARER_TOKEN`, `ACCOUNT_ID`, and `WORKSPACE_ID` are no longer used. The bearer token is passed through from the client to Skyflow; account/workspace IDs were never consumed by the SDK.
For enterprise-managed authorization variables (`ENTERPRISE_AUTH_*`, `ENTERPRISE_IDP_*`, `SKYFLOW_API_KEY`), see [docs/enterprise-managed-auth.md](docs/enterprise-managed-auth.md).

**Note**: `REQUIRED_BEARER_TOKEN`, `ACCOUNT_ID`, and `WORKSPACE_ID` are no longer used. The bearer token is passed through from the client to Skyflow; account/workspace IDs were never consumed by the SDK. `SKYFLOW_API_KEY` is only consumed as a service credential for enterprise-managed authorization deployments.

## Anonymous Mode (Try Before You Buy)

Expand Down
Loading
Loading