@augmenting-integrations/auth 3.0.1 → 4.0.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
@@ -36,18 +36,9 @@ module.exports = __toCommonJS(index_exports);
36
36
  var import_next_auth = __toESM(require("next-auth"));
37
37
  var import_credentials = __toESM(require("next-auth/providers/credentials"));
38
38
  var import_cognito = __toESM(require("next-auth/providers/cognito"));
39
- var VALID_ROLES = [
40
- "visitor",
41
- "broker",
42
- "company_admin",
43
- "manager",
44
- "agent",
45
- "admin"
46
- ];
47
39
  function roleFromGroups(groups) {
48
40
  if (Array.isArray(groups) && groups.length > 0) {
49
- const first = String(groups[0]).toLowerCase();
50
- if (VALID_ROLES.includes(first)) return first;
41
+ return String(groups[0]).toLowerCase();
51
42
  }
52
43
  return "visitor";
53
44
  }
@@ -55,7 +46,10 @@ function createAuth(opts) {
55
46
  const isProd = opts.isProd ?? process.env.NODE_ENV === "production";
56
47
  const signInPage = opts.signInPage ?? "/login";
57
48
  const cookieDomain = isProd ? opts.cookieDomain ?? process.env.AUTH_COOKIE_DOMAIN : void 0;
58
- const SECRET = process.env.AUTH_SECRET ?? (isProd ? void 0 : "dev-only-fallback-not-for-prod");
49
+ const SECRET = opts.secret ?? process.env.AUTH_SECRET ?? (isProd ? void 0 : "dev-only-fallback-not-for-prod");
50
+ const cognitoClientId = opts.cognito?.clientId ?? process.env.AUTH_COGNITO_ID;
51
+ const cognitoClientSecret = opts.cognito?.clientSecret ?? process.env.AUTH_COGNITO_SECRET;
52
+ const cognitoIssuer = opts.cognito?.issuer ?? process.env.AUTH_COGNITO_ISSUER;
59
53
  const config = {
60
54
  secret: SECRET,
61
55
  cookies: cookieDomain ? {
@@ -72,9 +66,9 @@ function createAuth(opts) {
72
66
  } : void 0,
73
67
  providers: isProd ? [
74
68
  (0, import_cognito.default)({
75
- clientId: process.env.AUTH_COGNITO_ID,
76
- clientSecret: process.env.AUTH_COGNITO_SECRET,
77
- issuer: process.env.AUTH_COGNITO_ISSUER
69
+ clientId: cognitoClientId,
70
+ clientSecret: cognitoClientSecret,
71
+ issuer: cognitoIssuer
78
72
  })
79
73
  ] : [
80
74
  (0, import_credentials.default)({
@@ -83,12 +77,12 @@ function createAuth(opts) {
83
77
  role: {
84
78
  label: "Role",
85
79
  type: "text",
86
- placeholder: "visitor | broker | company_admin | manager | agent | admin"
80
+ placeholder: "any role string"
87
81
  }
88
82
  },
89
83
  authorize: async (credentials) => {
90
84
  const role = credentials?.role;
91
- if (!role || !VALID_ROLES.includes(role)) return null;
85
+ if (!role) return null;
92
86
  const display = role.charAt(0).toUpperCase() + role.slice(1);
93
87
  return {
94
88
  id: `mock-${role}`,
@@ -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// Compass role taxonomy. Sourced from census-platform-main's profiles.role\n// enum (broker, company_admin) plus the sales-dashboard manager/agent\n// hierarchy. `visitor` is the implicit anonymous default (no Cognito group).\n// Adding a new role here requires adding the matching Cognito group in\n// compass-infra/template.yaml.\nexport type Role = \"visitor\" | \"broker\" | \"company_admin\" | \"manager\" | \"agent\" | \"admin\";\n\nconst VALID_ROLES: Role[] = [\n \"visitor\",\n \"broker\",\n \"company_admin\",\n \"manager\",\n \"agent\",\n \"admin\",\n];\n\ndeclare module \"next-auth\" {\n interface Session {\n user: {\n role: Role;\n } & DefaultSession[\"user\"];\n }\n interface User {\n role: Role;\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://compass.aillc.link/login\"`) to delegate login to a peer app\n * on a sibling subdomain. Default: `/login`.\n */\n signInPage?: string;\n /**\n * Cookie domain for the session token. Set this to the parent domain\n * (e.g. `.compass.aillc.link`) when running across multiple subdomains\n * so the session JWT is readable by every app sharing that parent.\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\nfunction roleFromGroups(groups: unknown): Role {\n if (Array.isArray(groups) && groups.length > 0) {\n const first = String(groups[0]).toLowerCase();\n if ((VALID_ROLES as string[]).includes(first)) return first as Role;\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 * Cognito creds are read from process.env (typically set from SSM at deploy):\n * AUTH_COGNITO_ID, AUTH_COGNITO_SECRET, AUTH_COGNITO_ISSUER, AUTH_SECRET\n */\nexport function createAuth(opts: CreateAuthOptions) {\n const isProd = opts.isProd ?? process.env.NODE_ENV === \"production\";\n const signInPage = opts.signInPage ?? \"/login\";\n // Cookie domain is only honored in production. In dev, leaving it unset\n // keeps the cookie scoped to localhost so apps on different ports don't\n // stomp on each other.\n const cookieDomain = isProd\n ? (opts.cookieDomain ?? process.env.AUTH_COOKIE_DOMAIN)\n : undefined;\n\n const SECRET =\n process.env.AUTH_SECRET ?? (isProd ? undefined : \"dev-only-fallback-not-for-prod\");\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: process.env.AUTH_COGNITO_ID,\n clientSecret: process.env.AUTH_COGNITO_SECRET,\n issuer: process.env.AUTH_COGNITO_ISSUER,\n }),\n ]\n : [\n Credentials({\n name: \"Mock role (dev only)\",\n credentials: {\n role: {\n label: \"Role\",\n type: \"text\",\n placeholder: \"visitor | broker | company_admin | manager | agent | admin\",\n },\n },\n authorize: async (credentials) => {\n const role = credentials?.role as Role | undefined;\n if (!role || !VALID_ROLES.includes(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?: Role }).role) {\n (token as Record<string, unknown>)[\"cognito:groups\"] = [\n (user as { role: Role }).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 * Convenience: build the proxy default-export for `src/proxy.ts`. In Next 16\n * the proxy must be a default export and must live at `src/proxy.ts` — the\n * named-export form throws \"TypeError: adapterFn is not a function\".\n *\n * Usage:\n * // src/proxy.ts\n * import { auth } from \"@/lib/auth\";\n * export default auth;\n * export const config = {\n * matcher: [\"/dashboard\", \"/dashboard/:path*\"],\n * };\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// 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"]}
package/dist/index.d.ts CHANGED
@@ -1,13 +1,12 @@
1
1
  import { type DefaultSession } from "next-auth";
2
- export type Role = "visitor" | "broker" | "company_admin" | "manager" | "agent" | "admin";
3
2
  declare module "next-auth" {
4
3
  interface Session {
5
4
  user: {
6
- role: Role;
5
+ role: string;
7
6
  } & DefaultSession["user"];
8
7
  }
9
8
  interface User {
10
- role: Role;
9
+ role: string;
11
10
  }
12
11
  }
13
12
  export type CreateAuthOptions = {
@@ -19,14 +18,18 @@ export type CreateAuthOptions = {
19
18
  /**
20
19
  * Page to redirect to when an unauthed user hits a gated route. Can be a
21
20
  * relative path (`"/login"`) for in-app login, or a fully-qualified URL
22
- * (`"https://compass.aillc.link/login"`) to delegate login to a peer app
23
- * on a sibling subdomain. Default: `/login`.
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
24
  */
25
25
  signInPage?: string;
26
26
  /**
27
27
  * Cookie domain for the session token. Set this to the parent domain
28
- * (e.g. `.compass.aillc.link`) when running across multiple subdomains
29
- * so the session JWT is readable by every app sharing that parent.
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
+ *
30
33
  * Default: read from `process.env.AUTH_COOKIE_DOMAIN`; if unset, Auth.js
31
34
  * defaults to host-only (current request hostname).
32
35
  *
@@ -38,28 +41,38 @@ export type CreateAuthOptions = {
38
41
  * Override prod/dev detection. Default reads NODE_ENV.
39
42
  */
40
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
+ };
41
63
  };
42
64
  /**
43
65
  * Build an Auth.js v5 NextAuth() invocation. Each consuming app calls this
44
66
  * once at module scope and re-exports the returned `handlers`, `auth`,
45
67
  * `signIn`, `signOut`.
46
68
  *
47
- * Cognito creds are read from process.env (typically set from SSM at deploy):
48
- * AUTH_COGNITO_ID, AUTH_COGNITO_SECRET, AUTH_COGNITO_ISSUER, AUTH_SECRET
69
+ * Secrets default to env vars for simple deploys; pass them as options to
70
+ * support runtime fetching from a secret store.
49
71
  */
50
72
  export declare function createAuth(opts: CreateAuthOptions): import("next-auth").NextAuthResult;
51
73
  /**
52
- * Convenience: build the proxy default-export for `src/proxy.ts`. In Next 16
53
- * the proxy must be a default export and must live at `src/proxy.ts` — the
54
- * named-export form throws "TypeError: adapterFn is not a function".
55
- *
56
- * Usage:
57
- * // src/proxy.ts
58
- * import { auth } from "@/lib/auth";
59
- * export default auth;
60
- * export const config = {
61
- * matcher: ["/dashboard", "/dashboard/:path*"],
62
- * };
74
+ * Re-export of NextAuthConfig for consumers that want to extend the config
75
+ * shape returned by createAuth().
63
76
  */
64
77
  export type { NextAuthConfig } from "next-auth";
65
78
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAiB,EAAE,KAAK,cAAc,EAAuB,MAAM,WAAW,CAAC;AAS/E,MAAM,MAAM,IAAI,GAAG,SAAS,GAAG,QAAQ,GAAG,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAW1F,OAAO,QAAQ,WAAW,CAAC;IACzB,UAAU,OAAO;QACf,IAAI,EAAE;YACJ,IAAI,EAAE,IAAI,CAAC;SACZ,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;KAC5B;IACD,UAAU,IAAI;QACZ,IAAI,EAAE,IAAI,CAAC;KACZ;CACF;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAUF;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,sCAoGjD;AAED;;;;;;;;;;;;GAYG;AACH,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
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"}
package/dist/index.js CHANGED
@@ -2,18 +2,9 @@
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 VALID_ROLES = [
6
- "visitor",
7
- "broker",
8
- "company_admin",
9
- "manager",
10
- "agent",
11
- "admin"
12
- ];
13
5
  function roleFromGroups(groups) {
14
6
  if (Array.isArray(groups) && groups.length > 0) {
15
- const first = String(groups[0]).toLowerCase();
16
- if (VALID_ROLES.includes(first)) return first;
7
+ return String(groups[0]).toLowerCase();
17
8
  }
18
9
  return "visitor";
19
10
  }
@@ -21,7 +12,10 @@ function createAuth(opts) {
21
12
  const isProd = opts.isProd ?? process.env.NODE_ENV === "production";
22
13
  const signInPage = opts.signInPage ?? "/login";
23
14
  const cookieDomain = isProd ? opts.cookieDomain ?? process.env.AUTH_COOKIE_DOMAIN : void 0;
24
- const SECRET = process.env.AUTH_SECRET ?? (isProd ? void 0 : "dev-only-fallback-not-for-prod");
15
+ const SECRET = opts.secret ?? process.env.AUTH_SECRET ?? (isProd ? void 0 : "dev-only-fallback-not-for-prod");
16
+ const cognitoClientId = opts.cognito?.clientId ?? process.env.AUTH_COGNITO_ID;
17
+ const cognitoClientSecret = opts.cognito?.clientSecret ?? process.env.AUTH_COGNITO_SECRET;
18
+ const cognitoIssuer = opts.cognito?.issuer ?? process.env.AUTH_COGNITO_ISSUER;
25
19
  const config = {
26
20
  secret: SECRET,
27
21
  cookies: cookieDomain ? {
@@ -38,9 +32,9 @@ function createAuth(opts) {
38
32
  } : void 0,
39
33
  providers: isProd ? [
40
34
  Cognito({
41
- clientId: process.env.AUTH_COGNITO_ID,
42
- clientSecret: process.env.AUTH_COGNITO_SECRET,
43
- issuer: process.env.AUTH_COGNITO_ISSUER
35
+ clientId: cognitoClientId,
36
+ clientSecret: cognitoClientSecret,
37
+ issuer: cognitoIssuer
44
38
  })
45
39
  ] : [
46
40
  Credentials({
@@ -49,12 +43,12 @@ function createAuth(opts) {
49
43
  role: {
50
44
  label: "Role",
51
45
  type: "text",
52
- placeholder: "visitor | broker | company_admin | manager | agent | admin"
46
+ placeholder: "any role string"
53
47
  }
54
48
  },
55
49
  authorize: async (credentials) => {
56
50
  const role = credentials?.role;
57
- if (!role || !VALID_ROLES.includes(role)) return null;
51
+ if (!role) return null;
58
52
  const display = role.charAt(0).toUpperCase() + role.slice(1);
59
53
  return {
60
54
  id: `mock-${role}`,
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// Compass role taxonomy. Sourced from census-platform-main's profiles.role\n// enum (broker, company_admin) plus the sales-dashboard manager/agent\n// hierarchy. `visitor` is the implicit anonymous default (no Cognito group).\n// Adding a new role here requires adding the matching Cognito group in\n// compass-infra/template.yaml.\nexport type Role = \"visitor\" | \"broker\" | \"company_admin\" | \"manager\" | \"agent\" | \"admin\";\n\nconst VALID_ROLES: Role[] = [\n \"visitor\",\n \"broker\",\n \"company_admin\",\n \"manager\",\n \"agent\",\n \"admin\",\n];\n\ndeclare module \"next-auth\" {\n interface Session {\n user: {\n role: Role;\n } & DefaultSession[\"user\"];\n }\n interface User {\n role: Role;\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://compass.aillc.link/login\"`) to delegate login to a peer app\n * on a sibling subdomain. Default: `/login`.\n */\n signInPage?: string;\n /**\n * Cookie domain for the session token. Set this to the parent domain\n * (e.g. `.compass.aillc.link`) when running across multiple subdomains\n * so the session JWT is readable by every app sharing that parent.\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\nfunction roleFromGroups(groups: unknown): Role {\n if (Array.isArray(groups) && groups.length > 0) {\n const first = String(groups[0]).toLowerCase();\n if ((VALID_ROLES as string[]).includes(first)) return first as Role;\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 * Cognito creds are read from process.env (typically set from SSM at deploy):\n * AUTH_COGNITO_ID, AUTH_COGNITO_SECRET, AUTH_COGNITO_ISSUER, AUTH_SECRET\n */\nexport function createAuth(opts: CreateAuthOptions) {\n const isProd = opts.isProd ?? process.env.NODE_ENV === \"production\";\n const signInPage = opts.signInPage ?? \"/login\";\n // Cookie domain is only honored in production. In dev, leaving it unset\n // keeps the cookie scoped to localhost so apps on different ports don't\n // stomp on each other.\n const cookieDomain = isProd\n ? (opts.cookieDomain ?? process.env.AUTH_COOKIE_DOMAIN)\n : undefined;\n\n const SECRET =\n process.env.AUTH_SECRET ?? (isProd ? undefined : \"dev-only-fallback-not-for-prod\");\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: process.env.AUTH_COGNITO_ID,\n clientSecret: process.env.AUTH_COGNITO_SECRET,\n issuer: process.env.AUTH_COGNITO_ISSUER,\n }),\n ]\n : [\n Credentials({\n name: \"Mock role (dev only)\",\n credentials: {\n role: {\n label: \"Role\",\n type: \"text\",\n placeholder: \"visitor | broker | company_admin | manager | agent | admin\",\n },\n },\n authorize: async (credentials) => {\n const role = credentials?.role as Role | undefined;\n if (!role || !VALID_ROLES.includes(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?: Role }).role) {\n (token as Record<string, unknown>)[\"cognito:groups\"] = [\n (user as { role: Role }).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 * Convenience: build the proxy default-export for `src/proxy.ts`. In Next 16\n * the proxy must be a default export and must live at `src/proxy.ts` — the\n * named-export form throws \"TypeError: adapterFn is not a function\".\n *\n * Usage:\n * // src/proxy.ts\n * import { auth } from \"@/lib/auth\";\n * export default auth;\n * export const config = {\n * matcher: [\"/dashboard\", \"/dashboard/:path*\"],\n * };\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// 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":";AAeA,OAAO,cAA4D;AACnE,OAAO,iBAAiB;AACxB,OAAO,aAAa;AAsEpB,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;AAUO,SAAS,WAAW,MAAyB;AAClD,QAAM,SAAS,KAAK,UAAU,QAAQ,IAAI,aAAa;AACvD,QAAM,aAAa,KAAK,cAAc;AACtC,QAAM,eAAe,SAChB,KAAK,gBAAgB,QAAQ,IAAI,qBAClC;AAEJ,QAAM,SACJ,KAAK,UACL,QAAQ,IAAI,gBACX,SAAS,SAAY;AAExB,QAAM,kBAAkB,KAAK,SAAS,YAAY,QAAQ,IAAI;AAC9D,QAAM,sBACJ,KAAK,SAAS,gBAAgB,QAAQ,IAAI;AAC5C,QAAM,gBAAgB,KAAK,SAAS,UAAU,QAAQ,IAAI;AAE1D,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,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,UAAW,KAA2B,MAAM;AAC/C,YAAC,MAAkC,gBAAgB,IAAI;AAAA,cACpD,KAA0B;AAAA,YAC7B;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,gBAAQ,KAAK,OAAO;AAAA,UACjB,MAAkC,gBAAgB;AAAA,QACrD;AACA,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,IACF;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": "3.0.1",
3
+ "version": "4.0.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": {