diff --git a/apps/e2e/tests/backend/endpoints/api/v1/auth/saml/discover.test.ts b/apps/e2e/tests/backend/endpoints/api/v1/auth/saml/discover.test.ts new file mode 100644 index 000000000..a6dcf2897 --- /dev/null +++ b/apps/e2e/tests/backend/endpoints/api/v1/auth/saml/discover.test.ts @@ -0,0 +1,97 @@ +import { it } from "../../../../../../helpers"; +import { Project, niceBackendFetch } from "../../../../../backend-helpers"; + +/** + * Tests for GET /auth/saml/discover. + * + * Test integrity: drives the API only — no imports from + * apps/backend/src/saml/. Project config is set via the standard config + * override endpoint (no special test-only mutator), so the discovery + * lookup runs through the same code path real customers would hit. + * + * Connection isolation note: each `it` block creates its own project via + * Project.createAndSwitch, so connections don't leak across tests. + */ + +async function createProjectWithSamlConnection(slug: string, domain: string) { + const { projectId } = await Project.createAndSwitch(); + // Push the SAML connection at the environment level — that's where the + // IdP-side fields live. The discovery endpoint reads from the rendered + // organization config which folds in env overrides. + await Project.updateConfig({ + [`auth.saml.connections.${slug}.displayName`]: `${slug} SSO`, + [`auth.saml.connections.${slug}.allowSignIn`]: true, + [`auth.saml.connections.${slug}.domain`]: domain, + [`auth.saml.connections.${slug}.idpEntityId`]: `https://idp.${domain}/saml/metadata`, + [`auth.saml.connections.${slug}.idpSsoUrl`]: `https://idp.${domain}/saml/sso`, + [`auth.saml.connections.${slug}.idpCertificate`]: "MIICertificatePlaceholderForDiscoveryTest=", + }); + return { projectId }; +} + +it("returns the matching connection for a known email domain", async ({ expect }) => { + const { projectId } = await createProjectWithSamlConnection("acme", "acme.test"); + + const response = await niceBackendFetch( + `/api/v1/auth/saml/discover?email=alice@acme.test&project_id=${projectId}`, + { method: "GET" }, + ); + + expect(response).toMatchInlineSnapshot(` + NiceResponse { + "status": 200, + "body": { + "connection_id": "acme", + "display_name": "acme SSO", + }, + "headers": Headers {