@augmenting-integrations/auth 8.4.1 → 8.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.cjs +2 -9
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.ts +0 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -9
- package/dist/client.js.map +1 -1
- package/dist/server/createAuth.d.ts +31 -2
- package/dist/server/createAuth.d.ts.map +1 -1
- package/dist/server/invitations.d.ts +82 -0
- package/dist/server/invitations.d.ts.map +1 -0
- package/dist/server/settings.d.ts +64 -0
- package/dist/server/settings.d.ts.map +1 -0
- package/dist/server.cjs +285 -93
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.ts +2 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +282 -88
- package/dist/server.js.map +1 -1
- package/package.json +5 -3
- package/dist/client/tenant.cjs +0 -64
- package/dist/client/tenant.cjs.map +0 -1
- package/dist/client/tenant.d.ts +0 -9
- package/dist/client/tenant.d.ts.map +0 -1
- package/dist/client/tenant.js +0 -30
- package/dist/client/tenant.js.map +0 -1
- package/dist/server/tenant.d.ts +0 -26
- package/dist/server/tenant.d.ts.map +0 -1
- package/dist/tenant-types.cjs +0 -29
- package/dist/tenant-types.cjs.map +0 -1
- package/dist/tenant-types.d.ts +0 -63
- package/dist/tenant-types.d.ts.map +0 -1
- package/dist/tenant-types.js +0 -5
- package/dist/tenant-types.js.map +0 -1
package/dist/server.cjs
CHANGED
|
@@ -33,18 +33,16 @@ __export(server_exports, {
|
|
|
33
33
|
AuthError: () => AuthError,
|
|
34
34
|
IMPERSONATE_COOKIE_NAME: () => IMPERSONATE_COOKIE_NAME,
|
|
35
35
|
IMPERSONATE_TTL_SECONDS: () => IMPERSONATE_TTL_SECONDS,
|
|
36
|
-
TENANT_GLOBAL_KEY: () => TENANT_GLOBAL_KEY,
|
|
37
|
-
TenantBootScript: () => TenantBootScript,
|
|
38
36
|
createAuth: () => createAuth,
|
|
39
37
|
createGetOrCreateAppUser: () => createGetOrCreateAppUser,
|
|
40
38
|
createImpersonateHandlers: () => createImpersonateHandlers,
|
|
41
39
|
createInvitationHandlers: () => createInvitationHandlers,
|
|
40
|
+
createInvitationSendHandlers: () => createInvitationSendHandlers,
|
|
42
41
|
createMeHandler: () => createMeHandler,
|
|
42
|
+
createSettingsHandlers: () => createSettingsHandlers,
|
|
43
43
|
getUserGroups: () => getUserGroups,
|
|
44
44
|
hasGroup: () => hasGroup,
|
|
45
|
-
loadTenantConfig: () => loadTenantConfig,
|
|
46
45
|
mintImpersonationToken: () => mintImpersonationToken,
|
|
47
|
-
publicSubset: () => publicSubset,
|
|
48
46
|
requireGroup: () => requireGroup,
|
|
49
47
|
verifyImpersonationToken: () => verifyImpersonationToken
|
|
50
48
|
});
|
|
@@ -115,6 +113,9 @@ function createAuth(opts) {
|
|
|
115
113
|
appDomain: tenant.appDomain,
|
|
116
114
|
apex: tenant.apex
|
|
117
115
|
});
|
|
116
|
+
const requiredIdentityGroups = opts.appAccess?.requiredIdentityGroups ?? [];
|
|
117
|
+
const hasAccessPolicy = requiredIdentityGroups.length > 0;
|
|
118
|
+
const forbiddenPage = opts.appAccess?.forbiddenPage ?? (tenant.appDomain === tenant.apex ? `/login?error=app_forbidden&app=${encodeURIComponent(tenant.appSlug ?? "apex")}` : `https://${tenant.apex}/login?error=app_forbidden&app=${encodeURIComponent(tenant.appSlug ?? "")}`);
|
|
118
119
|
const config = {
|
|
119
120
|
secret: authSecret,
|
|
120
121
|
cookies: cookieDomain ? {
|
|
@@ -161,7 +162,7 @@ function createAuth(opts) {
|
|
|
161
162
|
],
|
|
162
163
|
session: { strategy: "jwt" },
|
|
163
164
|
callbacks: {
|
|
164
|
-
jwt: ({ token, user, profile }) => {
|
|
165
|
+
jwt: ({ token, user, profile, account }) => {
|
|
165
166
|
if (user) {
|
|
166
167
|
token.sub ??= user.id ?? void 0;
|
|
167
168
|
token.email ??= user.email ?? void 0;
|
|
@@ -179,11 +180,18 @@ function createAuth(opts) {
|
|
|
179
180
|
token["cognito:groups"] = groups;
|
|
180
181
|
}
|
|
181
182
|
}
|
|
183
|
+
if (account?.access_token) {
|
|
184
|
+
token["accessToken"] = account.access_token;
|
|
185
|
+
}
|
|
182
186
|
return token;
|
|
183
187
|
},
|
|
184
188
|
session: ({ session, token }) => {
|
|
185
189
|
const groups = token["cognito:groups"] ?? [];
|
|
186
190
|
session.user.groups = groups;
|
|
191
|
+
const accessToken = token["accessToken"];
|
|
192
|
+
if (typeof accessToken === "string") {
|
|
193
|
+
session.accessToken = accessToken;
|
|
194
|
+
}
|
|
187
195
|
return session;
|
|
188
196
|
},
|
|
189
197
|
authorized: ({ auth: session, request: { nextUrl } }) => {
|
|
@@ -199,6 +207,16 @@ function createAuth(opts) {
|
|
|
199
207
|
}
|
|
200
208
|
return false;
|
|
201
209
|
}
|
|
210
|
+
if (session && isAuthedRoute && hasAccessPolicy) {
|
|
211
|
+
const userGroups = (session.user?.groups ?? []).map((g) => g.toLowerCase());
|
|
212
|
+
const allowed = requiredIdentityGroups.some(
|
|
213
|
+
(g) => userGroups.includes(g.toLowerCase())
|
|
214
|
+
);
|
|
215
|
+
if (!allowed) {
|
|
216
|
+
const target = forbiddenPage.startsWith("http") ? new URL(forbiddenPage) : new URL(forbiddenPage, nextUrl.origin);
|
|
217
|
+
return Response.redirect(target.toString(), 302);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
202
220
|
return true;
|
|
203
221
|
},
|
|
204
222
|
redirect: buildRedirectCallback(tenant.parentDomain)
|
|
@@ -345,91 +363,8 @@ function createGetOrCreateAppUser(opts) {
|
|
|
345
363
|
};
|
|
346
364
|
}
|
|
347
365
|
|
|
348
|
-
// src/server/tenant.ts
|
|
349
|
-
var import_server_only3 = require("server-only");
|
|
350
|
-
var React = __toESM(require("react"));
|
|
351
|
-
|
|
352
|
-
// src/tenant-types.ts
|
|
353
|
-
var TENANT_GLOBAL_KEY = "__TENANT__";
|
|
354
|
-
|
|
355
|
-
// src/server/tenant.ts
|
|
356
|
-
function loadTenantConfig(opts) {
|
|
357
|
-
const env = process.env;
|
|
358
|
-
const o = opts.overrides ?? {};
|
|
359
|
-
const parentDomainRaw = o.parentDomain ?? env.AUTH_ALLOWED_PARENT_DOMAIN;
|
|
360
|
-
const apexFallback = parentDomainRaw?.replace(/^\./, "");
|
|
361
|
-
const draft = {
|
|
362
|
-
role: opts.role,
|
|
363
|
-
apex: o.apex ?? env.APEX_DOMAIN ?? apexFallback,
|
|
364
|
-
cookieDomain: o.cookieDomain ?? env.AUTH_COOKIE_DOMAIN,
|
|
365
|
-
parentDomain: parentDomainRaw,
|
|
366
|
-
region: o.region ?? env.AWS_REGION ?? "us-east-1",
|
|
367
|
-
appSlug: o.appSlug ?? env.APP_SLUG,
|
|
368
|
-
appDomain: o.appDomain ?? env.APP_DOMAIN,
|
|
369
|
-
authSecretArn: o.authSecretArn ?? env.AUTH_SECRET_ARN,
|
|
370
|
-
registryTable: o.registryTable ?? env.APP_REGISTRY_TABLE,
|
|
371
|
-
authCognitoSecretArn: o.authCognitoSecretArn ?? env.AUTH_COGNITO_SECRET_ARN,
|
|
372
|
-
cognitoIssuer: o.cognitoIssuer ?? env.AUTH_COGNITO_ISSUER,
|
|
373
|
-
cognitoClientId: o.cognitoClientId ?? env.AUTH_COGNITO_ID,
|
|
374
|
-
adminEmails: o.adminEmails ?? env.ADMIN_EMAILS,
|
|
375
|
-
dbSecretArn: o.dbSecretArn ?? env.DB_SECRET_ARN,
|
|
376
|
-
dbHost: o.dbHost ?? env.DB_HOST,
|
|
377
|
-
dbName: o.dbName ?? env.DB_NAME,
|
|
378
|
-
stripeSecretArn: o.stripeSecretArn ?? env.STRIPE_SECRET_ARN,
|
|
379
|
-
stripeWebhookSecretArn: o.stripeWebhookSecretArn ?? env.STRIPE_WEBHOOK_SECRET_ARN
|
|
380
|
-
};
|
|
381
|
-
if (opts.role === "apex" && !draft.appDomain) {
|
|
382
|
-
draft.appDomain = draft.apex;
|
|
383
|
-
}
|
|
384
|
-
const required = [
|
|
385
|
-
{ key: "apex", env: "APEX_DOMAIN (or derived from AUTH_ALLOWED_PARENT_DOMAIN)" },
|
|
386
|
-
{ key: "cookieDomain", env: "AUTH_COOKIE_DOMAIN" },
|
|
387
|
-
{ key: "parentDomain", env: "AUTH_ALLOWED_PARENT_DOMAIN" },
|
|
388
|
-
{ key: "authSecretArn", env: "AUTH_SECRET_ARN" },
|
|
389
|
-
{ key: "appDomain", env: "APP_DOMAIN" }
|
|
390
|
-
];
|
|
391
|
-
if (opts.role === "apex") {
|
|
392
|
-
required.push(
|
|
393
|
-
{ key: "authCognitoSecretArn", env: "AUTH_COGNITO_SECRET_ARN" },
|
|
394
|
-
{ key: "cognitoIssuer", env: "AUTH_COGNITO_ISSUER" },
|
|
395
|
-
{ key: "cognitoClientId", env: "AUTH_COGNITO_ID" }
|
|
396
|
-
);
|
|
397
|
-
} else {
|
|
398
|
-
required.push({ key: "appSlug", env: "APP_SLUG" });
|
|
399
|
-
}
|
|
400
|
-
if (process.env.NEXT_PHASE === "phase-production-build" || !process.env.AWS_LAMBDA_FUNCTION_NAME) {
|
|
401
|
-
return draft;
|
|
402
|
-
}
|
|
403
|
-
const missing = required.filter((r) => !draft[r.key]).map((r) => r.env);
|
|
404
|
-
if (missing.length > 0) {
|
|
405
|
-
throw new Error(
|
|
406
|
-
`loadTenantConfig(${opts.role}): missing required env vars: ${missing.join(", ")}`
|
|
407
|
-
);
|
|
408
|
-
}
|
|
409
|
-
return draft;
|
|
410
|
-
}
|
|
411
|
-
function publicSubset(config) {
|
|
412
|
-
return {
|
|
413
|
-
apex: config.apex,
|
|
414
|
-
cookieDomain: config.cookieDomain,
|
|
415
|
-
parentDomain: config.parentDomain,
|
|
416
|
-
region: config.region,
|
|
417
|
-
appSlug: config.appSlug,
|
|
418
|
-
appDomain: config.appDomain,
|
|
419
|
-
role: config.role
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
var INNER_HTML_PROP = "dangerouslySetInnerHTML";
|
|
423
|
-
function TenantBootScript({ config }) {
|
|
424
|
-
const payload = JSON.stringify(config).replace(/</g, "\\u003c");
|
|
425
|
-
const body = `window.${TENANT_GLOBAL_KEY}=${payload};`;
|
|
426
|
-
const props = {};
|
|
427
|
-
props[INNER_HTML_PROP] = { __html: body };
|
|
428
|
-
return React.createElement("script", props);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
366
|
// src/server/handlers.ts
|
|
432
|
-
var
|
|
367
|
+
var import_server_only3 = require("server-only");
|
|
433
368
|
var import_server2 = require("next/server");
|
|
434
369
|
function createMeHandler(opts) {
|
|
435
370
|
return {
|
|
@@ -655,23 +590,280 @@ function createInvitationHandlers(opts) {
|
|
|
655
590
|
}
|
|
656
591
|
};
|
|
657
592
|
}
|
|
593
|
+
|
|
594
|
+
// src/server/settings.ts
|
|
595
|
+
var import_server_only4 = require("server-only");
|
|
596
|
+
var import_server3 = require("next/server");
|
|
597
|
+
function getAccessToken(session) {
|
|
598
|
+
if (!session) return null;
|
|
599
|
+
const fromTop = session.accessToken;
|
|
600
|
+
if (typeof fromTop === "string" && fromTop.length > 0) return fromTop;
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
function accessTokenMissingResponse() {
|
|
604
|
+
return import_server3.NextResponse.json(
|
|
605
|
+
{
|
|
606
|
+
error: "Cognito access token not present on the session. The user must sign in fresh on the apex.",
|
|
607
|
+
code: "access_token_missing"
|
|
608
|
+
},
|
|
609
|
+
{ status: 401 }
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
function jsonValidation(message) {
|
|
613
|
+
return import_server3.NextResponse.json({ error: message, code: "validation" }, { status: 400 });
|
|
614
|
+
}
|
|
615
|
+
function createSettingsHandlers(opts) {
|
|
616
|
+
const passwordChange = {
|
|
617
|
+
POST: async (request) => {
|
|
618
|
+
const session = await opts.auth();
|
|
619
|
+
if (!session?.user) {
|
|
620
|
+
return import_server3.NextResponse.json({ error: "unauthorized" }, { status: 401 });
|
|
621
|
+
}
|
|
622
|
+
const accessToken = getAccessToken(session);
|
|
623
|
+
if (!accessToken) return accessTokenMissingResponse();
|
|
624
|
+
let body;
|
|
625
|
+
try {
|
|
626
|
+
body = await request.json();
|
|
627
|
+
} catch {
|
|
628
|
+
return jsonValidation("invalid JSON body");
|
|
629
|
+
}
|
|
630
|
+
const currentPassword = typeof body.currentPassword === "string" ? body.currentPassword : "";
|
|
631
|
+
const newPassword = typeof body.newPassword === "string" ? body.newPassword : "";
|
|
632
|
+
if (!currentPassword) return jsonValidation("currentPassword required");
|
|
633
|
+
if (newPassword.length < 8)
|
|
634
|
+
return jsonValidation("newPassword must be at least 8 chars");
|
|
635
|
+
try {
|
|
636
|
+
await opts.cognito.changePassword({
|
|
637
|
+
accessToken,
|
|
638
|
+
previousPassword: currentPassword,
|
|
639
|
+
proposedPassword: newPassword
|
|
640
|
+
});
|
|
641
|
+
const appUser = await opts.getOrCreateAppUser(session);
|
|
642
|
+
const db = await opts.getDb();
|
|
643
|
+
await db.user.update({
|
|
644
|
+
where: { id: appUser.id },
|
|
645
|
+
data: { must_change_password: false }
|
|
646
|
+
});
|
|
647
|
+
return import_server3.NextResponse.json({ ok: true });
|
|
648
|
+
} catch (err) {
|
|
649
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
650
|
+
return import_server3.NextResponse.json({ error: message, code: "cognito" }, { status: 400 });
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
const twoFactorSetup = {
|
|
655
|
+
POST: async () => {
|
|
656
|
+
const session = await opts.auth();
|
|
657
|
+
if (!session?.user) {
|
|
658
|
+
return import_server3.NextResponse.json({ error: "unauthorized" }, { status: 401 });
|
|
659
|
+
}
|
|
660
|
+
const accessToken = getAccessToken(session);
|
|
661
|
+
if (!accessToken) return accessTokenMissingResponse();
|
|
662
|
+
try {
|
|
663
|
+
const { secretCode } = await opts.cognito.associateSoftwareToken({
|
|
664
|
+
accessToken
|
|
665
|
+
});
|
|
666
|
+
const accountName = session.user.email ?? "user";
|
|
667
|
+
const otpAuthUri = opts.cognito.buildOtpAuthUri({
|
|
668
|
+
secret: secretCode,
|
|
669
|
+
accountName,
|
|
670
|
+
issuer: opts.appDisplayName
|
|
671
|
+
});
|
|
672
|
+
const qrDataUrl = opts.generateQrDataUrl ? await opts.generateQrDataUrl(otpAuthUri).catch(() => "") : "";
|
|
673
|
+
return import_server3.NextResponse.json({ qrDataUrl, otpAuthUri, secret: secretCode });
|
|
674
|
+
} catch (err) {
|
|
675
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
676
|
+
return import_server3.NextResponse.json({ error: message, code: "cognito" }, { status: 500 });
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
const twoFactorVerify = {
|
|
681
|
+
POST: async (request) => {
|
|
682
|
+
const session = await opts.auth();
|
|
683
|
+
if (!session?.user) {
|
|
684
|
+
return import_server3.NextResponse.json({ error: "unauthorized" }, { status: 401 });
|
|
685
|
+
}
|
|
686
|
+
const accessToken = getAccessToken(session);
|
|
687
|
+
if (!accessToken) return accessTokenMissingResponse();
|
|
688
|
+
let body;
|
|
689
|
+
try {
|
|
690
|
+
body = await request.json();
|
|
691
|
+
} catch {
|
|
692
|
+
return jsonValidation("invalid JSON body");
|
|
693
|
+
}
|
|
694
|
+
const code = typeof body.code === "string" ? body.code : "";
|
|
695
|
+
if (!/^[0-9]{6}$/.test(code)) return jsonValidation("code must be 6 digits");
|
|
696
|
+
try {
|
|
697
|
+
const result = await opts.cognito.verifySoftwareToken({ accessToken, code });
|
|
698
|
+
if (result.status !== "SUCCESS") {
|
|
699
|
+
return import_server3.NextResponse.json(
|
|
700
|
+
{ error: `verify failed: ${result.status}`, code: "verify_failed" },
|
|
701
|
+
{ status: 400 }
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
await opts.cognito.setUserMfaPreference({ accessToken, enabled: true });
|
|
705
|
+
const appUser = await opts.getOrCreateAppUser(session);
|
|
706
|
+
const db = await opts.getDb();
|
|
707
|
+
await db.user.update({
|
|
708
|
+
where: { id: appUser.id },
|
|
709
|
+
data: { two_factor_confirmed_at: /* @__PURE__ */ new Date() }
|
|
710
|
+
});
|
|
711
|
+
return import_server3.NextResponse.json({ ok: true });
|
|
712
|
+
} catch (err) {
|
|
713
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
714
|
+
return import_server3.NextResponse.json({ error: message, code: "cognito" }, { status: 500 });
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
const twoFactorDisable = {
|
|
719
|
+
POST: async () => {
|
|
720
|
+
const session = await opts.auth();
|
|
721
|
+
if (!session?.user) {
|
|
722
|
+
return import_server3.NextResponse.json({ error: "unauthorized" }, { status: 401 });
|
|
723
|
+
}
|
|
724
|
+
const accessToken = getAccessToken(session);
|
|
725
|
+
if (!accessToken) return accessTokenMissingResponse();
|
|
726
|
+
try {
|
|
727
|
+
await opts.cognito.setUserMfaPreference({ accessToken, enabled: false });
|
|
728
|
+
const appUser = await opts.getOrCreateAppUser(session);
|
|
729
|
+
const db = await opts.getDb();
|
|
730
|
+
await db.user.update({
|
|
731
|
+
where: { id: appUser.id },
|
|
732
|
+
data: { two_factor_confirmed_at: null }
|
|
733
|
+
});
|
|
734
|
+
return import_server3.NextResponse.json({ ok: true });
|
|
735
|
+
} catch (err) {
|
|
736
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
737
|
+
return import_server3.NextResponse.json({ error: message, code: "cognito" }, { status: 500 });
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
return { passwordChange, twoFactorSetup, twoFactorVerify, twoFactorDisable };
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// src/server/invitations.ts
|
|
745
|
+
var import_server_only5 = require("server-only");
|
|
746
|
+
var import_server4 = require("next/server");
|
|
747
|
+
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
748
|
+
var ROLE_RE = /^[a-z_]+$/;
|
|
749
|
+
var PARENT_RE = /^\d+$/;
|
|
750
|
+
function jsonError(status, error, code, extra) {
|
|
751
|
+
return import_server4.NextResponse.json({ error, code, ...extra ?? {} }, { status });
|
|
752
|
+
}
|
|
753
|
+
function createInvitationSendHandlers(opts) {
|
|
754
|
+
const send = {
|
|
755
|
+
POST: async (request) => {
|
|
756
|
+
const session = await opts.auth();
|
|
757
|
+
if (!session?.user) return jsonError(401, "unauthorized", "unauthorized");
|
|
758
|
+
const caller = await opts.getOrCreateAppUser(session);
|
|
759
|
+
if (!opts.canInvite(caller.role)) {
|
|
760
|
+
return jsonError(403, "forbidden", "forbidden");
|
|
761
|
+
}
|
|
762
|
+
let body;
|
|
763
|
+
try {
|
|
764
|
+
body = await request.json();
|
|
765
|
+
} catch {
|
|
766
|
+
return jsonError(400, "invalid JSON body", "validation");
|
|
767
|
+
}
|
|
768
|
+
const email = typeof body.email === "string" ? body.email.trim().toLowerCase() : "";
|
|
769
|
+
const role = typeof body.role === "string" ? body.role.trim() : "";
|
|
770
|
+
const parentIdRaw = body.parent_id;
|
|
771
|
+
if (!EMAIL_RE.test(email) || email.length > 255) {
|
|
772
|
+
return jsonError(400, "invalid email", "validation");
|
|
773
|
+
}
|
|
774
|
+
if (!role || role.length > 30 || !ROLE_RE.test(role)) {
|
|
775
|
+
return jsonError(400, "role must be snake_case lowercase", "validation");
|
|
776
|
+
}
|
|
777
|
+
if (opts.allowedRoles && !opts.allowedRoles.includes(role)) {
|
|
778
|
+
return jsonError(
|
|
779
|
+
400,
|
|
780
|
+
`role must be one of: ${opts.allowedRoles.join(", ")}`,
|
|
781
|
+
"validation"
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
let parentOverride = null;
|
|
785
|
+
if (parentIdRaw !== void 0 && parentIdRaw !== null) {
|
|
786
|
+
if (typeof parentIdRaw !== "string" || !PARENT_RE.test(parentIdRaw)) {
|
|
787
|
+
return jsonError(
|
|
788
|
+
400,
|
|
789
|
+
"parent_id must be a stringified BigInt or null",
|
|
790
|
+
"validation"
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
parentOverride = BigInt(parentIdRaw);
|
|
794
|
+
}
|
|
795
|
+
const parent_id = opts.resolveInviteScope(caller, parentOverride);
|
|
796
|
+
const db = await opts.getDb();
|
|
797
|
+
const existing = await db.user.findUnique({ where: { email } });
|
|
798
|
+
if (existing) {
|
|
799
|
+
return jsonError(409, "user with that email already exists", "conflict");
|
|
800
|
+
}
|
|
801
|
+
const token = opts.generateInvitationToken();
|
|
802
|
+
const expiresAt = opts.invitationExpiresAt();
|
|
803
|
+
const invitation = await db.invitation.create({
|
|
804
|
+
data: {
|
|
805
|
+
token,
|
|
806
|
+
email,
|
|
807
|
+
inviter_id: caller.id,
|
|
808
|
+
intended_role: role,
|
|
809
|
+
parent_id,
|
|
810
|
+
expires_at: expiresAt
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
const origin = opts.appDomainEnv ? `https://${opts.appDomainEnv}` : new URL(request.url).origin;
|
|
814
|
+
const invitationUrl = opts.buildInvitationUrl(token, origin);
|
|
815
|
+
const rendered = opts.renderInvitationEmail({
|
|
816
|
+
inviterName: caller.name,
|
|
817
|
+
inviteeEmail: email,
|
|
818
|
+
intendedRole: role,
|
|
819
|
+
invitationUrl,
|
|
820
|
+
expiresAt,
|
|
821
|
+
appDisplayName: opts.appDisplayName
|
|
822
|
+
});
|
|
823
|
+
try {
|
|
824
|
+
await opts.sendEmail({
|
|
825
|
+
to: email,
|
|
826
|
+
subject: rendered.subject,
|
|
827
|
+
html: rendered.html,
|
|
828
|
+
text: rendered.text
|
|
829
|
+
});
|
|
830
|
+
} catch (err) {
|
|
831
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
832
|
+
return import_server4.NextResponse.json(
|
|
833
|
+
{
|
|
834
|
+
invitationId: invitation.id.toString(),
|
|
835
|
+
expiresAt: invitation.expires_at.toISOString(),
|
|
836
|
+
warning: `invitation saved but email send failed: ${message}`
|
|
837
|
+
},
|
|
838
|
+
{ status: 202 }
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
return import_server4.NextResponse.json(
|
|
842
|
+
{
|
|
843
|
+
invitationId: invitation.id.toString(),
|
|
844
|
+
expiresAt: invitation.expires_at.toISOString()
|
|
845
|
+
},
|
|
846
|
+
{ status: 201 }
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
return { send };
|
|
851
|
+
}
|
|
658
852
|
// Annotate the CommonJS export names for ESM import in node:
|
|
659
853
|
0 && (module.exports = {
|
|
660
854
|
AuthError,
|
|
661
855
|
IMPERSONATE_COOKIE_NAME,
|
|
662
856
|
IMPERSONATE_TTL_SECONDS,
|
|
663
|
-
TENANT_GLOBAL_KEY,
|
|
664
|
-
TenantBootScript,
|
|
665
857
|
createAuth,
|
|
666
858
|
createGetOrCreateAppUser,
|
|
667
859
|
createImpersonateHandlers,
|
|
668
860
|
createInvitationHandlers,
|
|
861
|
+
createInvitationSendHandlers,
|
|
669
862
|
createMeHandler,
|
|
863
|
+
createSettingsHandlers,
|
|
670
864
|
getUserGroups,
|
|
671
865
|
hasGroup,
|
|
672
|
-
loadTenantConfig,
|
|
673
866
|
mintImpersonationToken,
|
|
674
|
-
publicSubset,
|
|
675
867
|
requireGroup,
|
|
676
868
|
verifyImpersonationToken
|
|
677
869
|
});
|