Add enterprise IdP plugin with OIDC and SAML 2.0 support#9
Add enterprise IdP plugin with OIDC and SAML 2.0 support#9Tbsheff wants to merge 8 commits intovercel-labs:mainfrom
Conversation
New first-class `idp` service plugin providing a local enterprise identity provider for end-to-end testing of OIDC and SAML SSO flows. OIDC Provider: - OpenID Connect discovery, JWKS, authorize, token, userinfo, revoke, logout - RS256 JWT signing with auto-generated or seeded RSA keys - Authorization code flow with PKCE (S256/plain) - Refresh token support with rotation (offline_access scope) - Custom claim mappings via dot-path resolution - client_secret_basic and client_secret_post authentication - Browser sign-in picker matching existing emulate UX SAML 2.0 IdP: - IdP metadata endpoint with X.509 signing certificate - SP-initiated SSO with HTTP-Redirect binding - Signed SAML assertions (RSA-SHA256) via xml-crypto - Auto-submit POST form to Assertion Consumer Service - Microsoft Entra ID claim URI presets (nameidentifier, emailaddress, etc.) - Configurable attribute_mappings and name_id_format per service provider - RelayState roundtrip support Shared: - Strict mode: enforces client/SP validation, requires PKCE, disables debug - Seeded users with groups, roles, and custom attributes - Debug endpoint for inspecting sessions, codes, and tokens - CLI integration: start, list, init, programmatic API - Zero-config boot with sensible defaults Dependencies: - xml-crypto ^6 (XML signing with Exclusive C14N) - @peculiar/x509 ^2 (self-signed X.509 certificate generation) Testing: - 67 plugin tests (TDD: crypto, seed, OIDC integration, SAML integration) - All 160+ monorepo tests passing
|
@Tbsheff is attempting to deploy a commit to the Vercel Labs Team on Vercel. A member of the Team first needs to authorize it. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
Pull request overview
Adds a new first-class idp service plugin to the emulate toolchain, providing an offline enterprise Identity Provider emulator with OIDC/OAuth2 and SAML2 flows for E2E testing.
Changes:
- Introduces
@internal/idppackage implementing OIDC endpoints (discovery/JWKS/authorize/token/userinfo/revoke/logout/debug) and SAML endpoints (metadata/SSO/callback) with signing/cert generation. - Wires the new
idpplugin into CLI (start,list,init) and programmatic API (createEmulator), plus adds a basic API test. - Updates bundling (tsup) and lockfile to accommodate new dependencies.
Reviewed changes
Copilot reviewed 22 out of 23 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Adds @peculiar/x509, xml-crypto, @xmldom/xmldom and related transitive deps; also includes unrelated peer-resolution churn. |
| packages/emulate/tsup.config.ts | Adjusts tsup config (node platform + banner shim) for Node/CJS interop in ESM bundles. |
| packages/emulate/src/commands/start.ts | Registers idp service plugin and seeds it from config. |
| packages/emulate/src/commands/list.ts | Adds idp to the CLI service listing. |
| packages/emulate/src/commands/init.ts | Adds default seed template for idp service. |
| packages/emulate/src/api.ts | Adds idp to createEmulator programmatic API and seeding. |
| packages/emulate/src/tests/api.test.ts | Adds a smoke test for IdP discovery advertising RS256. |
| packages/emulate/package.json | Adds @internal/idp workspace dependency. |
| packages/@internal/idp/tsup.config.ts | Build configuration for the new internal IdP package. |
| packages/@internal/idp/tsconfig.json | TypeScript config for the new IdP package. |
| packages/@internal/idp/src/store.ts | Defines typed collections for IdP entities in the core Store. |
| packages/@internal/idp/src/saml-xml.ts | SAML XML builders + XML-DSig signing + auto-post HTML form. |
| packages/@internal/idp/src/saml-constants.ts | SAML constants and Entra ID default claim URI mappings. |
| packages/@internal/idp/src/routes/saml.ts | Implements /saml/metadata, /saml/sso, /saml/sso/callback. |
| packages/@internal/idp/src/routes/oidc.ts | Implements OIDC/OAuth endpoints including token/refresh/revoke/userinfo/logout/debug. |
| packages/@internal/idp/src/index.ts | Exposes plugin entrypoint and seed schema/logic. |
| packages/@internal/idp/src/helpers.ts | Strict-mode/issuer helpers + ephemeral in-store maps for codes/tokens/sessions/SAML requests. |
| packages/@internal/idp/src/entities.ts | Defines IdP entity types: users, groups, clients, signing keys, SPs. |
| packages/@internal/idp/src/crypto.ts | RSA keygen/JWK export, JWT signing, PKCE, X.509 self-signed cert generation, dot-path resolution. |
| packages/@internal/idp/src/tests/saml.test.ts | Unit/integration tests for SAML metadata/SSO/signing/form/mappings. |
| packages/@internal/idp/src/tests/idp.test.ts | OIDC integration tests (auth code, PKCE, refresh rotation, revoke, debug, JWKS verification). |
| packages/@internal/idp/src/tests/crypto.test.ts | Unit tests for keygen/import, JWT signing, path resolution, PKCE. |
| packages/@internal/idp/package.json | Declares the new internal package and its runtime/test deps. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Fix require() in ESM module — use static import - Validate redirect_uri before constructing URL (prevents 500) - Validate redirect_uri and client_id match in token exchange - Strict mode requires PKCE code_challenge - Fix TokenMap id type (use entity id, not uid string) - Validate client_id on refresh token exchange - Escape clientName in HTML output (XSS prevention) - XML-escape all interpolated SAML response parameters - Add xs namespace declaration for xsi:type attribute - Add TTL expiry check for pending SAML requests - Strict mode rejects unknown SPs even when none configured - Update service list label to include SAML
- Type grant handler context as Context instead of any - Add comment explaining reflect-metadata import - Add test: JSON Content-Type on /token endpoint - Add test: unsupported_grant_type rejection - Add test: invalid SAML request ref rejection - Use timingSafeEqual for PKCE verification
…d code - Add eviction cap on revokedTokens and tokenClients (10k max) - Remove dead sessions code (never written to) - Validate response_type=code in /authorize (reject unsupported types) - Wrap new URL() in try/catch in /logout endpoint - Fix client_id binding: reject mismatch when code was bound to a client - Only issue refresh token on refresh grant if offline_access in scope - HTML-escape user inputs in error messages (XSS prevention) - Remove dead getCertificatePem from crypto.ts - Move @xmldom/xmldom to devDependencies - Consolidate duplicate @internal/core imports in saml.ts
|
Thanks for this excellent contribution — the scope and quality here is really impressive. I found a few issues I'd like addressed before merging:
Everything else — SAML flow, sign-in picker, OIDC discovery, JWKS, build — works great. Happy to re-review once these are addressed! |
1. Regenerate lockfile to fix frozen-lockfile CI failures 2. Check revoked tokens in /userinfo so revocation is enforced 3. Return error for unknown uid instead of silently falling back to first user 4. Update all tests to use real user uids from the store 5. Add /logout endpoint test coverage (5 cases) 6. Add test verifying revoked tokens are rejected at /userinfo
|
Thanks for the thorough review! All 5 items addressed in commit 28a4fb2:
|
…idp-plugin # Conflicts: # packages/emulate/package.json # packages/emulate/src/api.ts # packages/emulate/src/commands/init.ts # packages/emulate/src/commands/start.ts # pnpm-lock.yaml
- CRITICAL: Remove SAML user fallback (auth bypass) — unknown uid now returns error - Add memory cap on pendingSamlRequests and pendingCodes maps - Increase eviction threshold to 50k with safety-valve documentation - Add test for response_type validation - Add test for PKCE strict mode enforcement - Document intentional ID token claim inclusion
…idp-plugin # Conflicts: # pnpm-lock.yaml
Summary
Add a new first-class
idpservice plugin that acts as a local enterprise identity provider for end-to-end testing of OIDC/OAuth 2.0 and SAML 2.0 authentication flows entirely offline.Motivation
Teams integrating enterprise login (SSO) need to test real auth behavior locally and in CI without network access. The existing Google plugin is a lightweight OAuth/OIDC shim (HS256, stub JWKS) — not suitable for enterprise auth testing. This plugin provides a production-grade local IdP with asymmetric signing, proper JWKS, refresh tokens, SAML assertions, and configurable claim mappings.
What's included
OIDC / OAuth 2.0 Provider (9 endpoints)
GET /.well-known/openid-configurationGET /jwks.jsonGET /authorizePOST /authorize/callbackPOST /tokenauthorization_code+refresh_tokengrants)GET /userinfoPOST /revokeGET /logoutGET /_debug/stateKey features:
offline_accessscope)"attributes.department")client_secret_basicandclient_secret_postauthenticationSAML 2.0 Identity Provider (3 endpoints)
GET /saml/metadataGET /saml/ssoPOST /saml/sso/callbackKey features:
xml-crypto@peculiar/x509attribute_mappingsandname_id_formatper service providerShared capabilities
emulate --service idp,emulate list,emulate init --service idpcreateEmulator({ service: "idp" })New dependencies
xml-crypto^6@peculiar/x509^2Both are pure JS with zero native dependencies.
Usage
CLI
Programmatic (Vitest/Jest)
Seed config (YAML)
Testing
Files
New package:
packages/@internal/idp/src/entities.ts— IdpUser, IdpGroup, IdpClient, IdpSigningKey, IdpServiceProvidersrc/store.ts— Typed collections with field indexessrc/crypto.ts— RSA keygen, RS256 JWT signing, X.509 cert generation, PKCEsrc/helpers.ts— Ephemeral data accessors, utility functionssrc/saml-constants.ts— Entra ID claim URIs, SAML namespace constantssrc/saml-xml.ts— XML builders for metadata, response, signing, auto-post formsrc/routes/oidc.ts— 9 OIDC endpointssrc/routes/saml.ts— 3 SAML endpointssrc/index.ts— Plugin export, seed config, seed functionsModified files
packages/emulate/src/commands/start.ts— Register idp in SERVICE_PLUGINSpackages/emulate/src/api.ts— Add idp to programmatic APIpackages/emulate/src/commands/list.ts— Add idp to service descriptionspackages/emulate/src/commands/init.ts— Add idp default config templatepackages/emulate/package.json— Add @internal/idp workspace dependencypackages/emulate/tsup.config.ts— Fix ESM bundle for CJS deps using Node built-ins