chore(backend): defer SAML seed + node-saml dep to stacked backend PR

Removes seedSamlConnections (and its STACK_SEED_ENABLE_SAML callsite)
plus the @node-saml/node-saml dependency from this PR. Both depend on
config.auth.saml schema entries that don't exist on this branch yet —
the seed wrote overrides that were silently dropped during config
normalization, and node-saml had no consumer here.

They land together in the stacked backend PR alongside the schema and
the SAML protocol wrapper that actually imports node-saml.
This commit is contained in:
Bilal Godil 2026-04-30 14:45:48 -07:00
parent 77e4fae463
commit 6b8cd7e564
3 changed files with 6 additions and 138 deletions

View File

@ -61,7 +61,6 @@
"@aws-sdk/client-s3": "^3.855.0",
"@clickhouse/client": "^1.14.0",
"@node-oauth/oauth2-server": "^5.1.0",
"@node-saml/node-saml": "^5.0.0",
"@openrouter/ai-sdk-provider": "2.2.3",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/api-logs": "^0.53.0",

View File

@ -1939,69 +1939,6 @@ async function seedBulkSignupsAndActivity(options: {
console.log(`[seed-activity] Events: $token-refresh=${tokenRefreshCount} $page-view=${pageViewCount} $click=${clickCount} total=${clickhouseRows.length}`);
}
/**
* Pre-creates two SAML connections (acme + globex) on the dummy project that
* point at the local mock SAML IdP. Gated on STACK_SEED_ENABLE_SAML='true'.
* Fetches the mock IdP's metadata at seed time so the seeded cert matches
* the cert the mock generated at startup the mock currently regenerates
* keys per restart, so re-seed if you restart the mock.
*/
async function seedSamlConnections(projectId: string): Promise<void> {
const mockUrl = getEnvVariable("STACK_MOCK_SAML_URL", "http://localhost:8115");
const tenants: Array<{ slug: string, displayName: string, domain: string }> = [
{ slug: "acme", displayName: "Acme Corp SSO", domain: "acme.test" },
{ slug: "globex", displayName: "Globex SAML", domain: "globex.test" },
];
const fetched = await Promise.all(
tenants.map(async (t) => {
const res = await fetch(`${mockUrl}/idp/${t.slug}/metadata`);
if (!res.ok) {
throw new Error(`Mock SAML IdP at ${mockUrl}/idp/${t.slug}/metadata returned ${res.status} — is the mock running?`);
}
const xml = await res.text();
// Inline minimal metadata parse to avoid a circular import. Format is
// exactly what the mock emits, so a regex is enough; the production
// parser at apps/backend/src/saml/metadata-parser.tsx is the
// robust one used by the dashboard "paste metadata" form.
const entityIdMatch = xml.match(/entityID="([^"]+)"/);
const ssoUrlMatch = xml.match(/Binding="urn:oasis:names:tc:SAML:2\.0:bindings:HTTP-Redirect"[^>]*Location="([^"]+)"/);
const certMatch = xml.match(/<X509Certificate>([\s\S]+?)<\/X509Certificate>/);
if (!entityIdMatch || !ssoUrlMatch || !certMatch) {
throw new Error(`Could not parse mock IdP metadata for tenant ${t.slug}`);
}
return {
...t,
idpEntityId: entityIdMatch[1],
idpSsoUrl: ssoUrlMatch[1],
idpCertificate: certMatch[1].replace(/\s+/g, ""),
};
}),
);
// Set the entire connection entry as a single value, not as deep
// dot-keys — config normalization with onDotIntoNonObject="ignore"
// drops dot-keys that try to navigate into a record entry that
// doesn't yet exist (same convention as auth.oauth.providers).
const overlay: Parameters<typeof overrideEnvironmentConfigOverride>[0]["environmentConfigOverrideOverride"] = {};
for (const f of fetched) {
overlay[`auth.saml.connections.${f.slug}`] = {
displayName: f.displayName,
allowSignIn: true,
domain: f.domain,
idpEntityId: f.idpEntityId,
idpSsoUrl: f.idpSsoUrl,
idpCertificate: f.idpCertificate,
};
}
await overrideEnvironmentConfigOverride({
projectId,
branchId: DEFAULT_BRANCH_ID,
environmentConfigOverrideOverride: overlay,
});
}
/**
* Creates a new project and fills it with dummy data (users, teams, payments, emails, analytics events).
* Used by both the seed script and the preview project creation endpoint.
@ -2153,16 +2090,6 @@ export async function seedDummyProject(options: SeedDummyProjectOptions): Promis
}),
]);
// Run sequentially after the parallel block. Both this and the
// `payments.testMode` write above target the same environment config,
// and `overrideEnvironmentConfigOverride` is read-modify-write — running
// them in parallel races and one write clobbers the other (TODO at
// config.ts:491 already documents this). Sequencing avoids the race
// until the underlying override is wrapped in a serializable txn.
if (getEnvVariable("STACK_SEED_ENABLE_SAML", "false") === "true") {
await seedSamlConnections(projectId);
}
await seedDummyTransactions({
prisma: dummyPrisma,
tenancyId: dummyTenancy.id,

View File

@ -126,9 +126,6 @@ importers:
'@node-oauth/oauth2-server':
specifier: ^5.1.0
version: 5.1.0
'@node-saml/node-saml':
specifier: ^5.0.0
version: 5.1.0
'@openrouter/ai-sdk-provider':
specifier: 2.2.3
version: 2.2.3(ai@6.0.81(zod@3.25.76))(zod@3.25.76)
@ -870,12 +867,6 @@ importers:
apps/mock-saml-idp:
dependencies:
'@types/express':
specifier: ^5.0.0
version: 5.0.0
'@types/node-forge':
specifier: ^1.3.11
version: 1.3.14
express:
specifier: ^4.21.2
version: 4.21.2
@ -889,6 +880,12 @@ importers:
specifier: ^2.10.0
version: 2.12.0
devDependencies:
'@types/express':
specifier: ^5.0.0
version: 5.0.0
'@types/node-forge':
specifier: ^1.3.11
version: 1.3.14
tsx:
specifier: ^4.16.2
version: 4.21.0
@ -5989,10 +5986,6 @@ packages:
resolution: {integrity: sha512-sYvqL1GeZLRSwgl++/oOzxJj/ZBe2yXnp6E5LGNQ5qjpn0+t/dwquXILUe3Sk2Y8/wU7XeRxToOtBVeSVkuJag==}
engines: {node: '>=16.0.0'}
'@node-saml/node-saml@5.1.0':
resolution: {integrity: sha512-t3cJnZ4aC7HhPZ6MGylGZULvUtBOZ6FzuUndaHGXjmIZHXnLfC/7L8a57O9Q9V7AxJGKAiRM5zu2wNm9EsvQpw==}
engines: {node: '>= 18'}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@ -10579,9 +10572,6 @@ packages:
'@types/qrcode@1.5.5':
resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
'@types/qs@6.15.0':
resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==}
'@types/qs@6.9.15':
resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==}
@ -10654,12 +10644,6 @@ packages:
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
'@types/xml-encryption@1.2.4':
resolution: {integrity: sha512-I69K/WW1Dv7j6O3jh13z0X8sLWJRXbu5xnHDl9yHzUNDUBtUoBY058eb5s+x/WG6yZC1h8aKdI2EoyEPjyEh+Q==}
'@types/xml2js@0.4.14':
resolution: {integrity: sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==}
'@types/yauzl@2.10.3':
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
@ -19408,9 +19392,6 @@ packages:
resolution: {integrity: sha512-leBOVQdVi8FvPJrMYoum7Ici9qyxfE4kVi+AkpUoYCSXaQF4IlBm1cneTK9oAxR61LpYxTx7lNcsnBIeRpGW2w==}
engines: {node: '>=16'}
xml-encryption@3.1.0:
resolution: {integrity: sha512-PV7qnYpoAMXbf1kvQkqMScLeQpjCMixddAKq9PtqVrho8HnYbBOWNfG0kA4R7zxQDo7w9kiYAyzS/ullAyO55Q==}
xml-escape@1.1.0:
resolution: {integrity: sha512-B/T4sDK8Z6aUh/qNr7mjKAwwncIljFuUP+DO/D5hloYFj+90O88z8Wf7oSucZTHxBAsC1/CTP4rtx/x1Uf72Mg==}
@ -19437,10 +19418,6 @@ packages:
resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
engines: {node: '>=4.0'}
xmlbuilder@15.1.1:
resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
engines: {node: '>=8.0'}
xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
@ -23801,23 +23778,6 @@ snapshots:
basic-auth: 2.0.1
type-is: 1.6.18
'@node-saml/node-saml@5.1.0':
dependencies:
'@types/debug': 4.1.12
'@types/qs': 6.15.0
'@types/xml-encryption': 1.2.4
'@types/xml2js': 0.4.14
'@xmldom/is-dom-node': 1.0.1
'@xmldom/xmldom': 0.8.13
debug: 4.4.3
xml-crypto: 6.1.2
xml-encryption: 3.1.0
xml2js: 0.6.2
xmlbuilder: 15.1.1
xpath: 0.0.34
transitivePeerDependencies:
- supports-color
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@ -30420,8 +30380,6 @@ snapshots:
dependencies:
'@types/node': 22.19.0
'@types/qs@6.15.0': {}
'@types/qs@6.9.15': {}
'@types/range-parser@1.2.7': {}
@ -30508,14 +30466,6 @@ snapshots:
dependencies:
'@types/node': 22.19.0
'@types/xml-encryption@1.2.4':
dependencies:
'@types/node': 22.19.0
'@types/xml2js@0.4.14':
dependencies:
'@types/node': 22.19.0
'@types/yauzl@2.10.3':
dependencies:
'@types/node': 22.19.0
@ -42025,12 +41975,6 @@ snapshots:
'@xmldom/xmldom': 0.8.13
xpath: 0.0.33
xml-encryption@3.1.0:
dependencies:
'@xmldom/xmldom': 0.8.13
escape-html: 1.0.3
xpath: 0.0.32
xml-escape@1.1.0: {}
xml-js@1.6.11:
@ -42055,8 +41999,6 @@ snapshots:
xmlbuilder@11.0.1: {}
xmlbuilder@15.1.1: {}
xmlchars@2.2.0: {}
xpath@0.0.32: {}