@augmenting-integrations/auth 4.0.2 → 4.1.1

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/index.cjs CHANGED
@@ -30,12 +30,84 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- createAuth: () => createAuth
33
+ AuthError: () => AuthError,
34
+ createAuth: () => createAuth,
35
+ getUserGroups: () => getUserGroups,
36
+ hasGroup: () => hasGroup,
37
+ requireGroup: () => requireGroup
34
38
  });
35
39
  module.exports = __toCommonJS(index_exports);
36
40
  var import_next_auth = __toESM(require("next-auth"));
37
41
  var import_credentials = __toESM(require("next-auth/providers/credentials"));
38
42
  var import_cognito = __toESM(require("next-auth/providers/cognito"));
43
+ var AuthError = class extends Error {
44
+ constructor(code) {
45
+ super(code);
46
+ this.code = code;
47
+ this.name = "AuthError";
48
+ }
49
+ code;
50
+ };
51
+ function getUserGroups(session) {
52
+ return session?.user?.groups ?? [];
53
+ }
54
+ function hasGroup(session, name) {
55
+ if (!session) return false;
56
+ const target = name.toLowerCase();
57
+ return getUserGroups(session).some((g) => g.toLowerCase() === target);
58
+ }
59
+ function requireGroup(session, ...names) {
60
+ if (!session) throw new AuthError("unauthenticated");
61
+ if (names.length === 0) return;
62
+ const ok = names.some((n) => hasGroup(session, n));
63
+ if (!ok) throw new AuthError("forbidden");
64
+ }
65
+ function validateProdEnv(args) {
66
+ if (!args.isProd) return;
67
+ if (process.env.NEXT_PHASE === "phase-production-build") return;
68
+ const missing = [];
69
+ if (!args.secret) missing.push("AUTH_SECRET");
70
+ if (!args.cognitoClientId) missing.push("AUTH_COGNITO_ID");
71
+ if (!args.cognitoClientSecret) missing.push("AUTH_COGNITO_SECRET");
72
+ if (!args.cognitoIssuer) missing.push("AUTH_COGNITO_ISSUER");
73
+ const hasAny = !!(args.cookieDomain || args.allowedParentDomain || args.appDomain);
74
+ if (hasAny) {
75
+ if (!args.cookieDomain) missing.push("AUTH_COOKIE_DOMAIN");
76
+ if (!args.allowedParentDomain) missing.push("AUTH_ALLOWED_PARENT_DOMAIN");
77
+ if (!args.appDomain) missing.push("APP_DOMAIN");
78
+ }
79
+ if (missing.length > 0) {
80
+ throw new Error(
81
+ `[@augmenting-integrations/auth] Missing required prod env vars: ${missing.join(
82
+ ", "
83
+ )}. Provide via createAuth() opts or process.env.`
84
+ );
85
+ }
86
+ }
87
+ function buildRedirectCallback(allowedParentDomain) {
88
+ return ({ url, baseUrl }) => {
89
+ try {
90
+ const target = new URL(url, baseUrl);
91
+ if (!allowedParentDomain) {
92
+ return target.origin === new URL(baseUrl).origin ? target.toString() : baseUrl;
93
+ }
94
+ const apex = allowedParentDomain.replace(/^\./, "").toLowerCase();
95
+ const host = target.hostname.toLowerCase();
96
+ const ok = host === apex || host.endsWith(`.${apex}`);
97
+ return ok ? target.toString() : baseUrl;
98
+ } catch {
99
+ return baseUrl;
100
+ }
101
+ };
102
+ }
103
+ function deriveSignInPage(args) {
104
+ if (args.signInPage) return args.signInPage;
105
+ if (args.appDomain && args.allowedParentDomain) {
106
+ const apex = args.allowedParentDomain.replace(/^\./, "");
107
+ return args.appDomain === apex ? "/login" : `https://${apex}/login`;
108
+ }
109
+ return "/login";
110
+ }
39
111
  function roleFromGroups(groups) {
40
112
  if (Array.isArray(groups) && groups.length > 0) {
41
113
  return String(groups[0]).toLowerCase();
@@ -44,12 +116,28 @@ function roleFromGroups(groups) {
44
116
  }
45
117
  function createAuth(opts) {
46
118
  const isProd = opts.isProd ?? process.env.NODE_ENV === "production";
47
- const signInPage = opts.signInPage ?? "/login";
48
119
  const cookieDomain = isProd ? opts.cookieDomain ?? process.env.AUTH_COOKIE_DOMAIN : void 0;
120
+ const allowedParentDomain = opts.allowedParentDomain ?? process.env.AUTH_ALLOWED_PARENT_DOMAIN;
121
+ const appDomain = opts.appDomain ?? process.env.APP_DOMAIN;
49
122
  const SECRET = opts.secret ?? process.env.AUTH_SECRET ?? (isProd ? void 0 : "dev-only-fallback-not-for-prod");
50
123
  const cognitoClientId = opts.cognito?.clientId ?? process.env.AUTH_COGNITO_ID;
51
124
  const cognitoClientSecret = opts.cognito?.clientSecret ?? process.env.AUTH_COGNITO_SECRET;
52
125
  const cognitoIssuer = opts.cognito?.issuer ?? process.env.AUTH_COGNITO_ISSUER;
126
+ validateProdEnv({
127
+ isProd,
128
+ cookieDomain,
129
+ allowedParentDomain,
130
+ appDomain,
131
+ secret: SECRET,
132
+ cognitoClientId,
133
+ cognitoClientSecret,
134
+ cognitoIssuer
135
+ });
136
+ const signInPage = deriveSignInPage({
137
+ signInPage: opts.signInPage,
138
+ appDomain,
139
+ allowedParentDomain
140
+ });
53
141
  const config = {
54
142
  secret: SECRET,
55
143
  cookies: cookieDomain ? {
@@ -88,7 +176,8 @@ function createAuth(opts) {
88
176
  id: `mock-${role}`,
89
177
  name: `${display} (mock)`,
90
178
  email: `${role}@example.local`,
91
- role
179
+ role,
180
+ groups: [role]
92
181
  };
93
182
  }
94
183
  })
@@ -99,10 +188,12 @@ function createAuth(opts) {
99
188
  if (user) {
100
189
  token.sub ??= user.id ?? void 0;
101
190
  token.email ??= user.email ?? void 0;
102
- if (!isProd && user.role) {
103
- token["cognito:groups"] = [
104
- user.role
105
- ];
191
+ if (!isProd) {
192
+ const u = user;
193
+ const groups = u.groups ?? (u.role ? [u.role] : []);
194
+ if (groups.length > 0) {
195
+ token["cognito:groups"] = groups;
196
+ }
106
197
  }
107
198
  }
108
199
  if (isProd && profile) {
@@ -114,9 +205,9 @@ function createAuth(opts) {
114
205
  return token;
115
206
  },
116
207
  session: ({ session, token }) => {
117
- session.user.role = roleFromGroups(
118
- token["cognito:groups"]
119
- );
208
+ const groups = token["cognito:groups"] ?? [];
209
+ session.user.groups = groups;
210
+ session.user.role = roleFromGroups(groups);
120
211
  return session;
121
212
  },
122
213
  authorized: ({ auth: session, request: { nextUrl } }) => {
@@ -126,7 +217,8 @@ function createAuth(opts) {
126
217
  );
127
218
  if (!session && isAuthedRoute) return false;
128
219
  return true;
129
- }
220
+ },
221
+ redirect: buildRedirectCallback(allowedParentDomain)
130
222
  },
131
223
  pages: { signIn: signInPage },
132
224
  trustHost: true
@@ -135,6 +227,10 @@ function createAuth(opts) {
135
227
  }
136
228
  // Annotate the CommonJS export names for ESM import in node:
137
229
  0 && (module.exports = {
138
- createAuth
230
+ AuthError,
231
+ createAuth,
232
+ getUserGroups,
233
+ hasGroup,
234
+ requireGroup
139
235
  });
140
236
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Auth.js v5 (the package is still distributed as `next-auth`, but treat\n// these as Auth.js v5 internally — docs at https://authjs.dev, NOT\n// next-auth.js.org which is v4 and incompatible).\n//\n// Provider strategy:\n// - Production: Cognito OIDC. Group membership from `cognito:groups`\n// drives role.\n// - Dev / preview: Credentials with a role picker, BUT shaped to mirror\n// Cognito's claim payload so consumers (callbacks, gates, audit logs)\n// never branch on environment. Same `sub`, `email`, `cognito:groups`\n// in both modes.\n//\n// IMPORTANT: do not let the dev-mode session diverge from production. If\n// you add a claim in prod, also add it (with a synthetic value) in dev.\n\nimport NextAuth, { type DefaultSession, type NextAuthConfig } from \"next-auth\";\nimport Credentials from \"next-auth/providers/credentials\";\nimport Cognito from \"next-auth/providers/cognito\";\n\n// Role is intentionally `string`. Consumer apps narrow it (e.g. via local\n// module augmentation or runtime check) to whatever taxonomy their Cognito\n// user pool defines. The auth package itself makes no assumptions about\n// what roles exist.\ndeclare module \"next-auth\" {\n interface Session {\n user: {\n role: string;\n } & DefaultSession[\"user\"];\n }\n interface User {\n role: string;\n }\n}\n\nexport type CreateAuthOptions = {\n /**\n * Path prefixes that require an authenticated session.\n * Empty array = no gating (rare).\n */\n authedRoutePrefixes: string[];\n /**\n * Page to redirect to when an unauthed user hits a gated route. Can be a\n * relative path (`\"/login\"`) for in-app login, or a fully-qualified URL\n * (`\"https://example.com/login\"`) to delegate login to a peer app on a\n * sibling subdomain or the apex of a path-routed deployment.\n * Default: `/login`.\n */\n signInPage?: string;\n /**\n * Cookie domain for the session token. Set this to the parent domain\n * (e.g. `.example.com`) when running across multiple subdomains so the\n * session JWT is readable by every app sharing that parent. For\n * path-routed deployments under a single domain, leave unset (host-only\n * is correct).\n *\n * Default: read from `process.env.AUTH_COOKIE_DOMAIN`; if unset, Auth.js\n * defaults to host-only (current request hostname).\n *\n * In dev (NODE_ENV !== \"production\"), this is ignored — cookies stay\n * scoped to localhost so per-port apps don't collide.\n */\n cookieDomain?: string;\n /**\n * Override prod/dev detection. Default reads NODE_ENV.\n */\n isProd?: boolean;\n /**\n * The JWT signing secret. If not provided, falls back to\n * `process.env.AUTH_SECRET`. Pass this from a runtime fetch (e.g. AWS\n * Secrets Manager via @aws-sdk/client-secrets-manager) to keep the secret\n * out of Lambda environment variables and to support rotation without\n * redeploy (Lambda containers re-init periodically and pick up the new\n * value).\n */\n secret?: string;\n /**\n * Cognito OIDC provider config. Each field falls back to the matching\n * `AUTH_COGNITO_*` env var if not provided. Provide them explicitly to\n * fetch the client secret from Secrets Manager at runtime.\n */\n cognito?: {\n clientId?: string;\n clientSecret?: string;\n issuer?: string;\n };\n};\n\nfunction roleFromGroups(groups: unknown): string {\n if (Array.isArray(groups) && groups.length > 0) {\n return String(groups[0]).toLowerCase();\n }\n return \"visitor\";\n}\n\n/**\n * Build an Auth.js v5 NextAuth() invocation. Each consuming app calls this\n * once at module scope and re-exports the returned `handlers`, `auth`,\n * `signIn`, `signOut`.\n *\n * Secrets default to env vars for simple deploys; pass them as options to\n * support runtime fetching from a secret store.\n */\nexport function createAuth(opts: CreateAuthOptions) {\n const isProd = opts.isProd ?? process.env.NODE_ENV === \"production\";\n const signInPage = opts.signInPage ?? \"/login\";\n const cookieDomain = isProd\n ? (opts.cookieDomain ?? process.env.AUTH_COOKIE_DOMAIN)\n : undefined;\n\n const SECRET =\n opts.secret ??\n process.env.AUTH_SECRET ??\n (isProd ? undefined : \"dev-only-fallback-not-for-prod\");\n\n const cognitoClientId = opts.cognito?.clientId ?? process.env.AUTH_COGNITO_ID;\n const cognitoClientSecret =\n opts.cognito?.clientSecret ?? process.env.AUTH_COGNITO_SECRET;\n const cognitoIssuer = opts.cognito?.issuer ?? process.env.AUTH_COGNITO_ISSUER;\n\n const config: NextAuthConfig = {\n secret: SECRET,\n cookies: cookieDomain\n ? {\n sessionToken: {\n name: \"authjs.session-token\",\n options: {\n domain: cookieDomain,\n sameSite: \"lax\",\n secure: true,\n httpOnly: true,\n path: \"/\",\n },\n },\n }\n : undefined,\n providers: isProd\n ? [\n Cognito({\n clientId: cognitoClientId,\n clientSecret: cognitoClientSecret,\n issuer: cognitoIssuer,\n }),\n ]\n : [\n Credentials({\n name: \"Mock role (dev only)\",\n credentials: {\n role: {\n label: \"Role\",\n type: \"text\",\n placeholder: \"any role string\",\n },\n },\n authorize: async (credentials) => {\n const role = credentials?.role as string | undefined;\n if (!role) return null;\n const display = role.charAt(0).toUpperCase() + role.slice(1);\n return {\n id: `mock-${role}`,\n name: `${display} (mock)`,\n email: `${role}@example.local`,\n role,\n };\n },\n }),\n ],\n session: { strategy: \"jwt\" },\n callbacks: {\n jwt: ({ token, user, profile }) => {\n if (user) {\n token.sub ??= user.id ?? undefined;\n token.email ??= user.email ?? undefined;\n if (!isProd && (user as { role?: string }).role) {\n (token as Record<string, unknown>)[\"cognito:groups\"] = [\n (user as { role: string }).role,\n ];\n }\n }\n if (isProd && profile) {\n const groups = (profile as Record<string, unknown>)[\"cognito:groups\"];\n if (groups) {\n (token as Record<string, unknown>)[\"cognito:groups\"] = groups;\n }\n }\n return token;\n },\n session: ({ session, token }) => {\n session.user.role = roleFromGroups(\n (token as Record<string, unknown>)[\"cognito:groups\"],\n );\n return session;\n },\n authorized: ({ auth: session, request: { nextUrl } }) => {\n const path = nextUrl.pathname;\n const isAuthedRoute = opts.authedRoutePrefixes.some(\n (prefix) => path === prefix || path.startsWith(`${prefix}/`),\n );\n if (!session && isAuthedRoute) return false;\n return true;\n },\n },\n pages: { signIn: signInPage },\n trustHost: true,\n };\n\n return NextAuth(config);\n}\n\n/**\n * Re-export of NextAuthConfig for consumers that want to extend the config\n * shape returned by createAuth().\n */\nexport type { NextAuthConfig } from \"next-auth\";\n"],"mappings":"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","names":["Cognito","Credentials","NextAuth"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Auth.js v5 (the package is still distributed as `next-auth`, but treat\n// these as Auth.js v5 internally — docs at https://authjs.dev, NOT\n// next-auth.js.org which is v4 and incompatible).\n//\n// Subdomain ecosystem model:\n// - One Cognito User Pool per tenant.\n// - One Cognito App Client with ONE callback URL at the apex\n// (https://<apex>/api/auth/callback/cognito).\n// - The apex app is the auth broker. Subdomain apps redirect through it.\n// - Session cookie scoped to Domain=.<apex> so every subdomain sees it.\n// - All apps use the same createAuth() invocation; the package derives\n// the right signInPage from appDomain + allowedParentDomain.\n//\n// Provider strategy:\n// - Production: Cognito OIDC. cognito:groups drives session.user.groups.\n// - Dev / preview: Credentials with a role picker, shaped to mirror\n// Cognito's claim payload (same groups, sub, email).\n\nimport NextAuth, {\n type DefaultSession,\n type NextAuthConfig,\n type Session,\n} from \"next-auth\";\nimport Credentials from \"next-auth/providers/credentials\";\nimport Cognito from \"next-auth/providers/cognito\";\n\ndeclare module \"next-auth\" {\n interface Session {\n user: {\n groups: string[];\n role: string;\n } & DefaultSession[\"user\"];\n }\n interface User {\n role?: string;\n groups?: string[];\n }\n}\n\nexport type CreateAuthOptions = {\n /** Path prefixes that require an authenticated session. */\n authedRoutePrefixes: string[];\n /**\n * Page to redirect to when an unauthed user hits a gated route.\n * If omitted, derived automatically from appDomain + allowedParentDomain:\n * apex app gets `/login`; subdomain apps get `https://<apex>/login`.\n */\n signInPage?: string;\n /**\n * Cookie Domain attribute. In subdomain ecosystems, set to the parent\n * (e.g. `.agency.aillc.link`). Default: process.env.AUTH_COOKIE_DOMAIN.\n * In dev (NODE_ENV !== \"production\") this is ignored — cookies stay\n * host-only so per-port localhost apps don't collide.\n */\n cookieDomain?: string;\n /**\n * The parent domain that all subdomain apps share (e.g.\n * `.agency.aillc.link`). The redirect callback uses this to allow\n * post-login redirects back to any subdomain of the parent (apex or\n * `<sub>.agency.aillc.link`). Default: process.env.AUTH_ALLOWED_PARENT_DOMAIN.\n */\n allowedParentDomain?: string;\n /**\n * This app's full FQDN (e.g. `agency.aillc.link` for the apex app, or\n * `leads.agency.aillc.link` for a subdomain app). Used to derive the\n * default signInPage. Default: process.env.APP_DOMAIN.\n */\n appDomain?: string;\n /** Override prod/dev detection. Default reads NODE_ENV. */\n isProd?: boolean;\n /**\n * The JWT signing secret. Default: process.env.AUTH_SECRET.\n * In prod, pass this from a runtime fetch (Secrets Manager) to keep the\n * secret out of Lambda env vars and to support rotation without redeploy.\n */\n secret?: string;\n cognito?: {\n clientId?: string;\n clientSecret?: string;\n issuer?: string;\n };\n};\n\n// ----- AuthError used by requireGroup -----\n\nexport class AuthError extends Error {\n constructor(public code: \"unauthenticated\" | \"forbidden\") {\n super(code);\n this.name = \"AuthError\";\n }\n}\n\n// ----- Group/authorization helpers -----\n\n/** Returns the user's Cognito groups (always an array, possibly empty). */\nexport function getUserGroups(session: Session | null | undefined): string[] {\n return session?.user?.groups ?? [];\n}\n\n/** Case-insensitive group membership check. */\nexport function hasGroup(session: Session | null | undefined, name: string): boolean {\n if (!session) return false;\n const target = name.toLowerCase();\n return getUserGroups(session).some((g) => g.toLowerCase() === target);\n}\n\n/**\n * Throws AuthError if no session (`unauthenticated`) or if the user is in\n * none of the provided groups (`forbidden`). Pass multiple names to allow\n * any-of.\n */\nexport function requireGroup(\n session: Session | null | undefined,\n ...names: string[]\n): void {\n if (!session) throw new AuthError(\"unauthenticated\");\n if (names.length === 0) return;\n const ok = names.some((n) => hasGroup(session, n));\n if (!ok) throw new AuthError(\"forbidden\");\n}\n\n// ----- Env validation -----\n\nfunction validateProdEnv(args: {\n isProd: boolean;\n cookieDomain: string | undefined;\n allowedParentDomain: string | undefined;\n appDomain: string | undefined;\n secret: string | undefined;\n cognitoClientId: string | undefined;\n cognitoClientSecret: string | undefined;\n cognitoIssuer: string | undefined;\n}): void {\n if (!args.isProd) return;\n // Skip during `next build` -- Cognito values come from SSM dynamic\n // refs and Secrets Manager at runtime in the deployed Lambda. Build\n // time only needs to compile route handlers; throwing here breaks\n // the build with no actionable fix (the values literally don't exist\n // yet at build time).\n if (process.env.NEXT_PHASE === \"phase-production-build\") return;\n const missing: string[] = [];\n if (!args.secret) missing.push(\"AUTH_SECRET\");\n if (!args.cognitoClientId) missing.push(\"AUTH_COGNITO_ID\");\n if (!args.cognitoClientSecret) missing.push(\"AUTH_COGNITO_SECRET\");\n if (!args.cognitoIssuer) missing.push(\"AUTH_COGNITO_ISSUER\");\n // Subdomain mode: if any of the three multi-domain values is set, all three must be.\n const hasAny = !!(args.cookieDomain || args.allowedParentDomain || args.appDomain);\n if (hasAny) {\n if (!args.cookieDomain) missing.push(\"AUTH_COOKIE_DOMAIN\");\n if (!args.allowedParentDomain) missing.push(\"AUTH_ALLOWED_PARENT_DOMAIN\");\n if (!args.appDomain) missing.push(\"APP_DOMAIN\");\n }\n if (missing.length > 0) {\n throw new Error(\n `[@augmenting-integrations/auth] Missing required prod env vars: ${missing.join(\n \", \",\n )}. Provide via createAuth() opts or process.env.`,\n );\n }\n}\n\n// ----- Redirect callback factory -----\n\nfunction buildRedirectCallback(allowedParentDomain: string | undefined) {\n return ({ url, baseUrl }: { url: string; baseUrl: string }): string => {\n try {\n const target = new URL(url, baseUrl);\n if (!allowedParentDomain) {\n return target.origin === new URL(baseUrl).origin ? target.toString() : baseUrl;\n }\n const apex = allowedParentDomain.replace(/^\\./, \"\").toLowerCase();\n const host = target.hostname.toLowerCase();\n const ok = host === apex || host.endsWith(`.${apex}`);\n return ok ? target.toString() : baseUrl;\n } catch {\n return baseUrl;\n }\n };\n}\n\n// ----- Sign-in page auto-derivation -----\n\nfunction deriveSignInPage(args: {\n signInPage: string | undefined;\n appDomain: string | undefined;\n allowedParentDomain: string | undefined;\n}): string {\n if (args.signInPage) return args.signInPage;\n if (args.appDomain && args.allowedParentDomain) {\n const apex = args.allowedParentDomain.replace(/^\\./, \"\");\n return args.appDomain === apex ? \"/login\" : `https://${apex}/login`;\n }\n return \"/login\";\n}\n\nfunction roleFromGroups(groups: unknown): string {\n if (Array.isArray(groups) && groups.length > 0) {\n return String(groups[0]).toLowerCase();\n }\n return \"visitor\";\n}\n\n// ----- Main factory -----\n\nexport function createAuth(opts: CreateAuthOptions) {\n const isProd = opts.isProd ?? process.env.NODE_ENV === \"production\";\n\n const cookieDomain = isProd\n ? (opts.cookieDomain ?? process.env.AUTH_COOKIE_DOMAIN)\n : undefined;\n const allowedParentDomain =\n opts.allowedParentDomain ?? process.env.AUTH_ALLOWED_PARENT_DOMAIN;\n const appDomain = opts.appDomain ?? process.env.APP_DOMAIN;\n\n const SECRET =\n opts.secret ??\n process.env.AUTH_SECRET ??\n (isProd ? undefined : \"dev-only-fallback-not-for-prod\");\n const cognitoClientId = opts.cognito?.clientId ?? process.env.AUTH_COGNITO_ID;\n const cognitoClientSecret =\n opts.cognito?.clientSecret ?? process.env.AUTH_COGNITO_SECRET;\n const cognitoIssuer = opts.cognito?.issuer ?? process.env.AUTH_COGNITO_ISSUER;\n\n validateProdEnv({\n isProd,\n cookieDomain,\n allowedParentDomain,\n appDomain,\n secret: SECRET,\n cognitoClientId,\n cognitoClientSecret,\n cognitoIssuer,\n });\n\n const signInPage = deriveSignInPage({\n signInPage: opts.signInPage,\n appDomain,\n allowedParentDomain,\n });\n\n const config: NextAuthConfig = {\n secret: SECRET,\n cookies: cookieDomain\n ? {\n sessionToken: {\n name: \"authjs.session-token\",\n options: {\n domain: cookieDomain,\n sameSite: \"lax\",\n secure: true,\n httpOnly: true,\n path: \"/\",\n },\n },\n }\n : undefined,\n providers: isProd\n ? [\n Cognito({\n clientId: cognitoClientId,\n clientSecret: cognitoClientSecret,\n issuer: cognitoIssuer,\n }),\n ]\n : [\n Credentials({\n name: \"Mock role (dev only)\",\n credentials: {\n role: {\n label: \"Role\",\n type: \"text\",\n placeholder: \"any role string\",\n },\n },\n authorize: async (credentials) => {\n const role = credentials?.role as string | undefined;\n if (!role) return null;\n const display = role.charAt(0).toUpperCase() + role.slice(1);\n return {\n id: `mock-${role}`,\n name: `${display} (mock)`,\n email: `${role}@example.local`,\n role,\n groups: [role],\n };\n },\n }),\n ],\n session: { strategy: \"jwt\" },\n callbacks: {\n jwt: ({ token, user, profile }) => {\n if (user) {\n token.sub ??= user.id ?? undefined;\n token.email ??= user.email ?? undefined;\n if (!isProd) {\n const u = user as { groups?: string[]; role?: string };\n const groups = u.groups ?? (u.role ? [u.role] : []);\n if (groups.length > 0) {\n (token as Record<string, unknown>)[\"cognito:groups\"] = groups;\n }\n }\n }\n if (isProd && profile) {\n const groups = (profile as Record<string, unknown>)[\"cognito:groups\"];\n if (groups) {\n (token as Record<string, unknown>)[\"cognito:groups\"] = groups;\n }\n }\n return token;\n },\n session: ({ session, token }) => {\n const groups =\n ((token as Record<string, unknown>)[\"cognito:groups\"] as\n | string[]\n | undefined) ?? [];\n session.user.groups = groups;\n session.user.role = roleFromGroups(groups);\n return session;\n },\n authorized: ({ auth: session, request: { nextUrl } }) => {\n const path = nextUrl.pathname;\n const isAuthedRoute = opts.authedRoutePrefixes.some(\n (prefix) => path === prefix || path.startsWith(`${prefix}/`),\n );\n if (!session && isAuthedRoute) return false;\n return true;\n },\n redirect: buildRedirectCallback(allowedParentDomain),\n },\n pages: { signIn: signInPage },\n trustHost: true,\n };\n\n return NextAuth(config);\n}\n\nexport type { NextAuthConfig } from \"next-auth\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBA,uBAIO;AACP,yBAAwB;AACxB,qBAAoB;AA6Db,IAAM,YAAN,cAAwB,MAAM;AAAA,EACnC,YAAmB,MAAuC;AACxD,UAAM,IAAI;AADO;AAEjB,SAAK,OAAO;AAAA,EACd;AAAA,EAHmB;AAIrB;AAKO,SAAS,cAAc,SAA+C;AAC3E,SAAO,SAAS,MAAM,UAAU,CAAC;AACnC;AAGO,SAAS,SAAS,SAAqC,MAAuB;AACnF,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,SAAS,KAAK,YAAY;AAChC,SAAO,cAAc,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,MAAM;AACtE;AAOO,SAAS,aACd,YACG,OACG;AACN,MAAI,CAAC,QAAS,OAAM,IAAI,UAAU,iBAAiB;AACnD,MAAI,MAAM,WAAW,EAAG;AACxB,QAAM,KAAK,MAAM,KAAK,CAAC,MAAM,SAAS,SAAS,CAAC,CAAC;AACjD,MAAI,CAAC,GAAI,OAAM,IAAI,UAAU,WAAW;AAC1C;AAIA,SAAS,gBAAgB,MAShB;AACP,MAAI,CAAC,KAAK,OAAQ;AAMlB,MAAI,QAAQ,IAAI,eAAe,yBAA0B;AACzD,QAAM,UAAoB,CAAC;AAC3B,MAAI,CAAC,KAAK,OAAQ,SAAQ,KAAK,aAAa;AAC5C,MAAI,CAAC,KAAK,gBAAiB,SAAQ,KAAK,iBAAiB;AACzD,MAAI,CAAC,KAAK,oBAAqB,SAAQ,KAAK,qBAAqB;AACjE,MAAI,CAAC,KAAK,cAAe,SAAQ,KAAK,qBAAqB;AAE3D,QAAM,SAAS,CAAC,EAAE,KAAK,gBAAgB,KAAK,uBAAuB,KAAK;AACxE,MAAI,QAAQ;AACV,QAAI,CAAC,KAAK,aAAc,SAAQ,KAAK,oBAAoB;AACzD,QAAI,CAAC,KAAK,oBAAqB,SAAQ,KAAK,4BAA4B;AACxE,QAAI,CAAC,KAAK,UAAW,SAAQ,KAAK,YAAY;AAAA,EAChD;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI;AAAA,MACR,mEAAmE,QAAQ;AAAA,QACzE;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAIA,SAAS,sBAAsB,qBAAyC;AACtE,SAAO,CAAC,EAAE,KAAK,QAAQ,MAAgD;AACrE,QAAI;AACF,YAAM,SAAS,IAAI,IAAI,KAAK,OAAO;AACnC,UAAI,CAAC,qBAAqB;AACxB,eAAO,OAAO,WAAW,IAAI,IAAI,OAAO,EAAE,SAAS,OAAO,SAAS,IAAI;AAAA,MACzE;AACA,YAAM,OAAO,oBAAoB,QAAQ,OAAO,EAAE,EAAE,YAAY;AAChE,YAAM,OAAO,OAAO,SAAS,YAAY;AACzC,YAAM,KAAK,SAAS,QAAQ,KAAK,SAAS,IAAI,IAAI,EAAE;AACpD,aAAO,KAAK,OAAO,SAAS,IAAI;AAAA,IAClC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAIA,SAAS,iBAAiB,MAIf;AACT,MAAI,KAAK,WAAY,QAAO,KAAK;AACjC,MAAI,KAAK,aAAa,KAAK,qBAAqB;AAC9C,UAAM,OAAO,KAAK,oBAAoB,QAAQ,OAAO,EAAE;AACvD,WAAO,KAAK,cAAc,OAAO,WAAW,WAAW,IAAI;AAAA,EAC7D;AACA,SAAO;AACT;AAEA,SAAS,eAAe,QAAyB;AAC/C,MAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,GAAG;AAC9C,WAAO,OAAO,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,EACvC;AACA,SAAO;AACT;AAIO,SAAS,WAAW,MAAyB;AAClD,QAAM,SAAS,KAAK,UAAU,QAAQ,IAAI,aAAa;AAEvD,QAAM,eAAe,SAChB,KAAK,gBAAgB,QAAQ,IAAI,qBAClC;AACJ,QAAM,sBACJ,KAAK,uBAAuB,QAAQ,IAAI;AAC1C,QAAM,YAAY,KAAK,aAAa,QAAQ,IAAI;AAEhD,QAAM,SACJ,KAAK,UACL,QAAQ,IAAI,gBACX,SAAS,SAAY;AACxB,QAAM,kBAAkB,KAAK,SAAS,YAAY,QAAQ,IAAI;AAC9D,QAAM,sBACJ,KAAK,SAAS,gBAAgB,QAAQ,IAAI;AAC5C,QAAM,gBAAgB,KAAK,SAAS,UAAU,QAAQ,IAAI;AAE1D,kBAAgB;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,aAAa,iBAAiB;AAAA,IAClC,YAAY,KAAK;AAAA,IACjB;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,SAAyB;AAAA,IAC7B,QAAQ;AAAA,IACR,SAAS,eACL;AAAA,MACE,cAAc;AAAA,QACZ,MAAM;AAAA,QACN,SAAS;AAAA,UACP,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF,IACA;AAAA,IACJ,WAAW,SACP;AAAA,UACE,eAAAA,SAAQ;AAAA,QACN,UAAU;AAAA,QACV,cAAc;AAAA,QACd,QAAQ;AAAA,MACV,CAAC;AAAA,IACH,IACA;AAAA,UACE,mBAAAC,SAAY;AAAA,QACV,MAAM;AAAA,QACN,aAAa;AAAA,UACX,MAAM;AAAA,YACJ,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,QACF;AAAA,QACA,WAAW,OAAO,gBAAgB;AAChC,gBAAM,OAAO,aAAa;AAC1B,cAAI,CAAC,KAAM,QAAO;AAClB,gBAAM,UAAU,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC;AAC3D,iBAAO;AAAA,YACL,IAAI,QAAQ,IAAI;AAAA,YAChB,MAAM,GAAG,OAAO;AAAA,YAChB,OAAO,GAAG,IAAI;AAAA,YACd;AAAA,YACA,QAAQ,CAAC,IAAI;AAAA,UACf;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACJ,SAAS,EAAE,UAAU,MAAM;AAAA,IAC3B,WAAW;AAAA,MACT,KAAK,CAAC,EAAE,OAAO,MAAM,QAAQ,MAAM;AACjC,YAAI,MAAM;AACR,gBAAM,QAAQ,KAAK,MAAM;AACzB,gBAAM,UAAU,KAAK,SAAS;AAC9B,cAAI,CAAC,QAAQ;AACX,kBAAM,IAAI;AACV,kBAAM,SAAS,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,IAAI,IAAI,CAAC;AACjD,gBAAI,OAAO,SAAS,GAAG;AACrB,cAAC,MAAkC,gBAAgB,IAAI;AAAA,YACzD;AAAA,UACF;AAAA,QACF;AACA,YAAI,UAAU,SAAS;AACrB,gBAAM,SAAU,QAAoC,gBAAgB;AACpE,cAAI,QAAQ;AACV,YAAC,MAAkC,gBAAgB,IAAI;AAAA,UACzD;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,MACA,SAAS,CAAC,EAAE,SAAS,MAAM,MAAM;AAC/B,cAAM,SACF,MAAkC,gBAAgB,KAElC,CAAC;AACrB,gBAAQ,KAAK,SAAS;AACtB,gBAAQ,KAAK,OAAO,eAAe,MAAM;AACzC,eAAO;AAAA,MACT;AAAA,MACA,YAAY,CAAC,EAAE,MAAM,SAAS,SAAS,EAAE,QAAQ,EAAE,MAAM;AACvD,cAAM,OAAO,QAAQ;AACrB,cAAM,gBAAgB,KAAK,oBAAoB;AAAA,UAC7C,CAAC,WAAW,SAAS,UAAU,KAAK,WAAW,GAAG,MAAM,GAAG;AAAA,QAC7D;AACA,YAAI,CAAC,WAAW,cAAe,QAAO;AACtC,eAAO;AAAA,MACT;AAAA,MACA,UAAU,sBAAsB,mBAAmB;AAAA,IACrD;AAAA,IACA,OAAO,EAAE,QAAQ,WAAW;AAAA,IAC5B,WAAW;AAAA,EACb;AAEA,aAAO,iBAAAC,SAAS,MAAM;AACxB;","names":["Cognito","Credentials","NextAuth"]}
package/dist/index.js CHANGED
@@ -2,6 +2,74 @@
2
2
  import NextAuth from "next-auth";
3
3
  import Credentials from "next-auth/providers/credentials";
4
4
  import Cognito from "next-auth/providers/cognito";
5
+ var AuthError = class extends Error {
6
+ constructor(code) {
7
+ super(code);
8
+ this.code = code;
9
+ this.name = "AuthError";
10
+ }
11
+ code;
12
+ };
13
+ function getUserGroups(session) {
14
+ return session?.user?.groups ?? [];
15
+ }
16
+ function hasGroup(session, name) {
17
+ if (!session) return false;
18
+ const target = name.toLowerCase();
19
+ return getUserGroups(session).some((g) => g.toLowerCase() === target);
20
+ }
21
+ function requireGroup(session, ...names) {
22
+ if (!session) throw new AuthError("unauthenticated");
23
+ if (names.length === 0) return;
24
+ const ok = names.some((n) => hasGroup(session, n));
25
+ if (!ok) throw new AuthError("forbidden");
26
+ }
27
+ function validateProdEnv(args) {
28
+ if (!args.isProd) return;
29
+ if (process.env.NEXT_PHASE === "phase-production-build") return;
30
+ const missing = [];
31
+ if (!args.secret) missing.push("AUTH_SECRET");
32
+ if (!args.cognitoClientId) missing.push("AUTH_COGNITO_ID");
33
+ if (!args.cognitoClientSecret) missing.push("AUTH_COGNITO_SECRET");
34
+ if (!args.cognitoIssuer) missing.push("AUTH_COGNITO_ISSUER");
35
+ const hasAny = !!(args.cookieDomain || args.allowedParentDomain || args.appDomain);
36
+ if (hasAny) {
37
+ if (!args.cookieDomain) missing.push("AUTH_COOKIE_DOMAIN");
38
+ if (!args.allowedParentDomain) missing.push("AUTH_ALLOWED_PARENT_DOMAIN");
39
+ if (!args.appDomain) missing.push("APP_DOMAIN");
40
+ }
41
+ if (missing.length > 0) {
42
+ throw new Error(
43
+ `[@augmenting-integrations/auth] Missing required prod env vars: ${missing.join(
44
+ ", "
45
+ )}. Provide via createAuth() opts or process.env.`
46
+ );
47
+ }
48
+ }
49
+ function buildRedirectCallback(allowedParentDomain) {
50
+ return ({ url, baseUrl }) => {
51
+ try {
52
+ const target = new URL(url, baseUrl);
53
+ if (!allowedParentDomain) {
54
+ return target.origin === new URL(baseUrl).origin ? target.toString() : baseUrl;
55
+ }
56
+ const apex = allowedParentDomain.replace(/^\./, "").toLowerCase();
57
+ const host = target.hostname.toLowerCase();
58
+ const ok = host === apex || host.endsWith(`.${apex}`);
59
+ return ok ? target.toString() : baseUrl;
60
+ } catch {
61
+ return baseUrl;
62
+ }
63
+ };
64
+ }
65
+ function deriveSignInPage(args) {
66
+ if (args.signInPage) return args.signInPage;
67
+ if (args.appDomain && args.allowedParentDomain) {
68
+ const apex = args.allowedParentDomain.replace(/^\./, "");
69
+ return args.appDomain === apex ? "/login" : `https://${apex}/login`;
70
+ }
71
+ return "/login";
72
+ }
5
73
  function roleFromGroups(groups) {
6
74
  if (Array.isArray(groups) && groups.length > 0) {
7
75
  return String(groups[0]).toLowerCase();
@@ -10,12 +78,28 @@ function roleFromGroups(groups) {
10
78
  }
11
79
  function createAuth(opts) {
12
80
  const isProd = opts.isProd ?? process.env.NODE_ENV === "production";
13
- const signInPage = opts.signInPage ?? "/login";
14
81
  const cookieDomain = isProd ? opts.cookieDomain ?? process.env.AUTH_COOKIE_DOMAIN : void 0;
82
+ const allowedParentDomain = opts.allowedParentDomain ?? process.env.AUTH_ALLOWED_PARENT_DOMAIN;
83
+ const appDomain = opts.appDomain ?? process.env.APP_DOMAIN;
15
84
  const SECRET = opts.secret ?? process.env.AUTH_SECRET ?? (isProd ? void 0 : "dev-only-fallback-not-for-prod");
16
85
  const cognitoClientId = opts.cognito?.clientId ?? process.env.AUTH_COGNITO_ID;
17
86
  const cognitoClientSecret = opts.cognito?.clientSecret ?? process.env.AUTH_COGNITO_SECRET;
18
87
  const cognitoIssuer = opts.cognito?.issuer ?? process.env.AUTH_COGNITO_ISSUER;
88
+ validateProdEnv({
89
+ isProd,
90
+ cookieDomain,
91
+ allowedParentDomain,
92
+ appDomain,
93
+ secret: SECRET,
94
+ cognitoClientId,
95
+ cognitoClientSecret,
96
+ cognitoIssuer
97
+ });
98
+ const signInPage = deriveSignInPage({
99
+ signInPage: opts.signInPage,
100
+ appDomain,
101
+ allowedParentDomain
102
+ });
19
103
  const config = {
20
104
  secret: SECRET,
21
105
  cookies: cookieDomain ? {
@@ -54,7 +138,8 @@ function createAuth(opts) {
54
138
  id: `mock-${role}`,
55
139
  name: `${display} (mock)`,
56
140
  email: `${role}@example.local`,
57
- role
141
+ role,
142
+ groups: [role]
58
143
  };
59
144
  }
60
145
  })
@@ -65,10 +150,12 @@ function createAuth(opts) {
65
150
  if (user) {
66
151
  token.sub ??= user.id ?? void 0;
67
152
  token.email ??= user.email ?? void 0;
68
- if (!isProd && user.role) {
69
- token["cognito:groups"] = [
70
- user.role
71
- ];
153
+ if (!isProd) {
154
+ const u = user;
155
+ const groups = u.groups ?? (u.role ? [u.role] : []);
156
+ if (groups.length > 0) {
157
+ token["cognito:groups"] = groups;
158
+ }
72
159
  }
73
160
  }
74
161
  if (isProd && profile) {
@@ -80,9 +167,9 @@ function createAuth(opts) {
80
167
  return token;
81
168
  },
82
169
  session: ({ session, token }) => {
83
- session.user.role = roleFromGroups(
84
- token["cognito:groups"]
85
- );
170
+ const groups = token["cognito:groups"] ?? [];
171
+ session.user.groups = groups;
172
+ session.user.role = roleFromGroups(groups);
86
173
  return session;
87
174
  },
88
175
  authorized: ({ auth: session, request: { nextUrl } }) => {
@@ -92,7 +179,8 @@ function createAuth(opts) {
92
179
  );
93
180
  if (!session && isAuthedRoute) return false;
94
181
  return true;
95
- }
182
+ },
183
+ redirect: buildRedirectCallback(allowedParentDomain)
96
184
  },
97
185
  pages: { signIn: signInPage },
98
186
  trustHost: true
@@ -100,6 +188,10 @@ function createAuth(opts) {
100
188
  return NextAuth(config);
101
189
  }
102
190
  export {
103
- createAuth
191
+ AuthError,
192
+ createAuth,
193
+ getUserGroups,
194
+ hasGroup,
195
+ requireGroup
104
196
  };
105
197
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Auth.js v5 (the package is still distributed as `next-auth`, but treat\n// these as Auth.js v5 internally — docs at https://authjs.dev, NOT\n// next-auth.js.org which is v4 and incompatible).\n//\n// Provider strategy:\n// - Production: Cognito OIDC. Group membership from `cognito:groups`\n// drives role.\n// - Dev / preview: Credentials with a role picker, BUT shaped to mirror\n// Cognito's claim payload so consumers (callbacks, gates, audit logs)\n// never branch on environment. Same `sub`, `email`, `cognito:groups`\n// in both modes.\n//\n// IMPORTANT: do not let the dev-mode session diverge from production. If\n// you add a claim in prod, also add it (with a synthetic value) in dev.\n\nimport NextAuth, { type DefaultSession, type NextAuthConfig } from \"next-auth\";\nimport Credentials from \"next-auth/providers/credentials\";\nimport Cognito from \"next-auth/providers/cognito\";\n\n// Role is intentionally `string`. Consumer apps narrow it (e.g. via local\n// module augmentation or runtime check) to whatever taxonomy their Cognito\n// user pool defines. The auth package itself makes no assumptions about\n// what roles exist.\ndeclare module \"next-auth\" {\n interface Session {\n user: {\n role: string;\n } & DefaultSession[\"user\"];\n }\n interface User {\n role: string;\n }\n}\n\nexport type CreateAuthOptions = {\n /**\n * Path prefixes that require an authenticated session.\n * Empty array = no gating (rare).\n */\n authedRoutePrefixes: string[];\n /**\n * Page to redirect to when an unauthed user hits a gated route. Can be a\n * relative path (`\"/login\"`) for in-app login, or a fully-qualified URL\n * (`\"https://example.com/login\"`) to delegate login to a peer app on a\n * sibling subdomain or the apex of a path-routed deployment.\n * Default: `/login`.\n */\n signInPage?: string;\n /**\n * Cookie domain for the session token. Set this to the parent domain\n * (e.g. `.example.com`) when running across multiple subdomains so the\n * session JWT is readable by every app sharing that parent. For\n * path-routed deployments under a single domain, leave unset (host-only\n * is correct).\n *\n * Default: read from `process.env.AUTH_COOKIE_DOMAIN`; if unset, Auth.js\n * defaults to host-only (current request hostname).\n *\n * In dev (NODE_ENV !== \"production\"), this is ignored — cookies stay\n * scoped to localhost so per-port apps don't collide.\n */\n cookieDomain?: string;\n /**\n * Override prod/dev detection. Default reads NODE_ENV.\n */\n isProd?: boolean;\n /**\n * The JWT signing secret. If not provided, falls back to\n * `process.env.AUTH_SECRET`. Pass this from a runtime fetch (e.g. AWS\n * Secrets Manager via @aws-sdk/client-secrets-manager) to keep the secret\n * out of Lambda environment variables and to support rotation without\n * redeploy (Lambda containers re-init periodically and pick up the new\n * value).\n */\n secret?: string;\n /**\n * Cognito OIDC provider config. Each field falls back to the matching\n * `AUTH_COGNITO_*` env var if not provided. Provide them explicitly to\n * fetch the client secret from Secrets Manager at runtime.\n */\n cognito?: {\n clientId?: string;\n clientSecret?: string;\n issuer?: string;\n };\n};\n\nfunction roleFromGroups(groups: unknown): string {\n if (Array.isArray(groups) && groups.length > 0) {\n return String(groups[0]).toLowerCase();\n }\n return \"visitor\";\n}\n\n/**\n * Build an Auth.js v5 NextAuth() invocation. Each consuming app calls this\n * once at module scope and re-exports the returned `handlers`, `auth`,\n * `signIn`, `signOut`.\n *\n * Secrets default to env vars for simple deploys; pass them as options to\n * support runtime fetching from a secret store.\n */\nexport function createAuth(opts: CreateAuthOptions) {\n const isProd = opts.isProd ?? process.env.NODE_ENV === \"production\";\n const signInPage = opts.signInPage ?? \"/login\";\n const cookieDomain = isProd\n ? (opts.cookieDomain ?? process.env.AUTH_COOKIE_DOMAIN)\n : undefined;\n\n const SECRET =\n opts.secret ??\n process.env.AUTH_SECRET ??\n (isProd ? undefined : \"dev-only-fallback-not-for-prod\");\n\n const cognitoClientId = opts.cognito?.clientId ?? process.env.AUTH_COGNITO_ID;\n const cognitoClientSecret =\n opts.cognito?.clientSecret ?? process.env.AUTH_COGNITO_SECRET;\n const cognitoIssuer = opts.cognito?.issuer ?? process.env.AUTH_COGNITO_ISSUER;\n\n const config: NextAuthConfig = {\n secret: SECRET,\n cookies: cookieDomain\n ? {\n sessionToken: {\n name: \"authjs.session-token\",\n options: {\n domain: cookieDomain,\n sameSite: \"lax\",\n secure: true,\n httpOnly: true,\n path: \"/\",\n },\n },\n }\n : undefined,\n providers: isProd\n ? [\n Cognito({\n clientId: cognitoClientId,\n clientSecret: cognitoClientSecret,\n issuer: cognitoIssuer,\n }),\n ]\n : [\n Credentials({\n name: \"Mock role (dev only)\",\n credentials: {\n role: {\n label: \"Role\",\n type: \"text\",\n placeholder: \"any role string\",\n },\n },\n authorize: async (credentials) => {\n const role = credentials?.role as string | undefined;\n if (!role) return null;\n const display = role.charAt(0).toUpperCase() + role.slice(1);\n return {\n id: `mock-${role}`,\n name: `${display} (mock)`,\n email: `${role}@example.local`,\n role,\n };\n },\n }),\n ],\n session: { strategy: \"jwt\" },\n callbacks: {\n jwt: ({ token, user, profile }) => {\n if (user) {\n token.sub ??= user.id ?? undefined;\n token.email ??= user.email ?? undefined;\n if (!isProd && (user as { role?: string }).role) {\n (token as Record<string, unknown>)[\"cognito:groups\"] = [\n (user as { role: string }).role,\n ];\n }\n }\n if (isProd && profile) {\n const groups = (profile as Record<string, unknown>)[\"cognito:groups\"];\n if (groups) {\n (token as Record<string, unknown>)[\"cognito:groups\"] = groups;\n }\n }\n return token;\n },\n session: ({ session, token }) => {\n session.user.role = roleFromGroups(\n (token as Record<string, unknown>)[\"cognito:groups\"],\n );\n return session;\n },\n authorized: ({ auth: session, request: { nextUrl } }) => {\n const path = nextUrl.pathname;\n const isAuthedRoute = opts.authedRoutePrefixes.some(\n (prefix) => path === prefix || path.startsWith(`${prefix}/`),\n );\n if (!session && isAuthedRoute) return false;\n return true;\n },\n },\n pages: { signIn: signInPage },\n trustHost: true,\n };\n\n return NextAuth(config);\n}\n\n/**\n * Re-export of NextAuthConfig for consumers that want to extend the config\n * shape returned by createAuth().\n */\nexport type { NextAuthConfig } from \"next-auth\";\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Auth.js v5 (the package is still distributed as `next-auth`, but treat\n// these as Auth.js v5 internally — docs at https://authjs.dev, NOT\n// next-auth.js.org which is v4 and incompatible).\n//\n// Subdomain ecosystem model:\n// - One Cognito User Pool per tenant.\n// - One Cognito App Client with ONE callback URL at the apex\n// (https://<apex>/api/auth/callback/cognito).\n// - The apex app is the auth broker. Subdomain apps redirect through it.\n// - Session cookie scoped to Domain=.<apex> so every subdomain sees it.\n// - All apps use the same createAuth() invocation; the package derives\n// the right signInPage from appDomain + allowedParentDomain.\n//\n// Provider strategy:\n// - Production: Cognito OIDC. cognito:groups drives session.user.groups.\n// - Dev / preview: Credentials with a role picker, shaped to mirror\n// Cognito's claim payload (same groups, sub, email).\n\nimport NextAuth, {\n type DefaultSession,\n type NextAuthConfig,\n type Session,\n} from \"next-auth\";\nimport Credentials from \"next-auth/providers/credentials\";\nimport Cognito from \"next-auth/providers/cognito\";\n\ndeclare module \"next-auth\" {\n interface Session {\n user: {\n groups: string[];\n role: string;\n } & DefaultSession[\"user\"];\n }\n interface User {\n role?: string;\n groups?: string[];\n }\n}\n\nexport type CreateAuthOptions = {\n /** Path prefixes that require an authenticated session. */\n authedRoutePrefixes: string[];\n /**\n * Page to redirect to when an unauthed user hits a gated route.\n * If omitted, derived automatically from appDomain + allowedParentDomain:\n * apex app gets `/login`; subdomain apps get `https://<apex>/login`.\n */\n signInPage?: string;\n /**\n * Cookie Domain attribute. In subdomain ecosystems, set to the parent\n * (e.g. `.agency.aillc.link`). Default: process.env.AUTH_COOKIE_DOMAIN.\n * In dev (NODE_ENV !== \"production\") this is ignored — cookies stay\n * host-only so per-port localhost apps don't collide.\n */\n cookieDomain?: string;\n /**\n * The parent domain that all subdomain apps share (e.g.\n * `.agency.aillc.link`). The redirect callback uses this to allow\n * post-login redirects back to any subdomain of the parent (apex or\n * `<sub>.agency.aillc.link`). Default: process.env.AUTH_ALLOWED_PARENT_DOMAIN.\n */\n allowedParentDomain?: string;\n /**\n * This app's full FQDN (e.g. `agency.aillc.link` for the apex app, or\n * `leads.agency.aillc.link` for a subdomain app). Used to derive the\n * default signInPage. Default: process.env.APP_DOMAIN.\n */\n appDomain?: string;\n /** Override prod/dev detection. Default reads NODE_ENV. */\n isProd?: boolean;\n /**\n * The JWT signing secret. Default: process.env.AUTH_SECRET.\n * In prod, pass this from a runtime fetch (Secrets Manager) to keep the\n * secret out of Lambda env vars and to support rotation without redeploy.\n */\n secret?: string;\n cognito?: {\n clientId?: string;\n clientSecret?: string;\n issuer?: string;\n };\n};\n\n// ----- AuthError used by requireGroup -----\n\nexport class AuthError extends Error {\n constructor(public code: \"unauthenticated\" | \"forbidden\") {\n super(code);\n this.name = \"AuthError\";\n }\n}\n\n// ----- Group/authorization helpers -----\n\n/** Returns the user's Cognito groups (always an array, possibly empty). */\nexport function getUserGroups(session: Session | null | undefined): string[] {\n return session?.user?.groups ?? [];\n}\n\n/** Case-insensitive group membership check. */\nexport function hasGroup(session: Session | null | undefined, name: string): boolean {\n if (!session) return false;\n const target = name.toLowerCase();\n return getUserGroups(session).some((g) => g.toLowerCase() === target);\n}\n\n/**\n * Throws AuthError if no session (`unauthenticated`) or if the user is in\n * none of the provided groups (`forbidden`). Pass multiple names to allow\n * any-of.\n */\nexport function requireGroup(\n session: Session | null | undefined,\n ...names: string[]\n): void {\n if (!session) throw new AuthError(\"unauthenticated\");\n if (names.length === 0) return;\n const ok = names.some((n) => hasGroup(session, n));\n if (!ok) throw new AuthError(\"forbidden\");\n}\n\n// ----- Env validation -----\n\nfunction validateProdEnv(args: {\n isProd: boolean;\n cookieDomain: string | undefined;\n allowedParentDomain: string | undefined;\n appDomain: string | undefined;\n secret: string | undefined;\n cognitoClientId: string | undefined;\n cognitoClientSecret: string | undefined;\n cognitoIssuer: string | undefined;\n}): void {\n if (!args.isProd) return;\n // Skip during `next build` -- Cognito values come from SSM dynamic\n // refs and Secrets Manager at runtime in the deployed Lambda. Build\n // time only needs to compile route handlers; throwing here breaks\n // the build with no actionable fix (the values literally don't exist\n // yet at build time).\n if (process.env.NEXT_PHASE === \"phase-production-build\") return;\n const missing: string[] = [];\n if (!args.secret) missing.push(\"AUTH_SECRET\");\n if (!args.cognitoClientId) missing.push(\"AUTH_COGNITO_ID\");\n if (!args.cognitoClientSecret) missing.push(\"AUTH_COGNITO_SECRET\");\n if (!args.cognitoIssuer) missing.push(\"AUTH_COGNITO_ISSUER\");\n // Subdomain mode: if any of the three multi-domain values is set, all three must be.\n const hasAny = !!(args.cookieDomain || args.allowedParentDomain || args.appDomain);\n if (hasAny) {\n if (!args.cookieDomain) missing.push(\"AUTH_COOKIE_DOMAIN\");\n if (!args.allowedParentDomain) missing.push(\"AUTH_ALLOWED_PARENT_DOMAIN\");\n if (!args.appDomain) missing.push(\"APP_DOMAIN\");\n }\n if (missing.length > 0) {\n throw new Error(\n `[@augmenting-integrations/auth] Missing required prod env vars: ${missing.join(\n \", \",\n )}. Provide via createAuth() opts or process.env.`,\n );\n }\n}\n\n// ----- Redirect callback factory -----\n\nfunction buildRedirectCallback(allowedParentDomain: string | undefined) {\n return ({ url, baseUrl }: { url: string; baseUrl: string }): string => {\n try {\n const target = new URL(url, baseUrl);\n if (!allowedParentDomain) {\n return target.origin === new URL(baseUrl).origin ? target.toString() : baseUrl;\n }\n const apex = allowedParentDomain.replace(/^\\./, \"\").toLowerCase();\n const host = target.hostname.toLowerCase();\n const ok = host === apex || host.endsWith(`.${apex}`);\n return ok ? target.toString() : baseUrl;\n } catch {\n return baseUrl;\n }\n };\n}\n\n// ----- Sign-in page auto-derivation -----\n\nfunction deriveSignInPage(args: {\n signInPage: string | undefined;\n appDomain: string | undefined;\n allowedParentDomain: string | undefined;\n}): string {\n if (args.signInPage) return args.signInPage;\n if (args.appDomain && args.allowedParentDomain) {\n const apex = args.allowedParentDomain.replace(/^\\./, \"\");\n return args.appDomain === apex ? \"/login\" : `https://${apex}/login`;\n }\n return \"/login\";\n}\n\nfunction roleFromGroups(groups: unknown): string {\n if (Array.isArray(groups) && groups.length > 0) {\n return String(groups[0]).toLowerCase();\n }\n return \"visitor\";\n}\n\n// ----- Main factory -----\n\nexport function createAuth(opts: CreateAuthOptions) {\n const isProd = opts.isProd ?? process.env.NODE_ENV === \"production\";\n\n const cookieDomain = isProd\n ? (opts.cookieDomain ?? process.env.AUTH_COOKIE_DOMAIN)\n : undefined;\n const allowedParentDomain =\n opts.allowedParentDomain ?? process.env.AUTH_ALLOWED_PARENT_DOMAIN;\n const appDomain = opts.appDomain ?? process.env.APP_DOMAIN;\n\n const SECRET =\n opts.secret ??\n process.env.AUTH_SECRET ??\n (isProd ? undefined : \"dev-only-fallback-not-for-prod\");\n const cognitoClientId = opts.cognito?.clientId ?? process.env.AUTH_COGNITO_ID;\n const cognitoClientSecret =\n opts.cognito?.clientSecret ?? process.env.AUTH_COGNITO_SECRET;\n const cognitoIssuer = opts.cognito?.issuer ?? process.env.AUTH_COGNITO_ISSUER;\n\n validateProdEnv({\n isProd,\n cookieDomain,\n allowedParentDomain,\n appDomain,\n secret: SECRET,\n cognitoClientId,\n cognitoClientSecret,\n cognitoIssuer,\n });\n\n const signInPage = deriveSignInPage({\n signInPage: opts.signInPage,\n appDomain,\n allowedParentDomain,\n });\n\n const config: NextAuthConfig = {\n secret: SECRET,\n cookies: cookieDomain\n ? {\n sessionToken: {\n name: \"authjs.session-token\",\n options: {\n domain: cookieDomain,\n sameSite: \"lax\",\n secure: true,\n httpOnly: true,\n path: \"/\",\n },\n },\n }\n : undefined,\n providers: isProd\n ? [\n Cognito({\n clientId: cognitoClientId,\n clientSecret: cognitoClientSecret,\n issuer: cognitoIssuer,\n }),\n ]\n : [\n Credentials({\n name: \"Mock role (dev only)\",\n credentials: {\n role: {\n label: \"Role\",\n type: \"text\",\n placeholder: \"any role string\",\n },\n },\n authorize: async (credentials) => {\n const role = credentials?.role as string | undefined;\n if (!role) return null;\n const display = role.charAt(0).toUpperCase() + role.slice(1);\n return {\n id: `mock-${role}`,\n name: `${display} (mock)`,\n email: `${role}@example.local`,\n role,\n groups: [role],\n };\n },\n }),\n ],\n session: { strategy: \"jwt\" },\n callbacks: {\n jwt: ({ token, user, profile }) => {\n if (user) {\n token.sub ??= user.id ?? undefined;\n token.email ??= user.email ?? undefined;\n if (!isProd) {\n const u = user as { groups?: string[]; role?: string };\n const groups = u.groups ?? (u.role ? [u.role] : []);\n if (groups.length > 0) {\n (token as Record<string, unknown>)[\"cognito:groups\"] = groups;\n }\n }\n }\n if (isProd && profile) {\n const groups = (profile as Record<string, unknown>)[\"cognito:groups\"];\n if (groups) {\n (token as Record<string, unknown>)[\"cognito:groups\"] = groups;\n }\n }\n return token;\n },\n session: ({ session, token }) => {\n const groups =\n ((token as Record<string, unknown>)[\"cognito:groups\"] as\n | string[]\n | undefined) ?? [];\n session.user.groups = groups;\n session.user.role = roleFromGroups(groups);\n return session;\n },\n authorized: ({ auth: session, request: { nextUrl } }) => {\n const path = nextUrl.pathname;\n const isAuthedRoute = opts.authedRoutePrefixes.some(\n (prefix) => path === prefix || path.startsWith(`${prefix}/`),\n );\n if (!session && isAuthedRoute) return false;\n return true;\n },\n redirect: buildRedirectCallback(allowedParentDomain),\n },\n pages: { signIn: signInPage },\n trustHost: true,\n };\n\n return NextAuth(config);\n}\n\nexport type { NextAuthConfig } from \"next-auth\";\n"],"mappings":";AAkBA,OAAO,cAIA;AACP,OAAO,iBAAiB;AACxB,OAAO,aAAa;AA6Db,IAAM,YAAN,cAAwB,MAAM;AAAA,EACnC,YAAmB,MAAuC;AACxD,UAAM,IAAI;AADO;AAEjB,SAAK,OAAO;AAAA,EACd;AAAA,EAHmB;AAIrB;AAKO,SAAS,cAAc,SAA+C;AAC3E,SAAO,SAAS,MAAM,UAAU,CAAC;AACnC;AAGO,SAAS,SAAS,SAAqC,MAAuB;AACnF,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,SAAS,KAAK,YAAY;AAChC,SAAO,cAAc,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,MAAM;AACtE;AAOO,SAAS,aACd,YACG,OACG;AACN,MAAI,CAAC,QAAS,OAAM,IAAI,UAAU,iBAAiB;AACnD,MAAI,MAAM,WAAW,EAAG;AACxB,QAAM,KAAK,MAAM,KAAK,CAAC,MAAM,SAAS,SAAS,CAAC,CAAC;AACjD,MAAI,CAAC,GAAI,OAAM,IAAI,UAAU,WAAW;AAC1C;AAIA,SAAS,gBAAgB,MAShB;AACP,MAAI,CAAC,KAAK,OAAQ;AAMlB,MAAI,QAAQ,IAAI,eAAe,yBAA0B;AACzD,QAAM,UAAoB,CAAC;AAC3B,MAAI,CAAC,KAAK,OAAQ,SAAQ,KAAK,aAAa;AAC5C,MAAI,CAAC,KAAK,gBAAiB,SAAQ,KAAK,iBAAiB;AACzD,MAAI,CAAC,KAAK,oBAAqB,SAAQ,KAAK,qBAAqB;AACjE,MAAI,CAAC,KAAK,cAAe,SAAQ,KAAK,qBAAqB;AAE3D,QAAM,SAAS,CAAC,EAAE,KAAK,gBAAgB,KAAK,uBAAuB,KAAK;AACxE,MAAI,QAAQ;AACV,QAAI,CAAC,KAAK,aAAc,SAAQ,KAAK,oBAAoB;AACzD,QAAI,CAAC,KAAK,oBAAqB,SAAQ,KAAK,4BAA4B;AACxE,QAAI,CAAC,KAAK,UAAW,SAAQ,KAAK,YAAY;AAAA,EAChD;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI;AAAA,MACR,mEAAmE,QAAQ;AAAA,QACzE;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAIA,SAAS,sBAAsB,qBAAyC;AACtE,SAAO,CAAC,EAAE,KAAK,QAAQ,MAAgD;AACrE,QAAI;AACF,YAAM,SAAS,IAAI,IAAI,KAAK,OAAO;AACnC,UAAI,CAAC,qBAAqB;AACxB,eAAO,OAAO,WAAW,IAAI,IAAI,OAAO,EAAE,SAAS,OAAO,SAAS,IAAI;AAAA,MACzE;AACA,YAAM,OAAO,oBAAoB,QAAQ,OAAO,EAAE,EAAE,YAAY;AAChE,YAAM,OAAO,OAAO,SAAS,YAAY;AACzC,YAAM,KAAK,SAAS,QAAQ,KAAK,SAAS,IAAI,IAAI,EAAE;AACpD,aAAO,KAAK,OAAO,SAAS,IAAI;AAAA,IAClC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAIA,SAAS,iBAAiB,MAIf;AACT,MAAI,KAAK,WAAY,QAAO,KAAK;AACjC,MAAI,KAAK,aAAa,KAAK,qBAAqB;AAC9C,UAAM,OAAO,KAAK,oBAAoB,QAAQ,OAAO,EAAE;AACvD,WAAO,KAAK,cAAc,OAAO,WAAW,WAAW,IAAI;AAAA,EAC7D;AACA,SAAO;AACT;AAEA,SAAS,eAAe,QAAyB;AAC/C,MAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,GAAG;AAC9C,WAAO,OAAO,OAAO,CAAC,CAAC,EAAE,YAAY;AAAA,EACvC;AACA,SAAO;AACT;AAIO,SAAS,WAAW,MAAyB;AAClD,QAAM,SAAS,KAAK,UAAU,QAAQ,IAAI,aAAa;AAEvD,QAAM,eAAe,SAChB,KAAK,gBAAgB,QAAQ,IAAI,qBAClC;AACJ,QAAM,sBACJ,KAAK,uBAAuB,QAAQ,IAAI;AAC1C,QAAM,YAAY,KAAK,aAAa,QAAQ,IAAI;AAEhD,QAAM,SACJ,KAAK,UACL,QAAQ,IAAI,gBACX,SAAS,SAAY;AACxB,QAAM,kBAAkB,KAAK,SAAS,YAAY,QAAQ,IAAI;AAC9D,QAAM,sBACJ,KAAK,SAAS,gBAAgB,QAAQ,IAAI;AAC5C,QAAM,gBAAgB,KAAK,SAAS,UAAU,QAAQ,IAAI;AAE1D,kBAAgB;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,aAAa,iBAAiB;AAAA,IAClC,YAAY,KAAK;AAAA,IACjB;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,SAAyB;AAAA,IAC7B,QAAQ;AAAA,IACR,SAAS,eACL;AAAA,MACE,cAAc;AAAA,QACZ,MAAM;AAAA,QACN,SAAS;AAAA,UACP,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,MAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF,IACA;AAAA,IACJ,WAAW,SACP;AAAA,MACE,QAAQ;AAAA,QACN,UAAU;AAAA,QACV,cAAc;AAAA,QACd,QAAQ;AAAA,MACV,CAAC;AAAA,IACH,IACA;AAAA,MACE,YAAY;AAAA,QACV,MAAM;AAAA,QACN,aAAa;AAAA,UACX,MAAM;AAAA,YACJ,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,QACF;AAAA,QACA,WAAW,OAAO,gBAAgB;AAChC,gBAAM,OAAO,aAAa;AAC1B,cAAI,CAAC,KAAM,QAAO;AAClB,gBAAM,UAAU,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC;AAC3D,iBAAO;AAAA,YACL,IAAI,QAAQ,IAAI;AAAA,YAChB,MAAM,GAAG,OAAO;AAAA,YAChB,OAAO,GAAG,IAAI;AAAA,YACd;AAAA,YACA,QAAQ,CAAC,IAAI;AAAA,UACf;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACJ,SAAS,EAAE,UAAU,MAAM;AAAA,IAC3B,WAAW;AAAA,MACT,KAAK,CAAC,EAAE,OAAO,MAAM,QAAQ,MAAM;AACjC,YAAI,MAAM;AACR,gBAAM,QAAQ,KAAK,MAAM;AACzB,gBAAM,UAAU,KAAK,SAAS;AAC9B,cAAI,CAAC,QAAQ;AACX,kBAAM,IAAI;AACV,kBAAM,SAAS,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,IAAI,IAAI,CAAC;AACjD,gBAAI,OAAO,SAAS,GAAG;AACrB,cAAC,MAAkC,gBAAgB,IAAI;AAAA,YACzD;AAAA,UACF;AAAA,QACF;AACA,YAAI,UAAU,SAAS;AACrB,gBAAM,SAAU,QAAoC,gBAAgB;AACpE,cAAI,QAAQ;AACV,YAAC,MAAkC,gBAAgB,IAAI;AAAA,UACzD;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,MACA,SAAS,CAAC,EAAE,SAAS,MAAM,MAAM;AAC/B,cAAM,SACF,MAAkC,gBAAgB,KAElC,CAAC;AACrB,gBAAQ,KAAK,SAAS;AACtB,gBAAQ,KAAK,OAAO,eAAe,MAAM;AACzC,eAAO;AAAA,MACT;AAAA,MACA,YAAY,CAAC,EAAE,MAAM,SAAS,SAAS,EAAE,QAAQ,EAAE,MAAM;AACvD,cAAM,OAAO,QAAQ;AACrB,cAAM,gBAAgB,KAAK,oBAAoB;AAAA,UAC7C,CAAC,WAAW,SAAS,UAAU,KAAK,WAAW,GAAG,MAAM,GAAG;AAAA,QAC7D;AACA,YAAI,CAAC,WAAW,cAAe,QAAO;AACtC,eAAO;AAAA,MACT;AAAA,MACA,UAAU,sBAAsB,mBAAmB;AAAA,IACrD;AAAA,IACA,OAAO,EAAE,QAAQ,WAAW;AAAA,IAC5B,WAAW;AAAA,EACb;AAEA,SAAO,SAAS,MAAM;AACxB;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@augmenting-integrations/auth",
3
- "version": "4.0.2",
3
+ "version": "4.1.1",
4
4
  "description": "Auth.js v5 factory: Cognito in prod, Credentials role-picker in dev. Same JWT shape (sub, email, cognito:groups) regardless of provider.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -21,6 +21,11 @@
21
21
  "dist",
22
22
  "README.md"
23
23
  ],
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "clean": "rm -rf dist",
27
+ "test": "vitest run --passWithNoTests"
28
+ },
24
29
  "peerDependencies": {
25
30
  "next": "^16.0.0",
26
31
  "next-auth": "^5.0.0-beta.31",
@@ -33,10 +38,5 @@
33
38
  "tsup": "^8.3.5",
34
39
  "typescript": "^5.7.2",
35
40
  "vitest": "^4.1.5"
36
- },
37
- "scripts": {
38
- "build": "tsup",
39
- "clean": "rm -rf dist",
40
- "test": "vitest run --passWithNoTests"
41
41
  }
42
- }
42
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Augmenting Integrations LLC
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/dist/index.d.ts DELETED
@@ -1,78 +0,0 @@
1
- import { type DefaultSession } from "next-auth";
2
- declare module "next-auth" {
3
- interface Session {
4
- user: {
5
- role: string;
6
- } & DefaultSession["user"];
7
- }
8
- interface User {
9
- role: string;
10
- }
11
- }
12
- export type CreateAuthOptions = {
13
- /**
14
- * Path prefixes that require an authenticated session.
15
- * Empty array = no gating (rare).
16
- */
17
- authedRoutePrefixes: string[];
18
- /**
19
- * Page to redirect to when an unauthed user hits a gated route. Can be a
20
- * relative path (`"/login"`) for in-app login, or a fully-qualified URL
21
- * (`"https://example.com/login"`) to delegate login to a peer app on a
22
- * sibling subdomain or the apex of a path-routed deployment.
23
- * Default: `/login`.
24
- */
25
- signInPage?: string;
26
- /**
27
- * Cookie domain for the session token. Set this to the parent domain
28
- * (e.g. `.example.com`) when running across multiple subdomains so the
29
- * session JWT is readable by every app sharing that parent. For
30
- * path-routed deployments under a single domain, leave unset (host-only
31
- * is correct).
32
- *
33
- * Default: read from `process.env.AUTH_COOKIE_DOMAIN`; if unset, Auth.js
34
- * defaults to host-only (current request hostname).
35
- *
36
- * In dev (NODE_ENV !== "production"), this is ignored — cookies stay
37
- * scoped to localhost so per-port apps don't collide.
38
- */
39
- cookieDomain?: string;
40
- /**
41
- * Override prod/dev detection. Default reads NODE_ENV.
42
- */
43
- isProd?: boolean;
44
- /**
45
- * The JWT signing secret. If not provided, falls back to
46
- * `process.env.AUTH_SECRET`. Pass this from a runtime fetch (e.g. AWS
47
- * Secrets Manager via @aws-sdk/client-secrets-manager) to keep the secret
48
- * out of Lambda environment variables and to support rotation without
49
- * redeploy (Lambda containers re-init periodically and pick up the new
50
- * value).
51
- */
52
- secret?: string;
53
- /**
54
- * Cognito OIDC provider config. Each field falls back to the matching
55
- * `AUTH_COGNITO_*` env var if not provided. Provide them explicitly to
56
- * fetch the client secret from Secrets Manager at runtime.
57
- */
58
- cognito?: {
59
- clientId?: string;
60
- clientSecret?: string;
61
- issuer?: string;
62
- };
63
- };
64
- /**
65
- * Build an Auth.js v5 NextAuth() invocation. Each consuming app calls this
66
- * once at module scope and re-exports the returned `handlers`, `auth`,
67
- * `signIn`, `signOut`.
68
- *
69
- * Secrets default to env vars for simple deploys; pass them as options to
70
- * support runtime fetching from a secret store.
71
- */
72
- export declare function createAuth(opts: CreateAuthOptions): import("next-auth").NextAuthResult;
73
- /**
74
- * Re-export of NextAuthConfig for consumers that want to extend the config
75
- * shape returned by createAuth().
76
- */
77
- export type { NextAuthConfig } from "next-auth";
78
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAiB,EAAE,KAAK,cAAc,EAAuB,MAAM,WAAW,CAAC;AAQ/E,OAAO,QAAQ,WAAW,CAAC;IACzB,UAAU,OAAO;QACf,IAAI,EAAE;YACJ,IAAI,EAAE,MAAM,CAAC;SACd,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;KAC5B;IACD,UAAU,IAAI;QACZ,IAAI,EAAE,MAAM,CAAC;KACd;CACF;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AASF;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,sCAwGjD;AAED;;;GAGG;AACH,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}