@augmenting-integrations/create-tenant 0.0.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.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import * as fs from "fs";
5
+ import * as path from "path";
6
+ import { fileURLToPath } from "url";
7
+ import process from "process";
8
+ function parseArgs(argv) {
9
+ const args = argv.slice(2);
10
+ let name;
11
+ let apex;
12
+ for (const a of args) {
13
+ if (a.startsWith("--apex=")) apex = a.slice("--apex=".length);
14
+ else if (!a.startsWith("--")) name = a;
15
+ }
16
+ if (!name) {
17
+ console.error("Usage: create-tenant <name> [--apex=tenant.example.com]");
18
+ process.exit(1);
19
+ }
20
+ return { name, apex: apex ?? `${name}.example.com` };
21
+ }
22
+ function templatesDir() {
23
+ const here = fileURLToPath(import.meta.url);
24
+ return path.resolve(path.dirname(here), "..", "templates");
25
+ }
26
+ function ensureEmptyDir(target) {
27
+ if (fs.existsSync(target)) {
28
+ const entries = fs.readdirSync(target);
29
+ if (entries.length > 0) {
30
+ console.error(`Refusing to write into non-empty directory: ${target}`);
31
+ process.exit(1);
32
+ }
33
+ } else {
34
+ fs.mkdirSync(target, { recursive: true });
35
+ }
36
+ }
37
+ function replaceVars(content, flags) {
38
+ return content.replace(/__TENANT_NAME__/g, flags.name).replace(/__TENANT_APEX__/g, flags.apex).replace(/__TENANT_PARENT__/g, `.${flags.apex}`);
39
+ }
40
+ function copyTree(src, dst, flags) {
41
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
42
+ const srcPath = path.join(src, entry.name);
43
+ const baseName = entry.name.endsWith(".tmpl") ? entry.name.slice(0, -".tmpl".length) : entry.name;
44
+ const dstPath = path.join(dst, replaceVars(baseName, flags));
45
+ if (entry.isDirectory()) {
46
+ fs.mkdirSync(dstPath, { recursive: true });
47
+ copyTree(srcPath, dstPath, flags);
48
+ } else {
49
+ const raw = fs.readFileSync(srcPath, "utf8");
50
+ fs.writeFileSync(dstPath, replaceVars(raw, flags), "utf8");
51
+ }
52
+ }
53
+ }
54
+ function main() {
55
+ const flags = parseArgs(process.argv);
56
+ const target = path.resolve(process.cwd(), flags.name);
57
+ ensureEmptyDir(target);
58
+ const templates = templatesDir();
59
+ if (!fs.existsSync(templates)) {
60
+ console.error(`Templates directory missing at ${templates}. Re-install the CLI.`);
61
+ process.exit(1);
62
+ }
63
+ copyTree(templates, target, flags);
64
+ console.log(`Created apex "${flags.name}" at ${target}`);
65
+ console.log(`Apex domain: ${flags.apex}`);
66
+ console.log("");
67
+ console.log("Next steps:");
68
+ console.log(` cd ${flags.name}`);
69
+ console.log(" cp .env.example .env # fill in Cognito ARNs + AWS resources");
70
+ console.log(" pnpm install");
71
+ console.log(" pnpm dev");
72
+ console.log("");
73
+ console.log("Then provision tenant infra (Cognito + DNS + registry table):");
74
+ console.log(" see README.md");
75
+ }
76
+ main();
77
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport process from \"node:process\";\n\n// =============================================================================\n// create-tenant -- scaffold a new tenant apex app.\n//\n// Usage:\n// pnpm dlx @augmenting-integrations/create-tenant <name> [--apex=foo.com]\n//\n// Writes a minimal but deployable Next 16 apex to ./<name>/ that:\n// - Mounts /api/auth/[...nextauth] -- the ONLY Cognito OAuth callback in\n// the tenant ecosystem\n// - Exposes /api/apps (registry auto-discovery, read by every spoke's\n// AppShell) and /api/admin/apps (admin-only CRUD)\n// - Wires loadTenantConfig({ role: \"apex\" }) + TenantBootScript +\n// TenantProvider so spokes can read the same tenant struct\n// - Includes a /studio admin landing page stub\n//\n// Out of scope (manual follow-up, documented in the generated README):\n// - Cognito User Pool + App Client provisioning\n// - DNS hosted zone + cert\n// - App registry DynamoDB table\n// - GitHub OIDC role for deploys\n// - First spoke (use create-spoke for each product subdomain)\n// =============================================================================\n\ntype Flags = {\n name: string;\n apex: string;\n};\n\nfunction parseArgs(argv: string[]): Flags {\n const args = argv.slice(2);\n let name: string | undefined;\n let apex: string | undefined;\n for (const a of args) {\n if (a.startsWith(\"--apex=\")) apex = a.slice(\"--apex=\".length);\n else if (!a.startsWith(\"--\")) name = a;\n }\n if (!name) {\n console.error(\"Usage: create-tenant <name> [--apex=tenant.example.com]\");\n process.exit(1);\n }\n return { name, apex: apex ?? `${name}.example.com` };\n}\n\nfunction templatesDir(): string {\n const here = fileURLToPath(import.meta.url);\n return path.resolve(path.dirname(here), \"..\", \"templates\");\n}\n\nfunction ensureEmptyDir(target: string): void {\n if (fs.existsSync(target)) {\n const entries = fs.readdirSync(target);\n if (entries.length > 0) {\n console.error(`Refusing to write into non-empty directory: ${target}`);\n process.exit(1);\n }\n } else {\n fs.mkdirSync(target, { recursive: true });\n }\n}\n\nfunction replaceVars(content: string, flags: Flags): string {\n return content\n .replace(/__TENANT_NAME__/g, flags.name)\n .replace(/__TENANT_APEX__/g, flags.apex)\n .replace(/__TENANT_PARENT__/g, `.${flags.apex}`);\n}\n\nfunction copyTree(src: string, dst: string, flags: Flags): void {\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\n const srcPath = path.join(src, entry.name);\n const baseName = entry.name.endsWith(\".tmpl\")\n ? entry.name.slice(0, -\".tmpl\".length)\n : entry.name;\n const dstPath = path.join(dst, replaceVars(baseName, flags));\n if (entry.isDirectory()) {\n fs.mkdirSync(dstPath, { recursive: true });\n copyTree(srcPath, dstPath, flags);\n } else {\n const raw = fs.readFileSync(srcPath, \"utf8\");\n fs.writeFileSync(dstPath, replaceVars(raw, flags), \"utf8\");\n }\n }\n}\n\nfunction main(): void {\n const flags = parseArgs(process.argv);\n const target = path.resolve(process.cwd(), flags.name);\n ensureEmptyDir(target);\n const templates = templatesDir();\n if (!fs.existsSync(templates)) {\n console.error(`Templates directory missing at ${templates}. Re-install the CLI.`);\n process.exit(1);\n }\n copyTree(templates, target, flags);\n console.log(`Created apex \"${flags.name}\" at ${target}`);\n console.log(`Apex domain: ${flags.apex}`);\n console.log(\"\");\n console.log(\"Next steps:\");\n console.log(` cd ${flags.name}`);\n console.log(\" cp .env.example .env # fill in Cognito ARNs + AWS resources\");\n console.log(\" pnpm install\");\n console.log(\" pnpm dev\");\n console.log(\"\");\n console.log(\"Then provision tenant infra (Cognito + DNS + registry table):\");\n console.log(\" see README.md\");\n}\n\nmain();\n"],"mappings":";;;AAAA,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,qBAAqB;AAC9B,OAAO,aAAa;AA8BpB,SAAS,UAAU,MAAuB;AACxC,QAAM,OAAO,KAAK,MAAM,CAAC;AACzB,MAAI;AACJ,MAAI;AACJ,aAAW,KAAK,MAAM;AACpB,QAAI,EAAE,WAAW,SAAS,EAAG,QAAO,EAAE,MAAM,UAAU,MAAM;AAAA,aACnD,CAAC,EAAE,WAAW,IAAI,EAAG,QAAO;AAAA,EACvC;AACA,MAAI,CAAC,MAAM;AACT,YAAQ,MAAM,yDAAyD;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO,EAAE,MAAM,MAAM,QAAQ,GAAG,IAAI,eAAe;AACrD;AAEA,SAAS,eAAuB;AAC9B,QAAM,OAAO,cAAc,YAAY,GAAG;AAC1C,SAAY,aAAa,aAAQ,IAAI,GAAG,MAAM,WAAW;AAC3D;AAEA,SAAS,eAAe,QAAsB;AAC5C,MAAO,cAAW,MAAM,GAAG;AACzB,UAAM,UAAa,eAAY,MAAM;AACrC,QAAI,QAAQ,SAAS,GAAG;AACtB,cAAQ,MAAM,+CAA+C,MAAM,EAAE;AACrE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,OAAO;AACL,IAAG,aAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AAAA,EAC1C;AACF;AAEA,SAAS,YAAY,SAAiB,OAAsB;AAC1D,SAAO,QACJ,QAAQ,oBAAoB,MAAM,IAAI,EACtC,QAAQ,oBAAoB,MAAM,IAAI,EACtC,QAAQ,sBAAsB,IAAI,MAAM,IAAI,EAAE;AACnD;AAEA,SAAS,SAAS,KAAa,KAAa,OAAoB;AAC9D,aAAW,SAAY,eAAY,KAAK,EAAE,eAAe,KAAK,CAAC,GAAG;AAChE,UAAM,UAAe,UAAK,KAAK,MAAM,IAAI;AACzC,UAAM,WAAW,MAAM,KAAK,SAAS,OAAO,IACxC,MAAM,KAAK,MAAM,GAAG,CAAC,QAAQ,MAAM,IACnC,MAAM;AACV,UAAM,UAAe,UAAK,KAAK,YAAY,UAAU,KAAK,CAAC;AAC3D,QAAI,MAAM,YAAY,GAAG;AACvB,MAAG,aAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AACzC,eAAS,SAAS,SAAS,KAAK;AAAA,IAClC,OAAO;AACL,YAAM,MAAS,gBAAa,SAAS,MAAM;AAC3C,MAAG,iBAAc,SAAS,YAAY,KAAK,KAAK,GAAG,MAAM;AAAA,IAC3D;AAAA,EACF;AACF;AAEA,SAAS,OAAa;AACpB,QAAM,QAAQ,UAAU,QAAQ,IAAI;AACpC,QAAM,SAAc,aAAQ,QAAQ,IAAI,GAAG,MAAM,IAAI;AACrD,iBAAe,MAAM;AACrB,QAAM,YAAY,aAAa;AAC/B,MAAI,CAAI,cAAW,SAAS,GAAG;AAC7B,YAAQ,MAAM,kCAAkC,SAAS,uBAAuB;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,WAAS,WAAW,QAAQ,KAAK;AACjC,UAAQ,IAAI,iBAAiB,MAAM,IAAI,QAAQ,MAAM,EAAE;AACvD,UAAQ,IAAI,gBAAgB,MAAM,IAAI,EAAE;AACxC,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,aAAa;AACzB,UAAQ,IAAI,QAAQ,MAAM,IAAI,EAAE;AAChC,UAAQ,IAAI,gEAAgE;AAC5E,UAAQ,IAAI,gBAAgB;AAC5B,UAAQ,IAAI,YAAY;AACxB,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,+DAA+D;AAC3E,UAAQ,IAAI,iBAAiB;AAC/B;AAEA,KAAK;","names":[]}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@augmenting-integrations/create-tenant",
3
+ "version": "0.0.0",
4
+ "description": "Scaffold a new tenant apex app for an augint deployment. Generates a Next 16 + Auth.js v5 + Cognito apex with TenantConfig wired up, library-owned /api/apps registry handler, and /studio admin. Single command: pnpm dlx @augmenting-integrations/create-tenant my-tenant.",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "type": "module",
10
+ "bin": {
11
+ "create-tenant": "./dist/cli.js"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "templates",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "clean": "rm -rf dist",
21
+ "test": "vitest run --passWithNoTests"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^22.0.0",
25
+ "tsup": "^8.3.5",
26
+ "typescript": "^5.7.2",
27
+ "vitest": "^4.1.5"
28
+ }
29
+ }
@@ -0,0 +1,22 @@
1
+ APP_DOMAIN=__TENANT_APEX__
2
+ APEX_DOMAIN=__TENANT_APEX__
3
+ AUTH_COOKIE_DOMAIN=__TENANT_PARENT__
4
+ AUTH_ALLOWED_PARENT_DOMAIN=__TENANT_PARENT__
5
+
6
+ # Cognito (apex-only; from your tenant infra stack)
7
+ AUTH_SECRET_ARN=
8
+ AUTH_COGNITO_SECRET_ARN=
9
+ AUTH_COGNITO_ID=
10
+ AUTH_COGNITO_ISSUER=
11
+
12
+ # App registry DynamoDB table (apex owns CRUD; spokes read)
13
+ APP_REGISTRY_TABLE=
14
+ TENANT_SLUG=__TENANT_NAME__
15
+ STAGE=staging
16
+
17
+ AWS_REGION=us-east-1
18
+ ADMIN_EMAILS=
19
+
20
+ # Local-dev fallbacks
21
+ AUTH_SECRET=dev-only-fallback-not-for-prod
22
+ NODE_ENV=development
@@ -0,0 +1,7 @@
1
+ node_modules
2
+ .next
3
+ .env
4
+ .env.local
5
+ *.log
6
+ .DS_Store
7
+ .vercel
@@ -0,0 +1,44 @@
1
+ # __TENANT_NAME__ apex
2
+
3
+ Generated by `@augmenting-integrations/create-tenant`. This is the apex
4
+ Next.js 16 app for a new augint tenant. It owns:
5
+
6
+ - The OAuth callback (`/api/auth/[...nextauth]`) for the ENTIRE tenant
7
+ - The session cookie scope (`Domain=__TENANT_PARENT__`)
8
+ - The app registry (`/api/apps` for spoke auto-discovery)
9
+ - The studio admin
10
+
11
+ ## Local dev
12
+
13
+ ```bash
14
+ cp .env.example .env
15
+ # fill in tenant identity values; AWS resources can be blank for local dev
16
+ pnpm install
17
+ pnpm dev
18
+ ```
19
+
20
+ ## Per-tenant infra (NOT scaffolded — provision separately)
21
+
22
+ The application code is portable; the AWS infra is tenant-specific:
23
+
24
+ 1. **AWS account** — sandbox/staging + prod accounts.
25
+ 2. **Cognito User Pool** with one App Client + ONE callback URL:
26
+ `https://__TENANT_APEX__/api/auth/callback/cognito`
27
+ 3. **Hosted zone + certs** for `__TENANT_APEX__` and `*.__TENANT_APEX__`.
28
+ 4. **App registry DynamoDB table** (PK = `slug`). Apex owns CRUD; spokes
29
+ read via `/api/apps`. Set `APP_REGISTRY_TABLE` env to the table name.
30
+ 5. **Secrets Manager** rows for `AUTH_SECRET`, `AUTH_COGNITO_SECRET`.
31
+ 6. **GitHub OIDC role** in each AWS account for CI deploys.
32
+
33
+ Copy `template.yaml` from an existing tenant (the example tenant ships one)
34
+ and adapt the parameters.
35
+
36
+ ## Adding spokes
37
+
38
+ ```bash
39
+ pnpm dlx @augmenting-integrations/create-spoke my-product-spoke
40
+ ```
41
+
42
+ Then register the new spoke in this apex's app registry table (slug,
43
+ subdomain, displayName, navOrder). The spoke shows up in every other
44
+ spoke's AppShell ecosystem nav automatically.
@@ -0,0 +1,8 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ output: "standalone",
5
+ outputFileTracingRoot: process.cwd(),
6
+ };
7
+
8
+ export default nextConfig;
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "__TENANT_NAME__-apex",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "next dev",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "eslint .",
11
+ "type-check": "tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "@augmenting-integrations/auth": "^8.0.0",
15
+ "@augmenting-integrations/aws": "^8.0.0",
16
+ "@augmenting-integrations/brand": "^8.0.0",
17
+ "@augmenting-integrations/registry": "^8.0.0",
18
+ "@augmenting-integrations/themes": "^8.0.0",
19
+ "@augmenting-integrations/ui": "^8.0.0",
20
+ "next": "^16.2.5",
21
+ "next-auth": "^5.0.0-beta.31",
22
+ "react": "^19.2.0",
23
+ "react-dom": "^19.2.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^22.0.0",
27
+ "@types/react": "^19.0.0",
28
+ "@types/react-dom": "^19.0.0",
29
+ "typescript": "^5.7.2"
30
+ }
31
+ }
@@ -0,0 +1,25 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import type { Session } from "next-auth";
5
+ import { SessionProvider } from "@augmenting-integrations/ui";
6
+ import {
7
+ TenantProvider,
8
+ type TenantPublicConfig,
9
+ } from "@augmenting-integrations/auth/client";
10
+
11
+ export function Providers({
12
+ children,
13
+ session,
14
+ tenant,
15
+ }: {
16
+ children: React.ReactNode;
17
+ session: Session | null;
18
+ tenant: TenantPublicConfig;
19
+ }) {
20
+ return (
21
+ <TenantProvider tenant={tenant}>
22
+ <SessionProvider session={session}>{children}</SessionProvider>
23
+ </TenantProvider>
24
+ );
25
+ }
@@ -0,0 +1,7 @@
1
+ // Auto-discovery endpoint consumed by every spoke's AppShell. Scans the
2
+ // DynamoDB app registry table + filters by the caller's Cognito groups.
3
+ import { createGetHandler } from "@augmenting-integrations/registry/api-route";
4
+ import { auth } from "@/lib/auth";
5
+
6
+ export const GET = createGetHandler({ authFn: auth });
7
+ export const runtime = "nodejs";
@@ -0,0 +1,6 @@
1
+ // The ONLY Cognito callback in the tenant ecosystem. Subdomain spokes
2
+ // redirect to /login here; we run the OAuth dance and set the parent-domain
3
+ // cookie so every spoke sees the session on the next request.
4
+ import { handlers } from "@/lib/auth";
5
+
6
+ export const { GET, POST } = handlers;
@@ -0,0 +1,22 @@
1
+ @import "tailwindcss";
2
+
3
+ @source "../../node_modules/@augmenting-integrations/ui/dist";
4
+ @source "../../node_modules/@augmenting-integrations/auth/dist";
5
+ @source "../../node_modules/@augmenting-integrations/brand/dist";
6
+
7
+ @custom-variant dark (&:where(.dark, .dark *));
8
+
9
+ :root {
10
+ --background: oklch(0.99 0 0);
11
+ --foreground: oklch(0.15 0 0);
12
+ }
13
+
14
+ .dark {
15
+ --background: oklch(0.12 0.02 240);
16
+ --foreground: oklch(0.95 0 0);
17
+ }
18
+
19
+ body {
20
+ background-color: var(--background);
21
+ color: var(--foreground);
22
+ }
@@ -0,0 +1,42 @@
1
+ import type { Metadata } from "next";
2
+ import { cookies } from "next/headers";
3
+ import {
4
+ THEME_COOKIE_KEY,
5
+ THEME_VARIANT_COOKIE_KEY,
6
+ } from "@augmenting-integrations/themes";
7
+ import { ThemeBootScript } from "@augmenting-integrations/ui";
8
+ import { TenantBootScript, publicSubset } from "@augmenting-integrations/auth/server";
9
+ import { auth, tenant } from "@/lib/auth";
10
+ import { Providers } from "./Providers";
11
+ import "./globals.css";
12
+
13
+ export const metadata: Metadata = {
14
+ title: { default: "__TENANT_NAME__", template: "%s — __TENANT_NAME__" },
15
+ };
16
+
17
+ export default async function RootLayout({ children }: { children: React.ReactNode }) {
18
+ const [session, cookieStore] = await Promise.all([auth(), cookies()]);
19
+ const themeName = cookieStore.get(THEME_COOKIE_KEY)?.value ?? "default";
20
+ const variantCookie = cookieStore.get(THEME_VARIANT_COOKIE_KEY)?.value;
21
+ const variant: "dark" | "light" =
22
+ variantCookie === "dark" || variantCookie === "light" ? variantCookie : "light";
23
+ const publicTenant = publicSubset(tenant);
24
+ return (
25
+ <html
26
+ lang="en"
27
+ data-theme={themeName}
28
+ className={variant === "dark" ? "dark" : undefined}
29
+ suppressHydrationWarning
30
+ >
31
+ <head>
32
+ <TenantBootScript config={publicTenant} />
33
+ <ThemeBootScript />
34
+ </head>
35
+ <body>
36
+ <Providers session={session} tenant={publicTenant}>
37
+ {children}
38
+ </Providers>
39
+ </body>
40
+ </html>
41
+ );
42
+ }
@@ -0,0 +1,26 @@
1
+ import { signIn } from "@/lib/auth";
2
+
3
+ export default function LoginPage() {
4
+ return (
5
+ <main
6
+ style={{
7
+ padding: "2rem",
8
+ fontFamily: "system-ui",
9
+ display: "flex",
10
+ flexDirection: "column",
11
+ alignItems: "center",
12
+ gap: "1rem",
13
+ }}
14
+ >
15
+ <h1>Sign in to __TENANT_NAME__</h1>
16
+ <form
17
+ action={async () => {
18
+ "use server";
19
+ await signIn("cognito", { redirectTo: "/home" });
20
+ }}
21
+ >
22
+ <button type="submit">Sign in with Cognito</button>
23
+ </form>
24
+ </main>
25
+ );
26
+ }
@@ -0,0 +1,14 @@
1
+ import Link from "next/link";
2
+
3
+ export default function Home() {
4
+ return (
5
+ <main style={{ padding: "2rem", fontFamily: "system-ui" }}>
6
+ <h1>__TENANT_NAME__</h1>
7
+ <p>Tenant apex at <strong>__TENANT_APEX__</strong>.</p>
8
+ <p>
9
+ <Link href="/login">Sign in</Link> to access the studio admin or any
10
+ product subdomain.
11
+ </p>
12
+ </main>
13
+ );
14
+ }
@@ -0,0 +1,20 @@
1
+ import { createAuth, loadTenantConfig } from "@augmenting-integrations/auth/server";
2
+ import { getSecret } from "@augmenting-integrations/aws/server";
3
+
4
+ // Single tenant configuration source.
5
+ export const tenant = loadTenantConfig({ role: "apex" });
6
+
7
+ // Apex is the OAuth broker -- it needs both auth secret and Cognito client
8
+ // secret. Both live in Secrets Manager (ARNs in tenant config).
9
+ const [authSecretRaw, cognitoClientSecretRaw] = await Promise.all([
10
+ getSecret(tenant.authSecretArn),
11
+ getSecret(tenant.authCognitoSecretArn!),
12
+ ]);
13
+ const authSecret = authSecretRaw ?? "dev-only-fallback-not-for-prod";
14
+
15
+ export const { handlers, auth, signIn, signOut } = createAuth({
16
+ tenant,
17
+ authedRoutePrefixes: ["/home", "/studio"],
18
+ authSecret,
19
+ cognitoClientSecret: cognitoClientSecretRaw,
20
+ });
@@ -0,0 +1,7 @@
1
+ import { auth } from "./lib/auth.js";
2
+
3
+ export default auth;
4
+
5
+ export const config = {
6
+ matcher: ["/((?!_next/static|_next/image|favicon.ico|api/auth/).*)"],
7
+ };
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": false,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "plugins": [{ "name": "next" }],
17
+ "paths": { "@/*": ["./src/*"] }
18
+ },
19
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
20
+ "exclude": ["node_modules"]
21
+ }