seamless-cli 0.2.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +5 -6
  2. package/dist/commands/help.js +18 -1
  3. package/dist/commands/help.js.map +1 -1
  4. package/dist/commands/init.js +104 -21
  5. package/dist/commands/init.js.map +1 -1
  6. package/dist/commands/verify.js +259 -0
  7. package/dist/commands/verify.js.map +1 -0
  8. package/dist/core/bootstrapSecret.js +12 -4
  9. package/dist/core/bootstrapSecret.js.map +1 -1
  10. package/dist/core/exec.js +2 -1
  11. package/dist/core/exec.js.map +1 -1
  12. package/dist/core/fetch.js +2 -1
  13. package/dist/core/fetch.js.map +1 -1
  14. package/dist/core/images.js +11 -0
  15. package/dist/core/images.js.map +1 -0
  16. package/dist/core/oauthProviders.js +89 -0
  17. package/dist/core/oauthProviders.js.map +1 -0
  18. package/dist/core/templates.js +185 -0
  19. package/dist/core/templates.js.map +1 -0
  20. package/dist/generators/auth/auth.js +40 -19
  21. package/dist/generators/auth/auth.js.map +1 -1
  22. package/dist/generators/config/config.js +3 -4
  23. package/dist/generators/config/config.js.map +1 -1
  24. package/dist/generators/docker/docker.js +51 -31
  25. package/dist/generators/docker/docker.js.map +1 -1
  26. package/dist/index.js +11 -2
  27. package/dist/index.js.map +1 -1
  28. package/dist/prompts/oauthSetup.js +36 -0
  29. package/dist/prompts/oauthSetup.js.map +1 -0
  30. package/dist/prompts/projectSetup.js +43 -19
  31. package/dist/prompts/projectSetup.js.map +1 -1
  32. package/package.json +6 -1
  33. package/verify/adapter-app/Dockerfile +14 -0
  34. package/verify/adapter-app/package.json +13 -0
  35. package/verify/adapter-app/server.mjs +62 -0
  36. package/verify/adapter-app/vendor/.gitignore +3 -0
  37. package/verify/adapter-app/vendor/.gitkeep +0 -0
  38. package/verify/docker-compose.verify.yml +154 -0
  39. package/verify/harness/adapter/emailOtpLogin.spec.ts +12 -0
  40. package/verify/harness/adapter/magicLink.spec.ts +14 -0
  41. package/verify/harness/adapter/oauth.spec.ts +11 -0
  42. package/verify/harness/adapter/registration.spec.ts +11 -0
  43. package/verify/harness/adapter/sessionLifecycle.spec.ts +20 -0
  44. package/verify/harness/api/adminBootstrap.spec.ts +34 -0
  45. package/verify/harness/api/emailOtp.spec.ts +17 -0
  46. package/verify/harness/api/jwks.spec.ts +17 -0
  47. package/verify/harness/api/magicLink.spec.ts +36 -0
  48. package/verify/harness/api/oauth.spec.ts +12 -0
  49. package/verify/harness/api/organizations.spec.ts +28 -0
  50. package/verify/harness/api/phoneOtp.spec.ts +19 -0
  51. package/verify/harness/api/sessionLifecycle.spec.ts +26 -0
  52. package/verify/harness/api/stepUp.spec.ts +39 -0
  53. package/verify/harness/api/totp.spec.ts +24 -0
  54. package/verify/harness/global-setup.ts +45 -0
  55. package/verify/harness/lib/adapterFlows.ts +57 -0
  56. package/verify/harness/lib/client.ts +32 -0
  57. package/verify/harness/lib/env.ts +45 -0
  58. package/verify/harness/lib/fixtures.ts +20 -0
  59. package/verify/harness/lib/flows.ts +204 -0
  60. package/verify/harness/lib/matrixReporter.ts +73 -0
  61. package/verify/harness/lib/reactFlows.ts +117 -0
  62. package/verify/harness/lib/serviceToken.ts +26 -0
  63. package/verify/harness/lib/totp.ts +36 -0
  64. package/verify/harness/mock-oidc.ts +112 -0
  65. package/verify/harness/package-lock.json +93 -0
  66. package/verify/harness/package.json +14 -0
  67. package/verify/harness/playwright.config.ts +30 -0
  68. package/verify/harness/react/emailOtpLogin.spec.ts +13 -0
  69. package/verify/harness/react/logout.spec.ts +17 -0
  70. package/verify/harness/react/magicLinkLogin.spec.ts +29 -0
  71. package/verify/harness/react/oauth.spec.ts +13 -0
  72. package/verify/harness/react/passkeyLogin.spec.ts +24 -0
  73. package/verify/harness/react/passkeyRegister.spec.ts +15 -0
  74. package/verify/harness/react/register.spec.ts +18 -0
  75. package/verify/react-vendor/.gitkeep +0 -0
  76. package/dist/commands/contribute.js +0 -121
  77. package/dist/commands/contribute.js.map +0 -1
  78. package/dist/commands/deploy.js +0 -190
  79. package/dist/commands/deploy.js.map +0 -1
  80. package/dist/commands/destroy.js +0 -122
  81. package/dist/commands/destroy.js.map +0 -1
  82. package/dist/contributor/catalog.js +0 -129
  83. package/dist/contributor/catalog.js.map +0 -1
  84. package/dist/contributor/workspace.js +0 -551
  85. package/dist/contributor/workspace.js.map +0 -1
  86. package/dist/core/acm.js +0 -24
  87. package/dist/core/acm.js.map +0 -1
  88. package/dist/core/aws.js +0 -374
  89. package/dist/core/aws.js.map +0 -1
  90. package/dist/core/buildPush.js +0 -91
  91. package/dist/core/buildPush.js.map +0 -1
  92. package/dist/core/configure.js +0 -25
  93. package/dist/core/configure.js.map +0 -1
  94. package/dist/core/deployConfig.js +0 -66
  95. package/dist/core/deployConfig.js.map +0 -1
  96. package/dist/core/deployManifest.js +0 -163
  97. package/dist/core/deployManifest.js.map +0 -1
  98. package/dist/core/deploySecrets.js +0 -31
  99. package/dist/core/deploySecrets.js.map +0 -1
  100. package/dist/core/destroy.js +0 -38
  101. package/dist/core/destroy.js.map +0 -1
  102. package/dist/core/envDiscovery.js +0 -153
  103. package/dist/core/envDiscovery.js.map +0 -1
  104. package/dist/core/envRegistry.js +0 -76
  105. package/dist/core/envRegistry.js.map +0 -1
  106. package/dist/core/health.js +0 -11
  107. package/dist/core/health.js.map +0 -1
  108. package/dist/core/help.js +0 -75
  109. package/dist/core/help.js.map +0 -1
  110. package/dist/core/preReqs.js +0 -14
  111. package/dist/core/preReqs.js.map +0 -1
  112. package/dist/core/terraform.js +0 -34
  113. package/dist/core/terraform.js.map +0 -1
  114. package/dist/core/tfVars.js +0 -69
  115. package/dist/core/tfVars.js.map +0 -1
  116. package/dist/generators/backend/express.js +0 -13
  117. package/dist/generators/backend/express.js.map +0 -1
  118. package/dist/generators/deploy/aws/dev/files.js +0 -36
  119. package/dist/generators/deploy/aws/dev/files.js.map +0 -1
  120. package/dist/generators/deploy/aws/dev/template.js +0 -1240
  121. package/dist/generators/deploy/aws/dev/template.js.map +0 -1
  122. package/dist/generators/frontend/react.js +0 -13
  123. package/dist/generators/frontend/react.js.map +0 -1
  124. package/dist/prompts/contributeSetup.js +0 -39
  125. package/dist/prompts/contributeSetup.js.map +0 -1
  126. package/dist/prompts/deploySetup.js +0 -244
  127. package/dist/prompts/deploySetup.js.map +0 -1
  128. package/dist/utils/repoUtils.js +0 -23
  129. package/dist/utils/repoUtils.js.map +0 -1
@@ -0,0 +1,11 @@
1
+ export const POSTGRES_IMAGE = "postgres:17";
2
+ export const SEAMLESS_AUTH_API_VERSION = "v0.2.1";
3
+ export const SEAMLESS_AUTH_API_IMAGE = `ghcr.io/fells-code/seamless-auth-api:${SEAMLESS_AUTH_API_VERSION}`;
4
+ export const SEAMLESS_AUTH_ADMIN_DASHBOARD_VERSION = "v0.1.0";
5
+ export const SEAMLESS_AUTH_ADMIN_DASHBOARD_IMAGE = `ghcr.io/fells-code/seamless-auth-admin-dashboard:${SEAMLESS_AUTH_ADMIN_DASHBOARD_VERSION}`;
6
+ // The starter templates monorepo the CLI scaffolds from. Pinned to a tag so a given
7
+ // CLI version always produces the same project. Override the ref with
8
+ // SEAMLESS_TEMPLATES_REF, or point at a local checkout with SEAMLESS_TEMPLATES_DIR.
9
+ export const SEAMLESS_TEMPLATES_REPO = "fells-code/seamless-templates";
10
+ export const SEAMLESS_TEMPLATES_REF = "v0.2.1";
11
+ //# sourceMappingURL=images.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"images.js","sourceRoot":"","sources":["../../src/core/images.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAC;AAE5C,MAAM,CAAC,MAAM,yBAAyB,GAAG,QAAQ,CAAC;AAElD,MAAM,CAAC,MAAM,uBAAuB,GAAG,wCAAwC,yBAAyB,EAAE,CAAC;AAE3G,MAAM,CAAC,MAAM,qCAAqC,GAAG,QAAQ,CAAC;AAE9D,MAAM,CAAC,MAAM,mCAAmC,GAAG,oDAAoD,qCAAqC,EAAE,CAAC;AAE/I,oFAAoF;AACpF,sEAAsE;AACtE,oFAAoF;AACpF,MAAM,CAAC,MAAM,uBAAuB,GAAG,+BAA+B,CAAC;AAEvE,MAAM,CAAC,MAAM,sBAAsB,GAAG,QAAQ,CAAC"}
@@ -0,0 +1,89 @@
1
+ import { generateSecret } from "./secrets.js";
2
+ // The web app's OAuth callback route. The scaffolded web container serves the app
3
+ // at :5173, and the react-oauth template redirects to /oauth/callback there.
4
+ const REDIRECT_URI = "http://localhost:5173/oauth/callback";
5
+ export const OAUTH_PROVIDER_CATALOG = [
6
+ {
7
+ id: "google",
8
+ label: "Google",
9
+ authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
10
+ tokenUrl: "https://oauth2.googleapis.com/token",
11
+ userInfoUrl: "https://openidconnect.googleapis.com/v1/userinfo",
12
+ scopes: ["openid", "email", "profile"],
13
+ pkce: true,
14
+ },
15
+ {
16
+ id: "github",
17
+ label: "GitHub",
18
+ authorizationUrl: "https://github.com/login/oauth/authorize",
19
+ tokenUrl: "https://github.com/login/oauth/access_token",
20
+ userInfoUrl: "https://api.github.com/user",
21
+ scopes: ["read:user", "user:email"],
22
+ extra: { subjectJsonPath: "id", nameJsonPath: "name" },
23
+ },
24
+ {
25
+ id: "microsoft",
26
+ label: "Microsoft",
27
+ authorizationUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
28
+ tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
29
+ userInfoUrl: "https://graph.microsoft.com/oidc/userinfo",
30
+ scopes: ["openid", "email", "profile"],
31
+ pkce: true,
32
+ },
33
+ {
34
+ id: "gitlab",
35
+ label: "GitLab",
36
+ authorizationUrl: "https://gitlab.com/oauth/authorize",
37
+ tokenUrl: "https://gitlab.com/oauth/token",
38
+ userInfoUrl: "https://gitlab.com/oauth/userinfo",
39
+ scopes: ["openid", "email", "profile"],
40
+ pkce: true,
41
+ },
42
+ ];
43
+ function secretEnvName(id) {
44
+ return `${id.toUpperCase().replace(/-/g, "_")}_CLIENT_SECRET`;
45
+ }
46
+ // Turns the chosen providers into the auth-server env: the OAUTH_PROVIDERS JSON, a
47
+ // per-provider client-secret env var, and OAUTH_STATE_SECRET. A provider missing
48
+ // either credential is scaffolded disabled (so the stack still boots) and reported
49
+ // in `pending` so the CLI can tell the user what to fill in.
50
+ export function buildOAuthAuthEnv(providers) {
51
+ const env = {};
52
+ const pending = [];
53
+ const configs = providers.map(({ catalog, clientId, clientSecret }) => {
54
+ const envName = secretEnvName(catalog.id);
55
+ const ready = clientId.length > 0 && clientSecret.length > 0;
56
+ if (!ready)
57
+ pending.push(catalog.label);
58
+ env[envName] = clientSecret;
59
+ return {
60
+ id: catalog.id,
61
+ name: catalog.label,
62
+ enabled: ready,
63
+ clientId: clientId || `REPLACE_WITH_${catalog.id.toUpperCase()}_CLIENT_ID`,
64
+ clientSecretEnv: envName,
65
+ authorizationUrl: catalog.authorizationUrl,
66
+ tokenUrl: catalog.tokenUrl,
67
+ userInfoUrl: catalog.userInfoUrl,
68
+ scopes: catalog.scopes,
69
+ redirectUri: REDIRECT_URI,
70
+ redirectUris: [REDIRECT_URI],
71
+ ...(catalog.pkce ? { pkce: true } : {}),
72
+ ...(catalog.extra ?? {}),
73
+ };
74
+ });
75
+ env.OAUTH_PROVIDERS = JSON.stringify(configs);
76
+ env.OAUTH_STATE_SECRET = generateSecret(32);
77
+ return { env, pending };
78
+ }
79
+ // Ensures a login method is present in a comma-separated LOGIN_METHODS value.
80
+ export function withLoginMethod(current, method) {
81
+ const methods = (current ?? "passkey,magic_link")
82
+ .split(",")
83
+ .map((m) => m.trim())
84
+ .filter(Boolean);
85
+ if (!methods.includes(method))
86
+ methods.push(method);
87
+ return methods.join(",");
88
+ }
89
+ //# sourceMappingURL=oauthProviders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthProviders.js","sourceRoot":"","sources":["../../src/core/oauthProviders.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,185 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import AdmZip from "adm-zip";
4
+ import { VERSION } from "../index.js";
5
+ import { parseEnvString, writeEnv } from "./env.js";
6
+ import { generateSecret } from "./secrets.js";
7
+ import { SEAMLESS_TEMPLATES_REF, SEAMLESS_TEMPLATES_REPO } from "./images.js";
8
+ // Build artifacts and local-only files that must never be copied into a scaffold,
9
+ // even if a local template checkout has them lying around.
10
+ const IGNORED_NAMES = new Set([
11
+ "node_modules",
12
+ ".git",
13
+ "dist",
14
+ "build",
15
+ ".next",
16
+ "out",
17
+ "coverage",
18
+ ".DS_Store",
19
+ ".env",
20
+ ".env.local",
21
+ ]);
22
+ export async function openTemplateSource() {
23
+ const localDir = process.env.SEAMLESS_TEMPLATES_DIR;
24
+ if (localDir) {
25
+ return openLocalSource(path.resolve(localDir));
26
+ }
27
+ return openRemoteSource(SEAMLESS_TEMPLATES_REPO, process.env.SEAMLESS_TEMPLATES_REF ?? SEAMLESS_TEMPLATES_REF);
28
+ }
29
+ function readRegistry(raw) {
30
+ const registry = JSON.parse(raw);
31
+ if (!registry || !Array.isArray(registry.templates)) {
32
+ throw new Error("Template registry is malformed (missing templates array).");
33
+ }
34
+ return registry;
35
+ }
36
+ function openLocalSource(dir) {
37
+ const registryPath = path.join(dir, "registry.json");
38
+ if (!fs.existsSync(registryPath)) {
39
+ throw new Error(`SEAMLESS_TEMPLATES_DIR is set to ${dir}, but no registry.json was found there.`);
40
+ }
41
+ const registry = readRegistry(fs.readFileSync(registryPath, "utf-8"));
42
+ return {
43
+ registry,
44
+ async readManifest(entry) {
45
+ const manifestPath = path.join(dir, entry.path, "template.json");
46
+ return JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
47
+ },
48
+ async copyInto(entry, destDir) {
49
+ copyDir(path.join(dir, entry.path), destDir);
50
+ },
51
+ };
52
+ }
53
+ async function openRemoteSource(repo, ref) {
54
+ const registryUrl = `https://raw.githubusercontent.com/${repo}/${ref}/registry.json`;
55
+ const res = await fetch(registryUrl);
56
+ if (!res.ok) {
57
+ throw new Error(`Failed to fetch the template registry (${res.status}) from ${registryUrl}.`);
58
+ }
59
+ const registry = readRegistry(await res.text());
60
+ // The whole monorepo is downloaded once, lazily, and shared across every template
61
+ // a single scaffold pulls in.
62
+ let archive = null;
63
+ const ensureArchive = async () => {
64
+ if (archive)
65
+ return archive;
66
+ const url = `https://github.com/${repo}/archive/${ref}.zip`;
67
+ const zipRes = await fetch(url);
68
+ if (!zipRes.ok) {
69
+ throw new Error(`Failed to download templates (${zipRes.status}) from ${url}.`);
70
+ }
71
+ const zip = new AdmZip(Buffer.from(await zipRes.arrayBuffer()));
72
+ const first = zip.getEntries()[0];
73
+ if (!first) {
74
+ throw new Error("Downloaded templates archive was empty.");
75
+ }
76
+ // GitHub archives nest everything under a single top-level directory.
77
+ const root = first.entryName.split("/")[0];
78
+ archive = { zip, root };
79
+ return archive;
80
+ };
81
+ return {
82
+ registry,
83
+ async readManifest(entry) {
84
+ const { zip, root } = await ensureArchive();
85
+ const name = `${root}/${entry.path}/template.json`;
86
+ const found = zip.getEntry(name);
87
+ if (!found) {
88
+ throw new Error(`Template "${entry.id}" is missing template.json in the registry.`);
89
+ }
90
+ return JSON.parse(found.getData().toString("utf-8"));
91
+ },
92
+ async copyInto(entry, destDir) {
93
+ const { zip, root } = await ensureArchive();
94
+ const prefix = `${root}/${entry.path}/`;
95
+ for (const e of zip.getEntries()) {
96
+ if (e.isDirectory || !e.entryName.startsWith(prefix))
97
+ continue;
98
+ const rel = e.entryName.slice(prefix.length);
99
+ if (rel.split("/").some((seg) => IGNORED_NAMES.has(seg)))
100
+ continue;
101
+ const out = path.join(destDir, rel);
102
+ fs.mkdirSync(path.dirname(out), { recursive: true });
103
+ fs.writeFileSync(out, e.getData());
104
+ }
105
+ },
106
+ };
107
+ }
108
+ function copyDir(src, dest) {
109
+ fs.mkdirSync(dest, { recursive: true });
110
+ for (const name of fs.readdirSync(src)) {
111
+ if (IGNORED_NAMES.has(name))
112
+ continue;
113
+ const from = path.join(src, name);
114
+ const to = path.join(dest, name);
115
+ const stat = fs.statSync(from);
116
+ if (stat.isDirectory()) {
117
+ copyDir(from, to);
118
+ }
119
+ else if (stat.isFile()) {
120
+ fs.copyFileSync(from, to);
121
+ }
122
+ }
123
+ }
124
+ export function assertCliSupports(manifest, label) {
125
+ const min = manifest.requires?.cliMin;
126
+ if (min && compareSemver(VERSION, min) < 0) {
127
+ throw new Error(`Template "${label}" requires seamless-cli >= ${min}, but this is ${VERSION}. Please upgrade.`);
128
+ }
129
+ }
130
+ // Copies the template's example env to .env (if declared), then applies the
131
+ // manifest's env.set, resolving {{placeholders}} against the scaffold context.
132
+ // This replaces the per-framework configure step.
133
+ export function applyTemplateEnv(destDir, manifest, ctx) {
134
+ const env = manifest.env ?? {};
135
+ const envPath = path.join(destDir, ".env");
136
+ let values = {};
137
+ if (env.fromExample) {
138
+ const examplePath = path.join(destDir, env.fromExample);
139
+ if (fs.existsSync(examplePath)) {
140
+ values = parseEnvString(fs.readFileSync(examplePath, "utf-8"));
141
+ }
142
+ }
143
+ else if (fs.existsSync(envPath)) {
144
+ values = parseEnvString(fs.readFileSync(envPath, "utf-8"));
145
+ }
146
+ for (const [key, raw] of Object.entries(env.set ?? {})) {
147
+ values[key] = resolvePlaceholders(raw, ctx);
148
+ }
149
+ writeEnv(envPath, values);
150
+ }
151
+ function resolvePlaceholders(value, ctx) {
152
+ return value.replace(/\{\{\s*([\w:]+)\s*\}\}/g, (_match, token) => resolveToken(token, ctx));
153
+ }
154
+ function resolveToken(token, ctx) {
155
+ const secret = /^secret:(\d+)$/.exec(token);
156
+ if (secret) {
157
+ return generateSecret(Number(secret[1]));
158
+ }
159
+ const known = {
160
+ authServerUrl: ctx.authServerUrl,
161
+ apiUrl: ctx.apiUrl,
162
+ apiToken: ctx.apiToken,
163
+ jwksKid: ctx.jwksKid,
164
+ };
165
+ if (token in known) {
166
+ const resolved = known[token];
167
+ if (resolved == null) {
168
+ throw new Error(`Template placeholder {{${token}}} has no value in this configuration.`);
169
+ }
170
+ return resolved;
171
+ }
172
+ throw new Error(`Unknown template placeholder {{${token}}}.`);
173
+ }
174
+ function compareSemver(a, b) {
175
+ const norm = (v) => v.replace(/^v/, "").split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
176
+ const pa = norm(a);
177
+ const pb = norm(b);
178
+ for (let i = 0; i < 3; i++) {
179
+ const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
180
+ if (diff !== 0)
181
+ return diff < 0 ? -1 : 1;
182
+ }
183
+ return 0;
184
+ }
185
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/core/templates.ts"],"names":[],"mappings":"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"}
@@ -1,47 +1,68 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
+ import { parseEnvString } from "../../core/env.js";
3
4
  import { runCommand } from "../../core/exec.js";
4
- import { writeEnv } from "../../utils/writeEnv.js";
5
+ import { fetchEnvExample } from "../../core/fetch.js";
6
+ import { POSTGRES_IMAGE, SEAMLESS_AUTH_API_IMAGE } from "../../core/images.js";
7
+ import { buildAuthEnv, configureAuthLocalEnv, envToDockerBlock, } from "../docker/docker.js";
5
8
  const AUTH_REPO = "https://github.com/fells-code/seamless-auth-api";
6
- const AUTH_PORT = 5312;
7
- export async function generateAuthServer(context, mode) {
9
+ export async function generateAuthServer(context, mode, oauth = []) {
8
10
  const { root } = context;
9
11
  if (mode === "local") {
10
- await setupLocalAuth(root);
11
- }
12
- else {
13
- await setupDockerAuth(root);
12
+ return await setupLocalAuth(root, oauth);
14
13
  }
14
+ return await setupDockerAuth(root);
15
15
  }
16
- async function setupLocalAuth(root) {
16
+ async function setupLocalAuth(root, oauth = []) {
17
17
  const authDir = path.join(root, "auth");
18
18
  console.log("Cloning SeamlessAuth server...");
19
19
  await runCommand("git", ["clone", AUTH_REPO, "auth"], root);
20
20
  console.log("Writing auth environment...");
21
- writeEnv(authDir, {
22
- PORT: AUTH_PORT,
23
- NODE_ENV: "development",
24
- AUTH_MODE: "server",
25
- ISSUER: `http://localhost:${AUTH_PORT}`,
26
- });
21
+ const shared = await configureAuthLocalEnv(root, oauth);
27
22
  console.log("Auth server ready in /auth");
23
+ return shared;
28
24
  }
29
25
  async function setupDockerAuth(root) {
30
26
  console.log("Creating docker-compose for SeamlessAuth...");
27
+ const raw = await fetchEnvExample();
28
+ const parsed = parseEnvString(raw);
29
+ const { env, shared } = buildAuthEnv(parsed, "docker");
30
+ const envBlock = envToDockerBlock(env);
31
31
  const dockerCompose = `
32
32
  services:
33
+ db:
34
+ image: ${POSTGRES_IMAGE}
35
+ container_name: seamless-db
36
+ ports:
37
+ - "5432:5432"
38
+ environment:
39
+ POSTGRES_USER: myuser
40
+ POSTGRES_PASSWORD: mypassword
41
+ POSTGRES_DB: postgres
42
+ volumes:
43
+ - pgdata:/var/lib/postgresql/data
44
+ healthcheck:
45
+ test: ["CMD-SHELL", "pg_isready -U myuser -d postgres"]
46
+ interval: 5s
47
+ timeout: 5s
48
+ retries: 5
49
+
33
50
  auth:
34
- image: ghcr.io/fells-code/seamless-auth-api:v0.1.2
51
+ image: ${SEAMLESS_AUTH_API_IMAGE}
35
52
  container_name: seamless-auth
36
53
  ports:
37
54
  - "5312:5312"
38
55
  environment:
39
- PORT: 5312
40
- NODE_ENV: development
41
- AUTH_MODE: server
42
- ISSUER: http://localhost:5312
56
+ ${envBlock}
57
+ depends_on:
58
+ db:
59
+ condition: service_healthy
60
+
61
+ volumes:
62
+ pgdata:
43
63
  `;
44
64
  fs.writeFileSync(path.join(root, "docker-compose.yml"), dockerCompose.trim() + "\n");
45
65
  console.log("Docker setup ready.");
66
+ return shared;
46
67
  }
47
68
  //# sourceMappingURL=auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/generators/auth/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD,MAAM,SAAS,GAAG,iDAAiD,CAAC;AACpE,MAAM,SAAS,GAAG,IAAI,CAAC;AAEvB,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAY,EACZ,IAAiC;IAEjC,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAEzB,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;SAAM,CAAC;QACN,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAY;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAExC,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAE9C,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IAE5D,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAE3C,QAAQ,CAAC,OAAO,EAAE;QAChB,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,aAAa;QACvB,SAAS,EAAE,QAAQ;QACnB,MAAM,EAAE,oBAAoB,SAAS,EAAE;KACxC,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;AAC5C,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,IAAY;IACzC,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;IAE3D,MAAM,aAAa,GAAG;;;;;;;;;;;;CAYvB,CAAC;IAEA,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,EACrC,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAC5B,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;AACrC,CAAC"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/generators/auth/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,SAAS,GAAG,iDAAiD,CAAC;AAEpE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAY,EACZ,IAAiC,EACjC,QAAkC,EAAE;IAEpC,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAEzB,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO,MAAM,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,QAAkC,EAAE;IAC9E,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAExC,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAE9C,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IAE5D,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAE3C,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAExD,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,IAAY;IACzC,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;IAE3D,MAAM,GAAG,GAAG,MAAM,eAAe,EAAE,CAAC;IACpC,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAEvC,MAAM,aAAa,GAAG;;;aAGX,cAAc;;;;;;;;;;;;;;;;;aAiBd,uBAAuB;;;;;EAKlC,QAAQ;;;;;;;CAOT,CAAC;IAEA,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,EACrC,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAC5B,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
3
  import { VERSION } from "../../index.js";
4
+ import { SEAMLESS_AUTH_ADMIN_DASHBOARD_IMAGE, SEAMLESS_AUTH_API_IMAGE, } from "../../core/images.js";
4
5
  export function generateSeamlessConfig(root, options) {
5
6
  const config = {
6
7
  version: VERSION,
@@ -17,15 +18,13 @@ export function generateSeamlessConfig(root, options) {
17
18
  },
18
19
  auth: {
19
20
  mode: options.authMode,
20
- image: options.authMode === "docker"
21
- ? "ghcr.io/fells-code/seamless-auth-api:latest"
22
- : null,
21
+ image: options.authMode === "docker" ? SEAMLESS_AUTH_API_IMAGE : null,
23
22
  path: options.authMode === "local" ? "./auth" : null,
24
23
  },
25
24
  admin: {
26
25
  mode: options.adminMode,
27
26
  image: options.adminMode === "image"
28
- ? "ghcr.io/fells-code/seamless-auth-admin-dashboard:latest"
27
+ ? SEAMLESS_AUTH_ADMIN_DASHBOARD_IMAGE
29
28
  : null,
30
29
  path: options.adminMode === "source" ? "./admin" : null,
31
30
  },
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/generators/config/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,MAAM,UAAU,sBAAsB,CACpC,IAAY,EACZ,OAMC;IAED,MAAM,MAAM,GAAG;QACb,OAAO,EAAE,OAAO;QAChB,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QACvD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAEnC,QAAQ,EAAE;YACR,GAAG,EAAE;gBACH,SAAS,EAAE,OAAO,CAAC,YAAY;gBAC/B,IAAI,EAAE,OAAO;aACd;YACD,GAAG,EAAE;gBACH,SAAS,EAAE,OAAO,CAAC,YAAY;gBAC/B,IAAI,EAAE,OAAO;aACd;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,OAAO,CAAC,QAAQ;gBACtB,KAAK,EACH,OAAO,CAAC,QAAQ,KAAK,QAAQ;oBAC3B,CAAC,CAAC,6CAA6C;oBAC/C,CAAC,CAAC,IAAI;gBACV,IAAI,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;aACrD;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,OAAO,CAAC,SAAS;gBACvB,KAAK,EACH,OAAO,CAAC,SAAS,KAAK,OAAO;oBAC3B,CAAC,CAAC,yDAAyD;oBAC3D,CAAC,CAAC,IAAI;gBACV,IAAI,EAAE,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;aACxD;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,UAAU;aACjB;SACF;QAED,MAAM,EAAE;YACN,WAAW,EAAE,oBAAoB;SAClC;KACF,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,sBAAsB,CAAC,EACvC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAChC,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;AAC1C,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/generators/config/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EACL,mCAAmC,EACnC,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,UAAU,sBAAsB,CACpC,IAAY,EACZ,OAMC;IAED,MAAM,MAAM,GAAG;QACb,OAAO,EAAE,OAAO;QAChB,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QACvD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAEnC,QAAQ,EAAE;YACR,GAAG,EAAE;gBACH,SAAS,EAAE,OAAO,CAAC,YAAY;gBAC/B,IAAI,EAAE,OAAO;aACd;YACD,GAAG,EAAE;gBACH,SAAS,EAAE,OAAO,CAAC,YAAY;gBAC/B,IAAI,EAAE,OAAO;aACd;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,OAAO,CAAC,QAAQ;gBACtB,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI;gBACrE,IAAI,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;aACrD;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,OAAO,CAAC,SAAS;gBACvB,KAAK,EACH,OAAO,CAAC,SAAS,KAAK,OAAO;oBAC3B,CAAC,CAAC,mCAAmC;oBACrC,CAAC,CAAC,IAAI;gBACV,IAAI,EAAE,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;aACxD;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,UAAU;aACjB;SACF;QAED,MAAM,EAAE;YACN,WAAW,EAAE,oBAAoB;SAClC;KACF,CAAC;IAEF,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,sBAAsB,CAAC,EACvC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAChC,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;AAC1C,CAAC"}
@@ -4,6 +4,8 @@ import { fetchEnvExample } from "../../core/fetch.js";
4
4
  import { parseEnv, parseEnvString } from "../../core/env.js";
5
5
  import { generateSecret } from "../../core/secrets.js";
6
6
  import { generateJWKS } from "../../core/jwks.js";
7
+ import { buildOAuthAuthEnv, withLoginMethod, } from "../../core/oauthProviders.js";
8
+ import { POSTGRES_IMAGE, SEAMLESS_AUTH_ADMIN_DASHBOARD_IMAGE, SEAMLESS_AUTH_API_IMAGE, } from "../../core/images.js";
7
9
  export async function generateDockerCompose(root, options) {
8
10
  const { compose, shared } = await buildCompose(options, root);
9
11
  fs.writeFileSync(path.join(root, "docker-compose.yml"), compose.trim() + "\n");
@@ -11,13 +13,13 @@ export async function generateDockerCompose(root, options) {
11
13
  return shared;
12
14
  }
13
15
  async function buildCompose(options, root) {
14
- const { authMode, adminMode, includeAdmin } = options;
15
- const { service: authBlock, shared } = await authService(authMode, root);
16
+ const { authMode, adminMode, includeAdmin, oauth } = options;
17
+ const { service: authBlock, shared } = await authService(authMode, root, oauth);
16
18
  return {
17
19
  compose: `
18
20
  services:
19
21
  db:
20
- image: postgres:16
22
+ image: ${POSTGRES_IMAGE}
21
23
  container_name: seamless-db
22
24
  ports:
23
25
  - "5432:5432"
@@ -27,6 +29,11 @@ services:
27
29
  POSTGRES_DB: postgres
28
30
  volumes:
29
31
  - pgdata:/var/lib/postgresql/data
32
+ healthcheck:
33
+ test: ["CMD-SHELL", "pg_isready -U myuser -d postgres"]
34
+ interval: 5s
35
+ timeout: 5s
36
+ retries: 5
30
37
 
31
38
  ${authBlock}
32
39
 
@@ -42,9 +49,9 @@ volumes:
42
49
  shared,
43
50
  };
44
51
  }
45
- async function authService(mode, root) {
52
+ async function authService(mode, root, oauth = []) {
46
53
  if (mode === "local") {
47
- const shared = await configureAuthLocalEnv(root);
54
+ const shared = await configureAuthLocalEnv(root, oauth);
48
55
  return {
49
56
  service: `
50
57
  auth:
@@ -63,12 +70,13 @@ async function authService(mode, root) {
63
70
  - ./auth:/app
64
71
  - /app/node_modules
65
72
  depends_on:
66
- - db
73
+ db:
74
+ condition: service_healthy
67
75
  `,
68
76
  shared,
69
77
  };
70
78
  }
71
- return await authServiceDocker();
79
+ return await authServiceDocker(oauth);
72
80
  }
73
81
  function apiService(shared) {
74
82
  return `
@@ -89,8 +97,10 @@ function apiService(shared) {
89
97
  - ./api:/app
90
98
  - /app/node_modules
91
99
  depends_on:
92
- - db
93
- - auth
100
+ db:
101
+ condition: service_healthy
102
+ auth:
103
+ condition: service_started
94
104
  `;
95
105
  }
96
106
  function webService() {
@@ -107,24 +117,30 @@ function webService() {
107
117
  - /app/node_modules
108
118
  depends_on:
109
119
  - api
120
+ healthcheck:
121
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/health"]
122
+ interval: 5s
123
+ timeout: 5s
124
+ retries: 10
110
125
  `;
111
126
  }
112
- async function authServiceDocker() {
127
+ async function authServiceDocker(oauth = []) {
113
128
  const raw = await fetchEnvExample();
114
129
  const parsed = parseEnvString(raw);
115
- const { env, shared } = buildAuthEnv(parsed, "docker");
130
+ const { env, shared } = buildAuthEnv(parsed, "docker", oauth);
116
131
  const envBlock = envToDockerBlock(env);
117
132
  return {
118
133
  service: `
119
134
  auth:
120
- image: ghcr.io/fells-code/seamless-auth-api:latest
135
+ image: ${SEAMLESS_AUTH_API_IMAGE}
121
136
  container_name: seamless-auth
122
137
  ports:
123
138
  - "5312:5312"
124
139
  environment:
125
140
  ${envBlock}
126
141
  depends_on:
127
- - db
142
+ db:
143
+ condition: service_healthy
128
144
  `,
129
145
  shared,
130
146
  };
@@ -149,7 +165,7 @@ function adminService(mode) {
149
165
  }
150
166
  return `
151
167
  admin:
152
- image: ghcr.io/fells-code/seamless-auth-admin-dashboard:latest
168
+ image: ${SEAMLESS_AUTH_ADMIN_DASHBOARD_IMAGE}
153
169
  container_name: admin
154
170
  ports:
155
171
  - "5174:80"
@@ -159,30 +175,34 @@ function adminService(mode) {
159
175
  - api
160
176
  `;
161
177
  }
162
- function buildAuthEnv(env, mode) {
178
+ export function buildAuthEnv(env, mode, oauth = []) {
163
179
  const apiToken = generateSecret(32);
164
180
  const bootstrapSecret = generateSecret(32);
181
+ const kid = "dev-main";
165
182
  env.SEAMLESS_BOOTSTRAP_ENABLED = "true";
166
183
  env.SEAMLESS_BOOTSTRAP_SECRET = bootstrapSecret;
167
184
  env.PORT = "5312";
168
185
  env.NODE_ENV = "development";
169
186
  env.AUTH_MODE = "server";
170
- env.ISSUER = "http://auth:5312";
187
+ env.ISSUER =
188
+ mode === "docker" ? "http://auth:5312" : "http://localhost:5312";
171
189
  env.DB_HOST = mode === "docker" ? "db" : "localhost";
172
190
  env.DB_PORT = "5432";
173
191
  env.API_SERVICE_TOKEN = apiToken;
174
- let kid = "main";
175
- env.JWKS_ACTIVE_KID = kid;
176
- if (mode === "docker") {
177
- const jwks = buildJWKSConfig();
178
- kid = jwks.kid;
179
- env.SEAMLESS_JWKS_ACTIVE_KID = jwks.kid;
180
- env.JWKS_ACTIVE_KID = jwks.kid;
181
- env[`SEAMLESS_JWKS_KEY_${jwks.kid}_PRIVATE`] = jwks.privateKey;
182
- env.JWKS_PUBLIC_KEYS = jwks.publicJwksJson;
183
- }
192
+ // Dedicated secrets the auth server otherwise leaves empty (falling back to the
193
+ // service token). Generate real ones so a scaffolded stack is production-shaped.
194
+ env.REFRESH_TOKEN_LOOKUP_SECRET = generateSecret(32);
195
+ env.TOTP_SECRET_ENCRYPTION_KEY = generateSecret(32);
184
196
  env.APP_ORIGINS = "http://localhost:3000";
185
- env.ORIGINS = "http://localhost:5173";
197
+ env.ORIGINS = "http://localhost:5173,http://localhost:5174";
198
+ // When the OAuth template collected providers, wire them into the auth server
199
+ // (OAUTH_PROVIDERS, per-provider secret env vars, OAUTH_STATE_SECRET) and enable
200
+ // the oauth login method.
201
+ if (oauth.length > 0) {
202
+ const { env: oauthEnv } = buildOAuthAuthEnv(oauth);
203
+ Object.assign(env, oauthEnv);
204
+ env.LOGIN_METHODS = withLoginMethod(env.LOGIN_METHODS, "oauth");
205
+ }
186
206
  return {
187
207
  env,
188
208
  shared: {
@@ -198,7 +218,7 @@ export function envToDockerBlock(env) {
198
218
  if (v.includes("\n")) {
199
219
  return ` ${k}: |\n${indentMultiline(v, 8)}`;
200
220
  }
201
- return ` ${k}: ${v}`;
221
+ return ` ${k}: ${JSON.stringify(v)}`;
202
222
  })
203
223
  .join("\n");
204
224
  }
@@ -226,7 +246,7 @@ export function buildJWKSConfig() {
226
246
  }, null, 2),
227
247
  };
228
248
  }
229
- export async function configureAuthLocalEnv(root) {
249
+ export async function configureAuthLocalEnv(root, oauth = []) {
230
250
  const authDir = path.join(root, "auth");
231
251
  const envExamplePath = path.join(authDir, ".env.example");
232
252
  const envPath = path.join(authDir, ".env");
@@ -235,7 +255,7 @@ export async function configureAuthLocalEnv(root) {
235
255
  }
236
256
  const raw = fs.readFileSync(envExamplePath, "utf-8");
237
257
  const parsed = parseEnvString(raw);
238
- const { env, shared } = buildAuthEnv(parsed, "local");
258
+ const { env, shared } = buildAuthEnv(parsed, "local", oauth);
239
259
  writeEnvFile(envPath, env);
240
260
  return shared;
241
261
  }
@@ -257,7 +277,7 @@ export function extractSharedFromExistingEnv(root) {
257
277
  const env = parseEnv(path.join(root, "auth", ".env"));
258
278
  return {
259
279
  apiToken: env.API_SERVICE_TOKEN,
260
- kid: env.JWKS_ACTIVE_KID || "main",
280
+ kid: env.SEAMLESS_JWKS_ACTIVE_KID || env.JWKS_ACTIVE_KID || "dev-main",
261
281
  };
262
282
  }
263
283
  //# sourceMappingURL=docker.js.map