@better-auth/infra 0.4.0 → 0.4.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/CHANGELOG.md +7 -0
- package/README.md +4 -0
- package/dist/client.d.mts +1 -2
- package/dist/client.mjs +9 -7
- package/dist/{constants-CcJHk5SE.mjs → constants-CtvGRrlI.mjs} +1 -1
- package/dist/{crypto-CAdWFWEz.mjs → crypto-CV91nSbp.mjs} +21 -6
- package/dist/email.mjs +1 -1
- package/dist/{identify-client-options-RI-JiXQx.d.mts → identify-client-options-CgijjwVT.d.mts} +1 -2
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +516 -452
- package/dist/native.d.mts +1 -2
- package/dist/native.mjs +7 -6
- package/dist/{pow-retry-DZhZiYd_.mjs → pow-retry-7sPi32Ow.mjs} +3 -2
- package/dist/{saml-policy-BTVLoTyS.mjs → saml-policy-DQR0MBLk.mjs} +2 -2
- package/dist/{types-B673lCib.d.mts → types-Ddk4r5x9.d.mts} +0 -1
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as INFRA_USER_AGENT, n as INFRA_API_URL, o as PLUGIN_VERSION, r as INFRA_KV_URL } from "./constants-
|
|
2
|
-
import { a as createAPI, n as hash$1, o as createKV, r as hmacSha256Hex } from "./crypto-
|
|
1
|
+
import { i as INFRA_USER_AGENT, n as INFRA_API_URL, o as PLUGIN_VERSION, r as INFRA_KV_URL } from "./constants-CtvGRrlI.mjs";
|
|
2
|
+
import { a as createAPI, n as hash$1, o as createKV, r as hmacSha256Hex } from "./crypto-CV91nSbp.mjs";
|
|
3
3
|
import { EMAIL_TEMPLATES, createEmailSender, sendBulkEmails, sendEmail } from "./email.mjs";
|
|
4
4
|
import { getCurrentAdapter, getCurrentAuthContext, getCurrentDBAdapterAsyncLocalStorage, runWithTransaction } from "@better-auth/core/context";
|
|
5
5
|
import { APIError, generateId, getAuthTables, logger } from "better-auth";
|
|
@@ -8,7 +8,7 @@ import { APIError as APIError$1, createAuthEndpoint, createAuthMiddleware, reque
|
|
|
8
8
|
import { deleteSessionCookie, setSessionCookie } from "better-auth/cookies";
|
|
9
9
|
import { createFetch } from "@better-fetch/fetch";
|
|
10
10
|
import { isValidPhoneNumber, parsePhoneNumberFromString } from "libphonenumber-js";
|
|
11
|
-
import z
|
|
11
|
+
import z, { z as z$1 } from "zod";
|
|
12
12
|
import { createLocalJWKSet, jwtVerify } from "jose";
|
|
13
13
|
import { generateRandomString, symmetricEncrypt } from "better-auth/crypto";
|
|
14
14
|
import { createOTP } from "@better-auth/utils/otp";
|
|
@@ -313,7 +313,8 @@ function tryDecode(value) {
|
|
|
313
313
|
const stripQuery = (value) => value.split("?")[0] || value;
|
|
314
314
|
const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
315
315
|
const routeToRegex = (route) => {
|
|
316
|
-
const
|
|
316
|
+
const normalized = stripQuery(route);
|
|
317
|
+
const pattern = escapeRegex(normalized).replace(/\/:([^/]+)/g, "/[^/]+");
|
|
317
318
|
return new RegExp(`^${pattern}(?:$|[/?])`);
|
|
318
319
|
};
|
|
319
320
|
const matchesAnyRoute = (path, routes) => {
|
|
@@ -1296,8 +1297,9 @@ function isEmailNormalizationEnabled(security) {
|
|
|
1296
1297
|
* @param $api — Dash client from `createAPI(opts, { throw: true })`.
|
|
1297
1298
|
*/
|
|
1298
1299
|
function createSecurityClient(conn, $api, options, onSecurityEvent) {
|
|
1300
|
+
const resolvedApiUrl = conn.apiUrl || INFRA_API_URL;
|
|
1299
1301
|
const emailSender = createEmailSender({
|
|
1300
|
-
apiUrl:
|
|
1302
|
+
apiUrl: resolvedApiUrl,
|
|
1301
1303
|
apiKey: conn.apiKey
|
|
1302
1304
|
});
|
|
1303
1305
|
function logEvent(event) {
|
|
@@ -1741,7 +1743,7 @@ const authPaths = [
|
|
|
1741
1743
|
"/email-otp/send-verification-otp"
|
|
1742
1744
|
];
|
|
1743
1745
|
const registration = new Set(registrationPaths);
|
|
1744
|
-
const all = new Set([...registrationPaths, ...authPaths]);
|
|
1746
|
+
const all = /* @__PURE__ */ new Set([...registrationPaths, ...authPaths]);
|
|
1745
1747
|
/** Path carries an email we hook for normalization + syntax validation. */
|
|
1746
1748
|
const allEmail = ({ path }) => !!path && all.has(path);
|
|
1747
1749
|
/**
|
|
@@ -1754,11 +1756,11 @@ const registrationEmail = ({ path }) => !!path && registration.has(path);
|
|
|
1754
1756
|
/**
|
|
1755
1757
|
* Gmail-like providers that ignore dots in the local part
|
|
1756
1758
|
*/
|
|
1757
|
-
const GMAIL_LIKE_DOMAINS = new Set(["gmail.com", "googlemail.com"]);
|
|
1759
|
+
const GMAIL_LIKE_DOMAINS = /* @__PURE__ */ new Set(["gmail.com", "googlemail.com"]);
|
|
1758
1760
|
/**
|
|
1759
1761
|
* Providers known to support plus addressing
|
|
1760
1762
|
*/
|
|
1761
|
-
const PLUS_ADDRESSING_DOMAINS = new Set([
|
|
1763
|
+
const PLUS_ADDRESSING_DOMAINS = /* @__PURE__ */ new Set([
|
|
1762
1764
|
"gmail.com",
|
|
1763
1765
|
"googlemail.com",
|
|
1764
1766
|
"outlook.com",
|
|
@@ -2028,7 +2030,7 @@ function createEmailHooks(options = {}) {
|
|
|
2028
2030
|
* Common fake/test phone numbers that should be blocked
|
|
2029
2031
|
* These are numbers commonly used in testing, movies, documentation, etc.
|
|
2030
2032
|
*/
|
|
2031
|
-
const INVALID_PHONE_NUMBERS = new Set([
|
|
2033
|
+
const INVALID_PHONE_NUMBERS = /* @__PURE__ */ new Set([
|
|
2032
2034
|
"+15550000000",
|
|
2033
2035
|
"+15550001111",
|
|
2034
2036
|
"+15550001234",
|
|
@@ -2117,7 +2119,7 @@ const INVALID_PHONE_PATTERNS = [
|
|
|
2117
2119
|
* Key: country code, Value: set of invalid prefixes
|
|
2118
2120
|
*/
|
|
2119
2121
|
const INVALID_PREFIXES_BY_COUNTRY = {
|
|
2120
|
-
US: new Set([
|
|
2122
|
+
US: /* @__PURE__ */ new Set([
|
|
2121
2123
|
"555",
|
|
2122
2124
|
"000",
|
|
2123
2125
|
"111",
|
|
@@ -2125,17 +2127,17 @@ const INVALID_PREFIXES_BY_COUNTRY = {
|
|
|
2125
2127
|
"411",
|
|
2126
2128
|
"611"
|
|
2127
2129
|
]),
|
|
2128
|
-
CA: new Set([
|
|
2130
|
+
CA: /* @__PURE__ */ new Set([
|
|
2129
2131
|
"555",
|
|
2130
2132
|
"000",
|
|
2131
2133
|
"911"
|
|
2132
2134
|
]),
|
|
2133
|
-
GB: new Set([
|
|
2135
|
+
GB: /* @__PURE__ */ new Set([
|
|
2134
2136
|
"7700900",
|
|
2135
2137
|
"1632960",
|
|
2136
2138
|
"1134960"
|
|
2137
2139
|
]),
|
|
2138
|
-
AU: new Set([
|
|
2140
|
+
AU: /* @__PURE__ */ new Set([
|
|
2139
2141
|
"0491570",
|
|
2140
2142
|
"0491571",
|
|
2141
2143
|
"0491572"
|
|
@@ -2205,7 +2207,7 @@ const validatePhone = (phone, options = {}) => {
|
|
|
2205
2207
|
if (blockVoip && phoneType === "VOIP") return false;
|
|
2206
2208
|
return true;
|
|
2207
2209
|
};
|
|
2208
|
-
const allPhonePaths = new Set([
|
|
2210
|
+
const allPhonePaths = /* @__PURE__ */ new Set([
|
|
2209
2211
|
"/phone-number/send-otp",
|
|
2210
2212
|
"/phone-number/verify",
|
|
2211
2213
|
"/sign-in/phone-number",
|
|
@@ -2786,33 +2788,33 @@ const ALL_SCIM_SCOPES = [
|
|
|
2786
2788
|
"scim.groups.read",
|
|
2787
2789
|
"scim.groups.write"
|
|
2788
2790
|
];
|
|
2789
|
-
const scimScopeSchema = z
|
|
2791
|
+
const scimScopeSchema = z.enum(ALL_SCIM_SCOPES);
|
|
2790
2792
|
const credentialPolicySchema = {
|
|
2791
|
-
scopes: z
|
|
2792
|
-
expiresAt: z
|
|
2793
|
+
scopes: z.array(scimScopeSchema).min(1).optional(),
|
|
2794
|
+
expiresAt: z.coerce.date().optional()
|
|
2793
2795
|
};
|
|
2794
|
-
const directorySyncSSOPairingSchema = z
|
|
2795
|
-
ssoProviderId: z
|
|
2796
|
-
protocol: z
|
|
2797
|
-
externalIdSource: z
|
|
2798
|
-
kind: z
|
|
2799
|
-
name: z
|
|
2796
|
+
const directorySyncSSOPairingSchema = z.discriminatedUnion("protocol", [z.object({
|
|
2797
|
+
ssoProviderId: z.string().trim().min(1).max(255),
|
|
2798
|
+
protocol: z.literal("oidc"),
|
|
2799
|
+
externalIdSource: z.discriminatedUnion("kind", [z.object({ kind: z.literal("subject") }), z.object({
|
|
2800
|
+
kind: z.literal("verifiedIdTokenClaim"),
|
|
2801
|
+
name: z.string().trim().min(1).max(255)
|
|
2800
2802
|
})])
|
|
2801
|
-
}), z
|
|
2802
|
-
ssoProviderId: z
|
|
2803
|
-
protocol: z
|
|
2804
|
-
externalIdSource: z
|
|
2805
|
-
kind: z
|
|
2806
|
-
name: z
|
|
2803
|
+
}), z.object({
|
|
2804
|
+
ssoProviderId: z.string().trim().min(1).max(255),
|
|
2805
|
+
protocol: z.literal("saml"),
|
|
2806
|
+
externalIdSource: z.discriminatedUnion("kind", [z.object({ kind: z.literal("nameId") }), z.object({
|
|
2807
|
+
kind: z.literal("attribute"),
|
|
2808
|
+
name: z.string().trim().min(1).max(255)
|
|
2807
2809
|
})])
|
|
2808
2810
|
})]);
|
|
2809
|
-
const createDirectoryBodySchema = z
|
|
2810
|
-
providerId: z
|
|
2811
|
+
const createDirectoryBodySchema = z.object({
|
|
2812
|
+
providerId: z.string().trim().min(1).max(255),
|
|
2811
2813
|
pairing: directorySyncSSOPairingSchema.optional(),
|
|
2812
2814
|
...credentialPolicySchema
|
|
2813
2815
|
});
|
|
2814
|
-
const rotateCredentialBodySchema = z
|
|
2815
|
-
const emptyBodySchema = z
|
|
2816
|
+
const rotateCredentialBodySchema = z.object(credentialPolicySchema);
|
|
2817
|
+
const emptyBodySchema = z.object({});
|
|
2816
2818
|
function setCredentialResponseSecurityHeaders(ctx) {
|
|
2817
2819
|
ctx.setHeader("Cache-Control", "no-store, max-age=0");
|
|
2818
2820
|
ctx.setHeader("Pragma", "no-cache");
|
|
@@ -2822,7 +2824,7 @@ function setCredentialResponseSecurityHeaders(ctx) {
|
|
|
2822
2824
|
//#region src/directory-sync/pairing.ts
|
|
2823
2825
|
let samlPolicyModule;
|
|
2824
2826
|
function loadSAMLPolicy() {
|
|
2825
|
-
samlPolicyModule ??= import("./saml-policy-
|
|
2827
|
+
samlPolicyModule ??= import("./saml-policy-DQR0MBLk.mjs");
|
|
2826
2828
|
return samlPolicyModule;
|
|
2827
2829
|
}
|
|
2828
2830
|
function isRecord$1(value) {
|
|
@@ -3017,35 +3019,41 @@ function instrumentDirectorySyncIntegration(ctx, options) {
|
|
|
3017
3019
|
logger.debug("[Dash] Managed directory sync enabled but SSO/SCIM plugins are not active. Skipping integration instrumentation");
|
|
3018
3020
|
return;
|
|
3019
3021
|
}
|
|
3020
|
-
if (options.ssoPairing)
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3022
|
+
if (options.ssoPairing) {
|
|
3023
|
+
if (ssoPlugin) {
|
|
3024
|
+
if (!ssoPlugin.options) logger.error("[Dash] Managed directory sync ssoPairing requires sso({ ... }) (at least sso({})) so pairing callbacks can be installed on the shared options object.");
|
|
3025
|
+
else {
|
|
3026
|
+
const ssoOptions = ssoPlugin.options;
|
|
3027
|
+
const previousResolveUser = ssoOptions.resolveUser;
|
|
3028
|
+
ssoOptions.resolveUser = async (input, context) => {
|
|
3029
|
+
const result = await resolveOrganizationDirectorySyncUser(input, context);
|
|
3030
|
+
if (result.action !== "continue") return result;
|
|
3031
|
+
if (previousResolveUser) return previousResolveUser(input, context);
|
|
3032
|
+
return { action: "continue" };
|
|
3033
|
+
};
|
|
3034
|
+
const previousGuard = ssoOptions.guardProviderMutation;
|
|
3035
|
+
ssoOptions.guardProviderMutation = async (input, context) => {
|
|
3036
|
+
await guardDirectorySyncSSOProviderMutation(input, context);
|
|
3037
|
+
if (previousGuard) await previousGuard(input, context);
|
|
3038
|
+
};
|
|
3039
|
+
}
|
|
3040
|
+
} else logger.debug("[Dash] Managed directory sync ssoPairing enabled but SSO plugin is not active. Skipping SSO pairing instrumentation");
|
|
3035
3041
|
}
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3042
|
+
if (options.membershipProjection.enabled) {
|
|
3043
|
+
if (scimPlugin) {
|
|
3044
|
+
if (!scimPlugin.options) logger.error("[Dash] Managed directory sync membershipProjection requires scim({ ... }) with an options object so membership projection can be installed.");
|
|
3045
|
+
else {
|
|
3046
|
+
const scimOptions = scimPlugin.options;
|
|
3047
|
+
const projection = scimOptions.projection ??= {};
|
|
3048
|
+
const previousReconcileUser = projection.reconcileUser;
|
|
3049
|
+
const reconcileUser = createOrganizationMembershipProjection({ role: options.membershipProjection.role });
|
|
3050
|
+
projection.reconcileUser = async (input, context) => {
|
|
3051
|
+
await reconcileUser(input, context);
|
|
3052
|
+
if (previousReconcileUser) await previousReconcileUser(input, context);
|
|
3053
|
+
};
|
|
3054
|
+
}
|
|
3055
|
+
} else logger.debug("[Dash] Managed directory sync membershipProjection enabled but SCIM plugin is not active. Skipping membership projection instrumentation");
|
|
3047
3056
|
}
|
|
3048
|
-
else logger.debug("[Dash] Managed directory sync membershipProjection enabled but SCIM plugin is not active. Skipping membership projection instrumentation");
|
|
3049
3057
|
}
|
|
3050
3058
|
//#endregion
|
|
3051
3059
|
//#region src/events/organization/events-invitation.ts
|
|
@@ -3462,7 +3470,7 @@ function snakeCaseToCamelCase(key) {
|
|
|
3462
3470
|
function keyVariantsForMatching(key) {
|
|
3463
3471
|
const trimmed = key.replace(/^[\s._-]+/, "");
|
|
3464
3472
|
if (!trimmed) return [key];
|
|
3465
|
-
const out = new Set([key, trimmed]);
|
|
3473
|
+
const out = /* @__PURE__ */ new Set([key, trimmed]);
|
|
3466
3474
|
if (trimmed.includes("_")) out.add(snakeCaseToCamelCase(trimmed));
|
|
3467
3475
|
return [...out];
|
|
3468
3476
|
}
|
|
@@ -3482,7 +3490,7 @@ function matchesNormalizedPatterns(key, patterns) {
|
|
|
3482
3490
|
if (!hasAnyPatterns(patterns)) return false;
|
|
3483
3491
|
const variants = keyVariantsForMatching(key);
|
|
3484
3492
|
const compactLower = key.replace(/[\s._-]/g, "").toLowerCase();
|
|
3485
|
-
const forms = new Set([compactLower]);
|
|
3493
|
+
const forms = /* @__PURE__ */ new Set([compactLower]);
|
|
3486
3494
|
for (const raw of variants) {
|
|
3487
3495
|
forms.add(raw);
|
|
3488
3496
|
forms.add(raw.toLowerCase());
|
|
@@ -3540,9 +3548,9 @@ function redact(value, options) {
|
|
|
3540
3548
|
const patterns = normalizePatterns(options?.patterns);
|
|
3541
3549
|
return redactInner(value, /* @__PURE__ */ new WeakSet(), options, patterns);
|
|
3542
3550
|
}
|
|
3543
|
-
const DASH_PLUGIN_OPTIONS_EXCLUDE_KEYS = { stripe: new Set(["stripeClient"]) };
|
|
3551
|
+
const DASH_PLUGIN_OPTIONS_EXCLUDE_KEYS = { stripe: /* @__PURE__ */ new Set(["stripeClient"]) };
|
|
3544
3552
|
/** Storage-strategy enums that match sensitive key suffixes but are not secrets. */
|
|
3545
|
-
const DASH_PLUGIN_OPTIONS_IGNORE_KEYS = new Set([
|
|
3553
|
+
const DASH_PLUGIN_OPTIONS_IGNORE_KEYS = /* @__PURE__ */ new Set([
|
|
3546
3554
|
"storeApiKey",
|
|
3547
3555
|
"storeClientSecret",
|
|
3548
3556
|
"storeSCIMToken",
|
|
@@ -3623,7 +3631,7 @@ function timingSafeEqualHash(a, b) {
|
|
|
3623
3631
|
* A freshly issued token is almost certainly legitimate.
|
|
3624
3632
|
*/
|
|
3625
3633
|
const JTI_CHECK_GRACE_PERIOD_SECONDS = 30;
|
|
3626
|
-
const JWKS_CACHE_TTL_MS =
|
|
3634
|
+
const JWKS_CACHE_TTL_MS = 9e5;
|
|
3627
3635
|
const jwksCache = /* @__PURE__ */ new Map();
|
|
3628
3636
|
const inflightRequests = /* @__PURE__ */ new Map();
|
|
3629
3637
|
async function fetchJWKS(ctx, cacheKey, $api) {
|
|
@@ -3672,7 +3680,8 @@ const jwtMiddleware = (options, schema, getJWT) => {
|
|
|
3672
3680
|
ctx.context.logger.warn("[Dash] JWT is missing from header");
|
|
3673
3681
|
throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
|
|
3674
3682
|
}
|
|
3675
|
-
const
|
|
3683
|
+
const remoteJWKs = await getJWKs(ctx, cacheKey, $api);
|
|
3684
|
+
const { payload } = await jwtVerify(jwsFromHeader, remoteJWKs, { maxTokenAge: "5m" }).catch((e) => {
|
|
3676
3685
|
ctx.context.logger.warn("[Dash] JWT verification failed:", e);
|
|
3677
3686
|
throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
|
|
3678
3687
|
});
|
|
@@ -3726,7 +3735,8 @@ const jwtValidateMiddleware = (options) => {
|
|
|
3726
3735
|
ctx.context.logger.warn("[Dash] JWT is missing from header");
|
|
3727
3736
|
throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
|
|
3728
3737
|
}
|
|
3729
|
-
const
|
|
3738
|
+
const remoteJWKs = await getJWKs(ctx, cacheKey, $api);
|
|
3739
|
+
const { payload } = await jwtVerify(jwsFromHeader, remoteJWKs, { maxTokenAge: "5m" }).catch((e) => {
|
|
3730
3740
|
ctx.context.logger.error("[Dash] JWT verification failed:", e);
|
|
3731
3741
|
throw ctx.error("UNAUTHORIZED", { message: "Invalid API key" });
|
|
3732
3742
|
});
|
|
@@ -4055,12 +4065,12 @@ function assertManagedDirectorySyncEnabled(ctx) {
|
|
|
4055
4065
|
}
|
|
4056
4066
|
//#endregion
|
|
4057
4067
|
//#region src/routes/directory-sync/route-contract.ts
|
|
4058
|
-
const DIRECTORY_SYNC_CREDENTIAL_LIFETIME_MS =
|
|
4059
|
-
const directorySyncClaimsSchema = z
|
|
4060
|
-
purpose: z
|
|
4061
|
-
organizationId: z
|
|
4062
|
-
actorId: z
|
|
4063
|
-
setupOperationId: z
|
|
4068
|
+
const DIRECTORY_SYNC_CREDENTIAL_LIFETIME_MS = 31536e6;
|
|
4069
|
+
const directorySyncClaimsSchema = z.object({
|
|
4070
|
+
purpose: z.literal(DIRECTORY_SYNC_PURPOSE),
|
|
4071
|
+
organizationId: z.string().trim().min(1),
|
|
4072
|
+
actorId: z.string().trim().min(1),
|
|
4073
|
+
setupOperationId: z.string().trim().min(16).max(255).optional()
|
|
4064
4074
|
});
|
|
4065
4075
|
function getScimEndpoint$1(baseUrl) {
|
|
4066
4076
|
return `${baseUrl}/scim/v2`;
|
|
@@ -4772,10 +4782,10 @@ const revokeDirectoryCredential = (options) => createAuthEndpoint("/dash/organiz
|
|
|
4772
4782
|
const DIRECTORY_EVENTS_DEFAULT_LIMIT = 10;
|
|
4773
4783
|
const DIRECTORY_PAGE_MAX_LIMIT = 100;
|
|
4774
4784
|
const DIRECTORY_EVENTS_DEFAULT_SORT_DIRECTION = "desc";
|
|
4775
|
-
const directoryEventsQuerySchema = z
|
|
4776
|
-
limit: z
|
|
4777
|
-
offset: z
|
|
4778
|
-
sortDirection: z
|
|
4785
|
+
const directoryEventsQuerySchema = z.object({
|
|
4786
|
+
limit: z.number().or(z.string().transform(Number)).optional(),
|
|
4787
|
+
offset: z.number().or(z.string().transform(Number)).optional(),
|
|
4788
|
+
sortDirection: z.enum(["asc", "desc"]).optional()
|
|
4779
4789
|
}).optional();
|
|
4780
4790
|
function resolveDirectoryEventsPage(query) {
|
|
4781
4791
|
const requestedLimit = query?.limit;
|
|
@@ -4927,7 +4937,7 @@ function parseMemberRoles(role) {
|
|
|
4927
4937
|
}
|
|
4928
4938
|
function getScimManagementRoles(ctx) {
|
|
4929
4939
|
const creatorRole = ctx.context.getPlugin("organization")?.options?.creatorRole ?? "owner";
|
|
4930
|
-
return Array.from(new Set(["admin", creatorRole]));
|
|
4940
|
+
return Array.from(/* @__PURE__ */ new Set(["admin", creatorRole]));
|
|
4931
4941
|
}
|
|
4932
4942
|
async function findOrganizationScimManagerUserId(ctx, organizationId) {
|
|
4933
4943
|
const requiredRoles = getScimManagementRoles(ctx);
|
|
@@ -5028,10 +5038,10 @@ const listOrganizationDirectories = (options) => {
|
|
|
5028
5038
|
const createOrganizationDirectoryLegacy = (options) => {
|
|
5029
5039
|
return createAuthEndpoint("/dash/organization/directory/create", {
|
|
5030
5040
|
method: "POST",
|
|
5031
|
-
use: [jwtMiddleware(options, z
|
|
5032
|
-
body: z
|
|
5033
|
-
providerId: z
|
|
5034
|
-
ownerUserId: z
|
|
5041
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
5042
|
+
body: z.object({
|
|
5043
|
+
providerId: z.string().min(1, "Provider ID is required"),
|
|
5044
|
+
ownerUserId: z.string().min(1, "Owner user ID is required")
|
|
5035
5045
|
})
|
|
5036
5046
|
}, async (ctx) => {
|
|
5037
5047
|
requireOrganizationPlugin(ctx);
|
|
@@ -5068,8 +5078,8 @@ const createOrganizationDirectoryLegacy = (options) => {
|
|
|
5068
5078
|
const deleteOrganizationDirectoryLegacy = (options) => {
|
|
5069
5079
|
return createAuthEndpoint("/dash/organization/directory/delete", {
|
|
5070
5080
|
method: "POST",
|
|
5071
|
-
use: [jwtMiddleware(options, z
|
|
5072
|
-
body: z
|
|
5081
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
5082
|
+
body: z.object({ providerId: z.string().min(1, "Provider ID is required") })
|
|
5073
5083
|
}, async (ctx) => {
|
|
5074
5084
|
requireOrganizationPlugin(ctx);
|
|
5075
5085
|
const scimPlugin = getSCIMPlugin(ctx);
|
|
@@ -5092,8 +5102,8 @@ const deleteOrganizationDirectoryLegacy = (options) => {
|
|
|
5092
5102
|
const regenerateDirectoryTokenLegacy = (options) => {
|
|
5093
5103
|
return createAuthEndpoint("/dash/organization/directory/regenerate-token", {
|
|
5094
5104
|
method: "POST",
|
|
5095
|
-
use: [jwtMiddleware(options, z
|
|
5096
|
-
body: z
|
|
5105
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
5106
|
+
body: z.object({ providerId: z.string().min(1, "Provider ID is required") })
|
|
5097
5107
|
}, async (ctx) => {
|
|
5098
5108
|
requireOrganizationPlugin(ctx);
|
|
5099
5109
|
const scimPlugin = getSCIMPlugin(ctx);
|
|
@@ -5163,13 +5173,13 @@ const getUserEvents = (options) => {
|
|
|
5163
5173
|
return createAuthEndpoint("/events/list", {
|
|
5164
5174
|
method: "GET",
|
|
5165
5175
|
use: [sessionMiddleware],
|
|
5166
|
-
query: z
|
|
5176
|
+
query: z.object({
|
|
5167
5177
|
/** Maximum number of events to return (default: 50, max: 100) */
|
|
5168
|
-
limit: z
|
|
5178
|
+
limit: z.number().or(z.string().transform(Number)).optional(),
|
|
5169
5179
|
/** Number of events to skip for pagination (default: 0) */
|
|
5170
|
-
offset: z
|
|
5180
|
+
offset: z.number().or(z.string().transform(Number)).optional(),
|
|
5171
5181
|
/** Filter by event type (e.g., "user_signed_in") */
|
|
5172
|
-
eventType: z
|
|
5182
|
+
eventType: z.string().optional()
|
|
5173
5183
|
}).optional()
|
|
5174
5184
|
}, async (ctx) => {
|
|
5175
5185
|
const session = ctx.context.session;
|
|
@@ -5232,13 +5242,13 @@ const getAuditLogs = (options) => {
|
|
|
5232
5242
|
return createAuthEndpoint("/events/audit-logs", {
|
|
5233
5243
|
method: "GET",
|
|
5234
5244
|
use: [sessionMiddleware],
|
|
5235
|
-
query: z
|
|
5236
|
-
limit: z
|
|
5237
|
-
offset: z
|
|
5238
|
-
userId: z
|
|
5239
|
-
organizationId: z
|
|
5240
|
-
identifier: z
|
|
5241
|
-
eventType: z
|
|
5245
|
+
query: z.object({
|
|
5246
|
+
limit: z.number().or(z.string().transform(Number)).optional(),
|
|
5247
|
+
offset: z.number().or(z.string().transform(Number)).optional(),
|
|
5248
|
+
userId: z.string().optional(),
|
|
5249
|
+
organizationId: z.string().optional(),
|
|
5250
|
+
identifier: z.string().optional(),
|
|
5251
|
+
eventType: z.string().optional()
|
|
5242
5252
|
}).optional()
|
|
5243
5253
|
}, async (ctx) => {
|
|
5244
5254
|
const session = ctx.context.session;
|
|
@@ -5313,7 +5323,7 @@ const getAuditLogs = (options) => {
|
|
|
5313
5323
|
};
|
|
5314
5324
|
});
|
|
5315
5325
|
};
|
|
5316
|
-
const OWNER_ADMIN_ROLES = new Set(["owner", "admin"]);
|
|
5326
|
+
const OWNER_ADMIN_ROLES = /* @__PURE__ */ new Set(["owner", "admin"]);
|
|
5317
5327
|
function isOwnerOrAdminRole(role) {
|
|
5318
5328
|
return role !== void 0 && OWNER_ADMIN_ROLES.has(role);
|
|
5319
5329
|
}
|
|
@@ -5354,15 +5364,15 @@ const getAllAuditLogs = (options) => {
|
|
|
5354
5364
|
return createAuthEndpoint("/events/all-audit-logs", {
|
|
5355
5365
|
method: "GET",
|
|
5356
5366
|
use: [sessionMiddleware],
|
|
5357
|
-
query: z
|
|
5358
|
-
limit: z
|
|
5359
|
-
offset: z
|
|
5360
|
-
userId: z
|
|
5361
|
-
organizationId: z
|
|
5367
|
+
query: z.object({
|
|
5368
|
+
limit: z.number().or(z.string().transform(Number)).optional(),
|
|
5369
|
+
offset: z.number().or(z.string().transform(Number)).optional(),
|
|
5370
|
+
userId: z.string().optional(),
|
|
5371
|
+
organizationId: z.string().optional(),
|
|
5362
5372
|
/** Filter by event type (e.g. `organization_member_added`) */
|
|
5363
|
-
eventType: z
|
|
5373
|
+
eventType: z.string().optional(),
|
|
5364
5374
|
/** Match `eventData.identifier` (organization-scoped actor identity) */
|
|
5365
|
-
identifier: z
|
|
5375
|
+
identifier: z.string().optional()
|
|
5366
5376
|
}).refine((q) => {
|
|
5367
5377
|
const u = q.userId?.trim();
|
|
5368
5378
|
const o = q.organizationId?.trim();
|
|
@@ -5419,10 +5429,10 @@ const getAllAuditLogs = (options) => {
|
|
|
5419
5429
|
};
|
|
5420
5430
|
//#endregion
|
|
5421
5431
|
//#region src/routes/execute-adapter/index.ts
|
|
5422
|
-
const whereClause = z.object({
|
|
5423
|
-
field: z.string(),
|
|
5424
|
-
value: z.unknown(),
|
|
5425
|
-
operator: z.enum([
|
|
5432
|
+
const whereClause = z$1.object({
|
|
5433
|
+
field: z$1.string(),
|
|
5434
|
+
value: z$1.unknown(),
|
|
5435
|
+
operator: z$1.enum([
|
|
5426
5436
|
"eq",
|
|
5427
5437
|
"ne",
|
|
5428
5438
|
"gt",
|
|
@@ -5434,44 +5444,44 @@ const whereClause = z.object({
|
|
|
5434
5444
|
"starts_with",
|
|
5435
5445
|
"ends_with"
|
|
5436
5446
|
]).optional(),
|
|
5437
|
-
connector: z.enum(["AND", "OR"]).optional()
|
|
5447
|
+
connector: z$1.enum(["AND", "OR"]).optional()
|
|
5438
5448
|
});
|
|
5439
|
-
const sortBySchema = z.object({
|
|
5440
|
-
field: z.string(),
|
|
5441
|
-
direction: z.enum(["asc", "desc"])
|
|
5449
|
+
const sortBySchema = z$1.object({
|
|
5450
|
+
field: z$1.string(),
|
|
5451
|
+
direction: z$1.enum(["asc", "desc"])
|
|
5442
5452
|
});
|
|
5443
|
-
const actionSchema = z.discriminatedUnion("action", [
|
|
5444
|
-
z.object({
|
|
5445
|
-
action: z.literal("findOne"),
|
|
5446
|
-
model: z.string(),
|
|
5447
|
-
where: z.array(whereClause).optional(),
|
|
5448
|
-
select: z.array(z.string()).optional(),
|
|
5449
|
-
join: z.record(z.string(), z.boolean()).optional()
|
|
5453
|
+
const actionSchema = z$1.discriminatedUnion("action", [
|
|
5454
|
+
z$1.object({
|
|
5455
|
+
action: z$1.literal("findOne"),
|
|
5456
|
+
model: z$1.string(),
|
|
5457
|
+
where: z$1.array(whereClause).optional(),
|
|
5458
|
+
select: z$1.array(z$1.string()).optional(),
|
|
5459
|
+
join: z$1.record(z$1.string(), z$1.boolean()).optional()
|
|
5450
5460
|
}),
|
|
5451
|
-
z.object({
|
|
5452
|
-
action: z.literal("findMany"),
|
|
5453
|
-
model: z.string(),
|
|
5454
|
-
where: z.array(whereClause).optional(),
|
|
5455
|
-
limit: z.number().optional(),
|
|
5456
|
-
offset: z.number().optional(),
|
|
5461
|
+
z$1.object({
|
|
5462
|
+
action: z$1.literal("findMany"),
|
|
5463
|
+
model: z$1.string(),
|
|
5464
|
+
where: z$1.array(whereClause).optional(),
|
|
5465
|
+
limit: z$1.number().optional(),
|
|
5466
|
+
offset: z$1.number().optional(),
|
|
5457
5467
|
sortBy: sortBySchema.optional(),
|
|
5458
|
-
join: z.record(z.string(), z.boolean()).optional()
|
|
5468
|
+
join: z$1.record(z$1.string(), z$1.boolean()).optional()
|
|
5459
5469
|
}),
|
|
5460
|
-
z.object({
|
|
5461
|
-
action: z.literal("create"),
|
|
5462
|
-
model: z.string(),
|
|
5463
|
-
data: z.record(z.string(), z.unknown())
|
|
5470
|
+
z$1.object({
|
|
5471
|
+
action: z$1.literal("create"),
|
|
5472
|
+
model: z$1.string(),
|
|
5473
|
+
data: z$1.record(z$1.string(), z$1.unknown())
|
|
5464
5474
|
}),
|
|
5465
|
-
z.object({
|
|
5466
|
-
action: z.literal("update"),
|
|
5467
|
-
model: z.string(),
|
|
5468
|
-
where: z.array(whereClause),
|
|
5469
|
-
update: z.record(z.string(), z.unknown())
|
|
5475
|
+
z$1.object({
|
|
5476
|
+
action: z$1.literal("update"),
|
|
5477
|
+
model: z$1.string(),
|
|
5478
|
+
where: z$1.array(whereClause),
|
|
5479
|
+
update: z$1.record(z$1.string(), z$1.unknown())
|
|
5470
5480
|
}),
|
|
5471
|
-
z.object({
|
|
5472
|
-
action: z.literal("count"),
|
|
5473
|
-
model: z.string(),
|
|
5474
|
-
where: z.array(whereClause).optional()
|
|
5481
|
+
z$1.object({
|
|
5482
|
+
action: z$1.literal("count"),
|
|
5483
|
+
model: z$1.string(),
|
|
5484
|
+
where: z$1.array(whereClause).optional()
|
|
5475
5485
|
})
|
|
5476
5486
|
]);
|
|
5477
5487
|
const executeAdapter = (options) => {
|
|
@@ -5589,8 +5599,8 @@ function isSafeHttpUrl(value) {
|
|
|
5589
5599
|
}
|
|
5590
5600
|
}
|
|
5591
5601
|
/** http(s) absolute URL without embedded credentials. */
|
|
5592
|
-
const safeUrlSchema = z
|
|
5593
|
-
const optionalSafeUrlSchema = z
|
|
5602
|
+
const safeUrlSchema = z.string().trim().min(1).pipe(z.url().refine(isSafeHttpUrl, { message: "URL must be a valid http(s) URL without credentials" })).transform((value) => new URL(value).href);
|
|
5603
|
+
const optionalSafeUrlSchema = z.union([safeUrlSchema, z.literal("")]).optional();
|
|
5594
5604
|
function getAuthBaseUrl(ctx) {
|
|
5595
5605
|
const baseURL = ctx.context.options.baseURL;
|
|
5596
5606
|
return typeof baseURL === "string" && baseURL.trim() ? baseURL : "/";
|
|
@@ -5669,10 +5679,13 @@ async function executePlatformInvitationCompletion(ctx, $api, invitation, args)
|
|
|
5669
5679
|
}
|
|
5670
5680
|
});
|
|
5671
5681
|
if (markError) ctx.context.logger.warn("[Dash] Failed to mark invitation as accepted", markError);
|
|
5672
|
-
if (shouldCreateSessionOnInviteComplete(invitation.authMode))
|
|
5673
|
-
session
|
|
5674
|
-
|
|
5675
|
-
|
|
5682
|
+
if (shouldCreateSessionOnInviteComplete(invitation.authMode)) {
|
|
5683
|
+
const session = await ctx.context.internalAdapter.createSession(existingUser.id);
|
|
5684
|
+
await setSessionCookie(ctx, {
|
|
5685
|
+
session,
|
|
5686
|
+
user: existingUser
|
|
5687
|
+
});
|
|
5688
|
+
}
|
|
5676
5689
|
return { redirectUrl: resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl, getAuthBaseUrl(ctx)) };
|
|
5677
5690
|
}
|
|
5678
5691
|
if (!password && !allowsPasswordlessInviteComplete(invitation.authMode)) throw new APIError("BAD_REQUEST", { message: "Password is required to complete this invitation." });
|
|
@@ -5697,10 +5710,13 @@ async function executePlatformInvitationCompletion(ctx, $api, invitation, args)
|
|
|
5697
5710
|
}
|
|
5698
5711
|
});
|
|
5699
5712
|
if (markError) ctx.context.logger.warn("[Dash] Failed to mark invitation as accepted", markError);
|
|
5700
|
-
if (shouldCreateSessionOnInviteComplete(invitation.authMode))
|
|
5701
|
-
session
|
|
5702
|
-
|
|
5703
|
-
|
|
5713
|
+
if (shouldCreateSessionOnInviteComplete(invitation.authMode)) {
|
|
5714
|
+
const session = await ctx.context.internalAdapter.createSession(user.id);
|
|
5715
|
+
await setSessionCookie(ctx, {
|
|
5716
|
+
session,
|
|
5717
|
+
user
|
|
5718
|
+
});
|
|
5719
|
+
}
|
|
5704
5720
|
return { redirectUrl: resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl, getAuthBaseUrl(ctx)) };
|
|
5705
5721
|
}
|
|
5706
5722
|
/**
|
|
@@ -5712,7 +5728,7 @@ const acceptInvitation = (options) => {
|
|
|
5712
5728
|
const { $api } = options;
|
|
5713
5729
|
return createAuthEndpoint("/dash/accept-invitation", {
|
|
5714
5730
|
method: "GET",
|
|
5715
|
-
query: z
|
|
5731
|
+
query: z.object({ token: z.string() })
|
|
5716
5732
|
}, async (ctx) => {
|
|
5717
5733
|
const { token } = ctx.query;
|
|
5718
5734
|
const invitation = await verifyPendingInvitation(token, $api, ctx);
|
|
@@ -5727,10 +5743,13 @@ const acceptInvitation = (options) => {
|
|
|
5727
5743
|
}
|
|
5728
5744
|
});
|
|
5729
5745
|
if (markError) ctx.context.logger.warn("[Dash] Failed to mark invitation as accepted (existing user)", markError);
|
|
5730
|
-
if (shouldCreateSessionOnInviteComplete(invitation.authMode))
|
|
5731
|
-
session
|
|
5732
|
-
|
|
5733
|
-
|
|
5746
|
+
if (shouldCreateSessionOnInviteComplete(invitation.authMode)) {
|
|
5747
|
+
const session = await ctx.context.internalAdapter.createSession(existingUser.id);
|
|
5748
|
+
await setSessionCookie(ctx, {
|
|
5749
|
+
session,
|
|
5750
|
+
user: existingUser
|
|
5751
|
+
});
|
|
5752
|
+
}
|
|
5734
5753
|
const redirectUrl = resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl, getAuthBaseUrl(ctx));
|
|
5735
5754
|
return ctx.redirect(redirectUrl);
|
|
5736
5755
|
}
|
|
@@ -5759,10 +5778,13 @@ const acceptInvitation = (options) => {
|
|
|
5759
5778
|
}
|
|
5760
5779
|
});
|
|
5761
5780
|
if (markError) ctx.context.logger.warn("[Dash] Failed to mark invitation as accepted", markError);
|
|
5762
|
-
if (shouldCreateSessionOnInviteComplete(invitation.authMode))
|
|
5763
|
-
session
|
|
5764
|
-
|
|
5765
|
-
|
|
5781
|
+
if (shouldCreateSessionOnInviteComplete(invitation.authMode)) {
|
|
5782
|
+
const session = await ctx.context.internalAdapter.createSession(user.id);
|
|
5783
|
+
await setSessionCookie(ctx, {
|
|
5784
|
+
session,
|
|
5785
|
+
user
|
|
5786
|
+
});
|
|
5787
|
+
}
|
|
5766
5788
|
const redirectUrl = resolveTrustedAuthRedirectUrl(ctx, invitation.redirectUrl, getAuthBaseUrl(ctx));
|
|
5767
5789
|
return ctx.redirect(redirectUrl);
|
|
5768
5790
|
});
|
|
@@ -5776,13 +5798,14 @@ const completeInvitation = (options) => {
|
|
|
5776
5798
|
const { $api } = options;
|
|
5777
5799
|
return createAuthEndpoint("/dash/complete-invitation", {
|
|
5778
5800
|
method: "POST",
|
|
5779
|
-
body: z
|
|
5780
|
-
token: z
|
|
5781
|
-
password: z
|
|
5801
|
+
body: z.object({
|
|
5802
|
+
token: z.string(),
|
|
5803
|
+
password: z.string().optional()
|
|
5782
5804
|
})
|
|
5783
5805
|
}, async (ctx) => {
|
|
5784
5806
|
const { token, password } = ctx.body;
|
|
5785
|
-
const
|
|
5807
|
+
const invitation = await verifyPendingInvitation(token, $api, ctx);
|
|
5808
|
+
const { redirectUrl } = await executePlatformInvitationCompletion(ctx, $api, invitation, {
|
|
5786
5809
|
token,
|
|
5787
5810
|
password
|
|
5788
5811
|
});
|
|
@@ -5800,14 +5823,15 @@ const completeInvitationHandoff = (options) => {
|
|
|
5800
5823
|
const { $api } = options;
|
|
5801
5824
|
return createAuthEndpoint("/dash/complete-invitation-handoff", {
|
|
5802
5825
|
method: "GET",
|
|
5803
|
-
query: z
|
|
5826
|
+
query: z.object({ handoff: z.string().min(1) })
|
|
5804
5827
|
}, async (ctx) => {
|
|
5805
5828
|
const { data, error } = await $api("/api/internal/invitations/redeem-handoff", {
|
|
5806
5829
|
method: "POST",
|
|
5807
5830
|
body: { handoff: ctx.query.handoff }
|
|
5808
5831
|
});
|
|
5809
5832
|
if (error || !data?.invitationToken) throw new APIError("BAD_REQUEST", { message: "This invitation link has expired. Please try again." });
|
|
5810
|
-
const
|
|
5833
|
+
const invitation = await verifyPendingInvitation(data.invitationToken, $api, ctx);
|
|
5834
|
+
const { redirectUrl } = await executePlatformInvitationCompletion(ctx, $api, invitation, {
|
|
5811
5835
|
token: data.invitationToken,
|
|
5812
5836
|
password: data.password ?? void 0
|
|
5813
5837
|
});
|
|
@@ -5818,7 +5842,7 @@ const completeInvitationSocial = (options) => {
|
|
|
5818
5842
|
const { $api } = options;
|
|
5819
5843
|
return createAuthEndpoint("/dash/complete-invitation-social", {
|
|
5820
5844
|
method: "GET",
|
|
5821
|
-
query: z
|
|
5845
|
+
query: z.object({ token: z.string() }),
|
|
5822
5846
|
use: [sessionMiddleware]
|
|
5823
5847
|
}, async (ctx) => {
|
|
5824
5848
|
const sessionUser = ctx.context.session?.user;
|
|
@@ -5853,7 +5877,7 @@ const checkUserExists = (options) => {
|
|
|
5853
5877
|
return createAuthEndpoint("/dash/check-user-exists", {
|
|
5854
5878
|
method: "POST",
|
|
5855
5879
|
use: [jwtMiddleware(options)],
|
|
5856
|
-
body: z
|
|
5880
|
+
body: z.object({ email: z.email() })
|
|
5857
5881
|
}, async (ctx) => {
|
|
5858
5882
|
const { email } = ctx.body;
|
|
5859
5883
|
const normalizedEmail = normalizeEmail(email, ctx.context);
|
|
@@ -5883,7 +5907,7 @@ function getOrganizationRoleKeys(orgOptions) {
|
|
|
5883
5907
|
if (roles && typeof roles === "object" && Object.keys(roles).length > 0) return Object.keys(roles);
|
|
5884
5908
|
return [...DEFAULT_ORG_MEMBER_ROLES];
|
|
5885
5909
|
}
|
|
5886
|
-
const organizationMemberRoleInputSchema = z
|
|
5910
|
+
const organizationMemberRoleInputSchema = z.string().trim().min(1, "Role is required").max(64, "Role is too long").regex(/^[a-zA-Z][a-zA-Z0-9_-]*$/, "Role must start with a letter and contain only letters, numbers, hyphens, and underscores");
|
|
5887
5911
|
function validateOrganizationMemberRole(ctx, role, orgOptions) {
|
|
5888
5912
|
const allowedRoles = getOrganizationRoleKeys(orgOptions);
|
|
5889
5913
|
if (!allowedRoles.includes(role)) throw ctx.error("BAD_REQUEST", { message: `Invalid role. Allowed roles: ${allowedRoles.join(", ")}` });
|
|
@@ -5945,14 +5969,14 @@ const listOrganizationInvitations = (options) => {
|
|
|
5945
5969
|
const inviteMember = (options) => {
|
|
5946
5970
|
return createAuthEndpoint("/dash/organization/invite-member", {
|
|
5947
5971
|
method: "POST",
|
|
5948
|
-
body: z
|
|
5949
|
-
email: z
|
|
5972
|
+
body: z.object({
|
|
5973
|
+
email: z.string(),
|
|
5950
5974
|
role: organizationMemberRoleInputSchema,
|
|
5951
|
-
invitedBy: z
|
|
5975
|
+
invitedBy: z.string()
|
|
5952
5976
|
}),
|
|
5953
|
-
use: [jwtMiddleware(options, z
|
|
5954
|
-
organizationId: z
|
|
5955
|
-
invitedBy: z
|
|
5977
|
+
use: [jwtMiddleware(options, z.object({
|
|
5978
|
+
organizationId: z.string(),
|
|
5979
|
+
invitedBy: z.string()
|
|
5956
5980
|
}))]
|
|
5957
5981
|
}, async (ctx) => {
|
|
5958
5982
|
const { organizationId } = ctx.context.payload;
|
|
@@ -5989,8 +6013,8 @@ const inviteMember = (options) => {
|
|
|
5989
6013
|
const checkUserByEmail = (options) => {
|
|
5990
6014
|
return createAuthEndpoint("/dash/organization/check-user-by-email", {
|
|
5991
6015
|
method: "POST",
|
|
5992
|
-
body: z
|
|
5993
|
-
use: [jwtMiddleware(options, z
|
|
6016
|
+
body: z.object({ email: z.string() }),
|
|
6017
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))]
|
|
5994
6018
|
}, async (ctx) => {
|
|
5995
6019
|
requireOrganizationPlugin(ctx);
|
|
5996
6020
|
const { organizationId } = ctx.context.payload;
|
|
@@ -6033,11 +6057,11 @@ const checkUserByEmail = (options) => {
|
|
|
6033
6057
|
const cancelInvitation = (options) => {
|
|
6034
6058
|
return createAuthEndpoint("/dash/organization/cancel-invitation", {
|
|
6035
6059
|
method: "POST",
|
|
6036
|
-
use: [jwtMiddleware(options, z
|
|
6037
|
-
organizationId: z
|
|
6038
|
-
invitationId: z
|
|
6060
|
+
use: [jwtMiddleware(options, z.object({
|
|
6061
|
+
organizationId: z.string(),
|
|
6062
|
+
invitationId: z.string()
|
|
6039
6063
|
}))],
|
|
6040
|
-
body: z
|
|
6064
|
+
body: z.object({ invitationId: z.string() })
|
|
6041
6065
|
}, async (ctx) => {
|
|
6042
6066
|
const orgOptions = requireOrganizationPlugin(ctx).options || {};
|
|
6043
6067
|
const { invitationId, organizationId } = ctx.context.payload;
|
|
@@ -6091,11 +6115,11 @@ const cancelInvitation = (options) => {
|
|
|
6091
6115
|
const resendInvitation = (options) => {
|
|
6092
6116
|
return createAuthEndpoint("/dash/organization/resend-invitation", {
|
|
6093
6117
|
method: "POST",
|
|
6094
|
-
use: [jwtMiddleware(options, z
|
|
6095
|
-
organizationId: z
|
|
6096
|
-
invitationId: z
|
|
6118
|
+
use: [jwtMiddleware(options, z.object({
|
|
6119
|
+
organizationId: z.string(),
|
|
6120
|
+
invitationId: z.string()
|
|
6097
6121
|
}))],
|
|
6098
|
-
body: z
|
|
6122
|
+
body: z.object({ invitationId: z.string() })
|
|
6099
6123
|
}, async (ctx) => {
|
|
6100
6124
|
const organizationPlugin = requireOrganizationPlugin(ctx);
|
|
6101
6125
|
const { invitationId, organizationId } = ctx.context.payload;
|
|
@@ -6204,9 +6228,9 @@ const listOrganizationMembers = (options) => {
|
|
|
6204
6228
|
const addMember = (options) => {
|
|
6205
6229
|
return createAuthEndpoint("/dash/organization/add-member", {
|
|
6206
6230
|
method: "POST",
|
|
6207
|
-
use: [jwtMiddleware(options, z
|
|
6208
|
-
body: z
|
|
6209
|
-
userId: z
|
|
6231
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
6232
|
+
body: z.object({
|
|
6233
|
+
userId: z.string(),
|
|
6210
6234
|
role: organizationMemberRoleInputSchema
|
|
6211
6235
|
})
|
|
6212
6236
|
}, async (ctx) => {
|
|
@@ -6261,8 +6285,8 @@ const addMember = (options) => {
|
|
|
6261
6285
|
const removeMember = (options) => {
|
|
6262
6286
|
return createAuthEndpoint("/dash/organization/remove-member", {
|
|
6263
6287
|
method: "POST",
|
|
6264
|
-
use: [jwtMiddleware(options, z
|
|
6265
|
-
body: z
|
|
6288
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
6289
|
+
body: z.object({ memberId: z.string() })
|
|
6266
6290
|
}, async (ctx) => {
|
|
6267
6291
|
const { organizationId } = ctx.context.payload;
|
|
6268
6292
|
const orgOptions = requireOrganizationPlugin(ctx).options || {};
|
|
@@ -6331,9 +6355,9 @@ const removeMember = (options) => {
|
|
|
6331
6355
|
const updateMemberRole = (options) => {
|
|
6332
6356
|
return createAuthEndpoint("/dash/organization/update-member-role", {
|
|
6333
6357
|
method: "POST",
|
|
6334
|
-
use: [jwtMiddleware(options, z
|
|
6335
|
-
body: z
|
|
6336
|
-
memberId: z
|
|
6358
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
6359
|
+
body: z.object({
|
|
6360
|
+
memberId: z.string(),
|
|
6337
6361
|
role: organizationMemberRoleInputSchema
|
|
6338
6362
|
})
|
|
6339
6363
|
}, async (ctx) => {
|
|
@@ -6392,7 +6416,7 @@ const updateMemberRole = (options) => {
|
|
|
6392
6416
|
//#region src/export-factory.ts
|
|
6393
6417
|
const exportFactory = (input, options) => async (ctx) => {
|
|
6394
6418
|
const batchSize = options?.batchSize || 1e4;
|
|
6395
|
-
const staleMs = options?.staleMs ||
|
|
6419
|
+
const staleMs = options?.staleMs || 3e5;
|
|
6396
6420
|
const enabledFields = options?.enabledFields || [];
|
|
6397
6421
|
const userLimit = input.limit;
|
|
6398
6422
|
const userOffset = input.offset || 0;
|
|
@@ -6489,16 +6513,16 @@ async function withConcurrency(items, fn, options) {
|
|
|
6489
6513
|
return results;
|
|
6490
6514
|
}
|
|
6491
6515
|
/** Zod schema for validating organization/project slug format. */
|
|
6492
|
-
const slugSchema = z.string().min(1, "Slug is required").regex(/^[a-z0-9-]+$/, "Slug can only contain lowercase letters, numbers, and hyphens");
|
|
6516
|
+
const slugSchema = z$1.string().min(1, "Slug is required").regex(/^[a-z0-9-]+$/, "Slug can only contain lowercase letters, numbers, and hyphens");
|
|
6493
6517
|
//#endregion
|
|
6494
6518
|
//#region src/routes/organizations/schemas.ts
|
|
6495
|
-
const DENIED_ORG_WRITE_KEYS = new Set([
|
|
6519
|
+
const DENIED_ORG_WRITE_KEYS = /* @__PURE__ */ new Set([
|
|
6496
6520
|
"id",
|
|
6497
6521
|
"createdAt",
|
|
6498
6522
|
"updatedAt"
|
|
6499
6523
|
]);
|
|
6500
|
-
const CREATE_NON_PERSISTED_KEYS = new Set(["defaultTeamName"]);
|
|
6501
|
-
const CREATE_REQUEST_ONLY_KEYS$1 = new Set(["userId", "skipDefaultTeam"]);
|
|
6524
|
+
const CREATE_NON_PERSISTED_KEYS = /* @__PURE__ */ new Set(["defaultTeamName"]);
|
|
6525
|
+
const CREATE_REQUEST_ONLY_KEYS$1 = /* @__PURE__ */ new Set(["userId", "skipDefaultTeam"]);
|
|
6502
6526
|
const CORE_CREATE_KEYS$1 = [
|
|
6503
6527
|
"name",
|
|
6504
6528
|
"slug",
|
|
@@ -6530,7 +6554,7 @@ function getWritableOrganizationFieldNames(orgOptions, mode) {
|
|
|
6530
6554
|
*/
|
|
6531
6555
|
function pickWritableOrganizationFields(body, orgOptions, mode) {
|
|
6532
6556
|
const allowed = getWritableOrganizationFieldNames(orgOptions, mode);
|
|
6533
|
-
const skip = new Set([
|
|
6557
|
+
const skip = /* @__PURE__ */ new Set([
|
|
6534
6558
|
...DENIED_ORG_WRITE_KEYS,
|
|
6535
6559
|
...mode === "create" ? CREATE_NON_PERSISTED_KEYS : [],
|
|
6536
6560
|
...mode === "create" ? CREATE_REQUEST_ONLY_KEYS$1 : []
|
|
@@ -6542,26 +6566,26 @@ function pickWritableOrganizationFields(body, orgOptions, mode) {
|
|
|
6542
6566
|
}
|
|
6543
6567
|
return result;
|
|
6544
6568
|
}
|
|
6545
|
-
const BaseCreateOrgCoreBodySchema = z
|
|
6546
|
-
name: z
|
|
6569
|
+
const BaseCreateOrgCoreBodySchema = z.object({
|
|
6570
|
+
name: z.string(),
|
|
6547
6571
|
slug: slugSchema,
|
|
6548
|
-
logo: z
|
|
6549
|
-
defaultTeamName: z
|
|
6572
|
+
logo: z.string().optional(),
|
|
6573
|
+
defaultTeamName: z.string().optional()
|
|
6550
6574
|
});
|
|
6551
|
-
const BaseUpdateOrgCoreBodySchema = z
|
|
6575
|
+
const BaseUpdateOrgCoreBodySchema = z.object({
|
|
6552
6576
|
logo: optionalSafeUrlSchema,
|
|
6553
|
-
name: z
|
|
6577
|
+
name: z.string().optional(),
|
|
6554
6578
|
slug: slugSchema.optional(),
|
|
6555
|
-
metadata: z
|
|
6579
|
+
metadata: z.string().optional()
|
|
6556
6580
|
});
|
|
6557
|
-
const CreateOrganizationBodySchema = BaseCreateOrgCoreBodySchema.catchall(z
|
|
6558
|
-
const UpdateOrganizationBodySchema = BaseUpdateOrgCoreBodySchema.catchall(z
|
|
6581
|
+
const CreateOrganizationBodySchema = BaseCreateOrgCoreBodySchema.catchall(z.unknown());
|
|
6582
|
+
const UpdateOrganizationBodySchema = BaseUpdateOrgCoreBodySchema.catchall(z.unknown());
|
|
6559
6583
|
function createSchemaForDBField$1(field) {
|
|
6560
6584
|
switch (field.type) {
|
|
6561
|
-
case "number": return field.required ? z
|
|
6562
|
-
case "boolean": return field.required ? z
|
|
6563
|
-
case "date": return field.required ? z
|
|
6564
|
-
default: return field.required ? z
|
|
6585
|
+
case "number": return field.required ? z.coerce.number() : z.coerce.number().optional();
|
|
6586
|
+
case "boolean": return field.required ? z.coerce.boolean() : z.coerce.boolean().optional();
|
|
6587
|
+
case "date": return field.required ? z.union([z.string().min(1), z.coerce.date()]) : z.union([z.string(), z.coerce.date()]).optional();
|
|
6588
|
+
default: return field.required ? z.string().min(1) : z.string().optional();
|
|
6565
6589
|
}
|
|
6566
6590
|
}
|
|
6567
6591
|
/**
|
|
@@ -6570,39 +6594,39 @@ function createSchemaForDBField$1(field) {
|
|
|
6570
6594
|
*/
|
|
6571
6595
|
function validateWritableCreateOrganizationFields(data, options) {
|
|
6572
6596
|
const shape = {
|
|
6573
|
-
name: z
|
|
6597
|
+
name: z.string().min(1),
|
|
6574
6598
|
slug: slugSchema,
|
|
6575
|
-
logo: z
|
|
6599
|
+
logo: z.string().optional()
|
|
6576
6600
|
};
|
|
6577
6601
|
const additional = getOrganizationAdditionalFields(options);
|
|
6578
6602
|
for (const [name, field] of Object.entries(additional)) {
|
|
6579
6603
|
if (field.input === false) continue;
|
|
6580
6604
|
shape[name] = createSchemaForDBField$1(field);
|
|
6581
6605
|
}
|
|
6582
|
-
const result = z
|
|
6606
|
+
const result = z.object(shape).strict().safeParse(data);
|
|
6583
6607
|
if (!result.success) throw result.error;
|
|
6584
6608
|
}
|
|
6585
6609
|
/**
|
|
6586
6610
|
* Ensures at least one field is present on update and validates additional field types.
|
|
6587
6611
|
*/
|
|
6588
6612
|
function validateWritableOrganizationUpdateFields(data, orgOptions) {
|
|
6589
|
-
if (Object.keys(data).length === 0) throw new z
|
|
6613
|
+
if (Object.keys(data).length === 0) throw new z.ZodError([{
|
|
6590
6614
|
code: "custom",
|
|
6591
6615
|
message: "No valid fields to update",
|
|
6592
6616
|
path: []
|
|
6593
6617
|
}]);
|
|
6594
6618
|
const shape = {
|
|
6595
|
-
name: z
|
|
6619
|
+
name: z.string().min(1).optional(),
|
|
6596
6620
|
slug: slugSchema.optional(),
|
|
6597
6621
|
logo: optionalSafeUrlSchema,
|
|
6598
|
-
metadata: z
|
|
6622
|
+
metadata: z.string().optional()
|
|
6599
6623
|
};
|
|
6600
6624
|
const additional = getOrganizationAdditionalFields(orgOptions);
|
|
6601
6625
|
for (const [name, field] of Object.entries(additional)) {
|
|
6602
6626
|
if (field.input === false) continue;
|
|
6603
6627
|
shape[name] = createSchemaForDBField$1(field);
|
|
6604
6628
|
}
|
|
6605
|
-
const result = z
|
|
6629
|
+
const result = z.object(shape).partial().strict().safeParse(data);
|
|
6606
6630
|
if (!result.success) throw result.error;
|
|
6607
6631
|
}
|
|
6608
6632
|
//#endregion
|
|
@@ -6644,26 +6668,26 @@ const listOrganizations = (options) => {
|
|
|
6644
6668
|
return createAuthEndpoint("/dash/list-organizations", {
|
|
6645
6669
|
method: "GET",
|
|
6646
6670
|
use: [jwtMiddleware(options)],
|
|
6647
|
-
query: z
|
|
6648
|
-
limit: z
|
|
6649
|
-
offset: z
|
|
6650
|
-
sortBy: z
|
|
6671
|
+
query: z.object({
|
|
6672
|
+
limit: z.number().or(z.string().transform(Number)).optional(),
|
|
6673
|
+
offset: z.number().or(z.string().transform(Number)).optional(),
|
|
6674
|
+
sortBy: z.enum([
|
|
6651
6675
|
"createdAt",
|
|
6652
6676
|
"name",
|
|
6653
6677
|
"slug",
|
|
6654
6678
|
"members"
|
|
6655
6679
|
]).optional(),
|
|
6656
|
-
sortOrder: z
|
|
6657
|
-
filterMembers: z
|
|
6680
|
+
sortOrder: z.enum(["asc", "desc"]).optional(),
|
|
6681
|
+
filterMembers: z.enum([
|
|
6658
6682
|
"abandoned",
|
|
6659
6683
|
"eq1",
|
|
6660
6684
|
"gt1",
|
|
6661
6685
|
"gt5",
|
|
6662
6686
|
"gt10"
|
|
6663
6687
|
]).optional(),
|
|
6664
|
-
search: z
|
|
6665
|
-
startDate: z
|
|
6666
|
-
endDate: z
|
|
6688
|
+
search: z.string().optional(),
|
|
6689
|
+
startDate: z.date().or(z.string().transform((val) => new Date(val))).optional(),
|
|
6690
|
+
endDate: z.date().or(z.string().transform((val) => new Date(val))).optional()
|
|
6667
6691
|
}).optional()
|
|
6668
6692
|
}, async (ctx) => {
|
|
6669
6693
|
const { limit = 10, offset = 0, sortBy = "createdAt", sortOrder = "desc", search, filterMembers } = ctx.query || {};
|
|
@@ -6784,12 +6808,12 @@ function parseWhereClause$1(val) {
|
|
|
6784
6808
|
if (!Array.isArray(parsed)) return [];
|
|
6785
6809
|
return parsed;
|
|
6786
6810
|
}
|
|
6787
|
-
const exportOrganizationsQuerySchema = z
|
|
6788
|
-
limit: z
|
|
6789
|
-
offset: z
|
|
6790
|
-
sortBy: z
|
|
6791
|
-
sortOrder: z
|
|
6792
|
-
where: z
|
|
6811
|
+
const exportOrganizationsQuerySchema = z.object({
|
|
6812
|
+
limit: z.number().or(z.string().transform(Number)).optional(),
|
|
6813
|
+
offset: z.number().or(z.string().transform(Number)).optional(),
|
|
6814
|
+
sortBy: z.string().optional(),
|
|
6815
|
+
sortOrder: z.enum(["asc", "desc"]).optional(),
|
|
6816
|
+
where: z.string().transform(parseWhereClause$1).optional()
|
|
6793
6817
|
}).optional();
|
|
6794
6818
|
const exportOrganizations = (options) => {
|
|
6795
6819
|
return createAuthEndpoint("/dash/export-organizations", {
|
|
@@ -6854,8 +6878,8 @@ const getOrganization = (options) => {
|
|
|
6854
6878
|
const deleteOrganization = (options) => {
|
|
6855
6879
|
return createAuthEndpoint("/dash/organization/delete", {
|
|
6856
6880
|
method: "POST",
|
|
6857
|
-
use: [jwtMiddleware(options, z
|
|
6858
|
-
body: z
|
|
6881
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
6882
|
+
body: z.object({ organizationId: z.string() })
|
|
6859
6883
|
}, async (ctx) => {
|
|
6860
6884
|
const { organizationId } = ctx.context.payload;
|
|
6861
6885
|
const { organizationId: bodyOrganizationId } = ctx.body;
|
|
@@ -6904,7 +6928,7 @@ const deleteOrganization = (options) => {
|
|
|
6904
6928
|
const deleteManyOrganizations = (options) => {
|
|
6905
6929
|
return createAuthEndpoint("/dash/organization/delete-many", {
|
|
6906
6930
|
method: "POST",
|
|
6907
|
-
use: [jwtMiddleware(options, z
|
|
6931
|
+
use: [jwtMiddleware(options, z.object({ organizationIds: z.string().array() }))]
|
|
6908
6932
|
}, async (ctx) => {
|
|
6909
6933
|
requireOrganizationPlugin(ctx);
|
|
6910
6934
|
const { organizationIds } = ctx.context.payload;
|
|
@@ -6941,9 +6965,9 @@ const deleteManyOrganizations = (options) => {
|
|
|
6941
6965
|
const createOrganization = (options) => {
|
|
6942
6966
|
return createAuthEndpoint("/dash/organization/create", {
|
|
6943
6967
|
method: "POST",
|
|
6944
|
-
use: [jwtMiddleware(options, z
|
|
6945
|
-
userId: z
|
|
6946
|
-
skipDefaultTeam: z
|
|
6968
|
+
use: [jwtMiddleware(options, z.object({
|
|
6969
|
+
userId: z.string(),
|
|
6970
|
+
skipDefaultTeam: z.boolean().optional().default(false)
|
|
6947
6971
|
}))],
|
|
6948
6972
|
body: CreateOrganizationBodySchema
|
|
6949
6973
|
}, async (ctx) => {
|
|
@@ -6969,7 +6993,7 @@ const createOrganization = (options) => {
|
|
|
6969
6993
|
try {
|
|
6970
6994
|
validateWritableCreateOrganizationFields(orgData, orgOptions);
|
|
6971
6995
|
} catch (error) {
|
|
6972
|
-
if (error instanceof z
|
|
6996
|
+
if (error instanceof z.ZodError) throw ctx.error("BAD_REQUEST", { message: error.issues[0]?.message ?? "Invalid organization data" });
|
|
6973
6997
|
throw error;
|
|
6974
6998
|
}
|
|
6975
6999
|
if (orgOptions.organizationCreation?.beforeCreate) {
|
|
@@ -7105,7 +7129,7 @@ const createOrganization = (options) => {
|
|
|
7105
7129
|
const updateOrganization = (options) => {
|
|
7106
7130
|
return createAuthEndpoint("/dash/organization/update", {
|
|
7107
7131
|
method: "POST",
|
|
7108
|
-
use: [jwtMiddleware(options, z
|
|
7132
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
7109
7133
|
body: UpdateOrganizationBodySchema
|
|
7110
7134
|
}, async (ctx) => {
|
|
7111
7135
|
const { organizationId } = ctx.context.payload;
|
|
@@ -7145,7 +7169,7 @@ const updateOrganization = (options) => {
|
|
|
7145
7169
|
try {
|
|
7146
7170
|
validateWritableOrganizationUpdateFields(updateData, orgOptions);
|
|
7147
7171
|
} catch (error) {
|
|
7148
|
-
if (error instanceof z
|
|
7172
|
+
if (error instanceof z.ZodError) throw ctx.error("BAD_REQUEST", { message: error.issues[0]?.message ?? "Invalid organization data" });
|
|
7149
7173
|
throw error;
|
|
7150
7174
|
}
|
|
7151
7175
|
if (typeof updateData.metadata === "string") try {
|
|
@@ -7232,10 +7256,10 @@ const listOrganizationTeams = (options) => {
|
|
|
7232
7256
|
const updateTeam = (options) => {
|
|
7233
7257
|
return createAuthEndpoint("/dash/organization/update-team", {
|
|
7234
7258
|
method: "POST",
|
|
7235
|
-
use: [jwtMiddleware(options, z
|
|
7236
|
-
body: z
|
|
7237
|
-
teamId: z
|
|
7238
|
-
name: z
|
|
7259
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
7260
|
+
body: z.object({
|
|
7261
|
+
teamId: z.string(),
|
|
7262
|
+
name: z.string().optional()
|
|
7239
7263
|
})
|
|
7240
7264
|
}, async (ctx) => {
|
|
7241
7265
|
const { organizationId } = ctx.context.payload;
|
|
@@ -7306,8 +7330,8 @@ const updateTeam = (options) => {
|
|
|
7306
7330
|
const deleteTeam = (options) => {
|
|
7307
7331
|
return createAuthEndpoint("/dash/organization/delete-team", {
|
|
7308
7332
|
method: "POST",
|
|
7309
|
-
use: [jwtMiddleware(options, z
|
|
7310
|
-
body: z
|
|
7333
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
7334
|
+
body: z.object({ teamId: z.string() })
|
|
7311
7335
|
}, async (ctx) => {
|
|
7312
7336
|
const { organizationId } = ctx.context.payload;
|
|
7313
7337
|
const orgOptions = requireTeamsEnabled(ctx);
|
|
@@ -7375,8 +7399,8 @@ const deleteTeam = (options) => {
|
|
|
7375
7399
|
const createTeam = (options) => {
|
|
7376
7400
|
return createAuthEndpoint("/dash/organization/create-team", {
|
|
7377
7401
|
method: "POST",
|
|
7378
|
-
use: [jwtMiddleware(options, z
|
|
7379
|
-
body: z
|
|
7402
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
7403
|
+
body: z.object({ name: z.string() })
|
|
7380
7404
|
}, async (ctx) => {
|
|
7381
7405
|
const { organizationId } = ctx.context.payload;
|
|
7382
7406
|
const orgOptions = requireTeamsEnabled(ctx);
|
|
@@ -7494,10 +7518,10 @@ const listTeamMembers = (options) => {
|
|
|
7494
7518
|
const addTeamMember = (options) => {
|
|
7495
7519
|
return createAuthEndpoint("/dash/organization/add-team-member", {
|
|
7496
7520
|
method: "POST",
|
|
7497
|
-
use: [jwtMiddleware(options, z
|
|
7498
|
-
body: z
|
|
7499
|
-
teamId: z
|
|
7500
|
-
userId: z
|
|
7521
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
7522
|
+
body: z.object({
|
|
7523
|
+
teamId: z.string(),
|
|
7524
|
+
userId: z.string()
|
|
7501
7525
|
})
|
|
7502
7526
|
}, async (ctx) => {
|
|
7503
7527
|
const { organizationId } = ctx.context.payload;
|
|
@@ -7587,10 +7611,10 @@ const addTeamMember = (options) => {
|
|
|
7587
7611
|
const removeTeamMember = (options) => {
|
|
7588
7612
|
return createAuthEndpoint("/dash/organization/remove-team-member", {
|
|
7589
7613
|
method: "POST",
|
|
7590
|
-
use: [jwtMiddleware(options, z
|
|
7591
|
-
body: z
|
|
7592
|
-
teamId: z
|
|
7593
|
-
userId: z
|
|
7614
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
7615
|
+
body: z.object({
|
|
7616
|
+
teamId: z.string(),
|
|
7617
|
+
userId: z.string()
|
|
7594
7618
|
})
|
|
7595
7619
|
}, async (ctx) => {
|
|
7596
7620
|
const { organizationId } = ctx.context.payload;
|
|
@@ -7693,7 +7717,7 @@ const revokeSession = (options) => createAuthEndpoint("/dash/sessions/revoke", {
|
|
|
7693
7717
|
const revokeAllSessions = (options) => createAuthEndpoint("/dash/sessions/revoke-all", {
|
|
7694
7718
|
method: "POST",
|
|
7695
7719
|
use: [jwtMiddleware(options)],
|
|
7696
|
-
body: z
|
|
7720
|
+
body: z.object({ userId: z.string() })
|
|
7697
7721
|
}, async (ctx) => {
|
|
7698
7722
|
const { userId } = ctx.body;
|
|
7699
7723
|
if (!await ctx.context.internalAdapter.findUserById(userId)) throw ctx.error("NOT_FOUND", { message: "User not found" });
|
|
@@ -7702,7 +7726,7 @@ const revokeAllSessions = (options) => createAuthEndpoint("/dash/sessions/revoke
|
|
|
7702
7726
|
});
|
|
7703
7727
|
const revokeManySessions = (options) => createAuthEndpoint("/dash/sessions/revoke-many", {
|
|
7704
7728
|
method: "POST",
|
|
7705
|
-
use: [jwtMiddleware(options, z
|
|
7729
|
+
use: [jwtMiddleware(options, z.object({ userIds: z.string().array() }))]
|
|
7706
7730
|
}, async (ctx) => {
|
|
7707
7731
|
const { userIds } = ctx.context.payload;
|
|
7708
7732
|
await withConcurrency(chunkArray(userIds, { batchSize: 50 }), async (chunk) => {
|
|
@@ -7747,7 +7771,7 @@ function getSSOPlugin(ctx) {
|
|
|
7747
7771
|
* (see packages/sso dist constants). Inlined so consumers (e.g. Metro) never
|
|
7748
7772
|
* statically pull @better-auth/sso for validation-only code paths.
|
|
7749
7773
|
*/
|
|
7750
|
-
const DEFAULT_MAX_SAML_METADATA_SIZE =
|
|
7774
|
+
const DEFAULT_MAX_SAML_METADATA_SIZE = 102400;
|
|
7751
7775
|
const RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
|
|
7752
7776
|
const SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1";
|
|
7753
7777
|
/** @public */
|
|
@@ -7831,44 +7855,44 @@ function requireOrganizationAccess(ctx) {
|
|
|
7831
7855
|
const orgIdFromToken = ctx.context.payload?.organizationId;
|
|
7832
7856
|
if (!orgIdFromToken || orgIdFromUrl !== orgIdFromToken) throw ctx.error("FORBIDDEN", { message: "You do not have access to this organization" });
|
|
7833
7857
|
}
|
|
7834
|
-
const samlConfigSchema = z
|
|
7835
|
-
idpMetadata: z
|
|
7836
|
-
metadata: z
|
|
7837
|
-
metadataUrl: z
|
|
7858
|
+
const samlConfigSchema = z.object({
|
|
7859
|
+
idpMetadata: z.object({
|
|
7860
|
+
metadata: z.string().optional(),
|
|
7861
|
+
metadataUrl: z.string().optional()
|
|
7838
7862
|
}).optional(),
|
|
7839
|
-
entryPoint: z
|
|
7840
|
-
cert: z
|
|
7841
|
-
entityId: z
|
|
7842
|
-
mapping: z
|
|
7863
|
+
entryPoint: z.string().optional(),
|
|
7864
|
+
cert: z.string().optional(),
|
|
7865
|
+
entityId: z.string().optional(),
|
|
7866
|
+
mapping: z.object({
|
|
7843
7867
|
/** @deprecated Removed in better-auth 1.7+; SAML subject is NameID. */
|
|
7844
|
-
id: z
|
|
7845
|
-
email: z
|
|
7846
|
-
emailVerified: z
|
|
7847
|
-
name: z
|
|
7848
|
-
firstName: z
|
|
7849
|
-
lastName: z
|
|
7850
|
-
extraFields: z
|
|
7868
|
+
id: z.string().optional(),
|
|
7869
|
+
email: z.string().optional(),
|
|
7870
|
+
emailVerified: z.string().optional(),
|
|
7871
|
+
name: z.string().optional(),
|
|
7872
|
+
firstName: z.string().optional(),
|
|
7873
|
+
lastName: z.string().optional(),
|
|
7874
|
+
extraFields: z.record(z.string(), z.any()).optional()
|
|
7851
7875
|
}).optional()
|
|
7852
7876
|
});
|
|
7853
|
-
const oidcConfigSchema = z
|
|
7854
|
-
clientId: z
|
|
7855
|
-
clientSecret: z
|
|
7856
|
-
discoveryUrl: z
|
|
7857
|
-
issuer: z
|
|
7858
|
-
discoveryEndpoint: z
|
|
7859
|
-
authorizationEndpoint: z
|
|
7860
|
-
tokenEndpoint: z
|
|
7861
|
-
jwksEndpoint: z
|
|
7862
|
-
userInfoEndpoint: z
|
|
7863
|
-
tokenEndpointAuthentication: z
|
|
7864
|
-
mapping: z
|
|
7877
|
+
const oidcConfigSchema = z.object({
|
|
7878
|
+
clientId: z.string(),
|
|
7879
|
+
clientSecret: z.string().optional(),
|
|
7880
|
+
discoveryUrl: z.string().optional(),
|
|
7881
|
+
issuer: z.string().optional(),
|
|
7882
|
+
discoveryEndpoint: z.string().optional(),
|
|
7883
|
+
authorizationEndpoint: z.string().optional(),
|
|
7884
|
+
tokenEndpoint: z.string().optional(),
|
|
7885
|
+
jwksEndpoint: z.string().optional(),
|
|
7886
|
+
userInfoEndpoint: z.string().optional(),
|
|
7887
|
+
tokenEndpointAuthentication: z.enum(["client_secret_post", "client_secret_basic"]).optional(),
|
|
7888
|
+
mapping: z.object({
|
|
7865
7889
|
/** @deprecated Removed in better-auth 1.7+; OIDC subject is `sub`. */
|
|
7866
|
-
id: z
|
|
7867
|
-
email: z
|
|
7868
|
-
emailVerified: z
|
|
7869
|
-
name: z
|
|
7870
|
-
image: z
|
|
7871
|
-
extraFields: z
|
|
7890
|
+
id: z.string().optional(),
|
|
7891
|
+
email: z.string().optional(),
|
|
7892
|
+
emailVerified: z.string().optional(),
|
|
7893
|
+
name: z.string().optional(),
|
|
7894
|
+
image: z.string().optional(),
|
|
7895
|
+
extraFields: z.record(z.string(), z.any()).optional()
|
|
7872
7896
|
}).optional()
|
|
7873
7897
|
});
|
|
7874
7898
|
async function resolveSAMLConfig(samlConfig, providerId, baseURL, ctx) {
|
|
@@ -7961,7 +7985,7 @@ async function resolveOIDCConfig(oidcConfig, _domain, ctx) {
|
|
|
7961
7985
|
const listOrganizationSsoProviders = (options) => {
|
|
7962
7986
|
return createAuthEndpoint("/dash/organization/:id/sso-providers", {
|
|
7963
7987
|
method: "GET",
|
|
7964
|
-
use: [jwtMiddleware(options, z
|
|
7988
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))]
|
|
7965
7989
|
}, async (ctx) => {
|
|
7966
7990
|
if (!isOrganizationEnabled(ctx)) {
|
|
7967
7991
|
ctx.context.logger.warn("[Dash] Organization plugin not enabled, returning empty SSO providers list");
|
|
@@ -7990,12 +8014,12 @@ const listOrganizationSsoProviders = (options) => {
|
|
|
7990
8014
|
const createSsoProvider = (options) => {
|
|
7991
8015
|
return createAuthEndpoint("/dash/organization/:id/sso-provider/create", {
|
|
7992
8016
|
method: "POST",
|
|
7993
|
-
use: [jwtMiddleware(options, z
|
|
7994
|
-
body: z
|
|
7995
|
-
providerId: z
|
|
7996
|
-
domain: z
|
|
7997
|
-
protocol: z
|
|
7998
|
-
userId: z
|
|
8017
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
8018
|
+
body: z.object({
|
|
8019
|
+
providerId: z.string(),
|
|
8020
|
+
domain: z.string(),
|
|
8021
|
+
protocol: z.enum(["SAML", "OIDC"]),
|
|
8022
|
+
userId: z.string(),
|
|
7999
8023
|
samlConfig: samlConfigSchema.optional(),
|
|
8000
8024
|
oidcConfig: oidcConfigSchema.optional()
|
|
8001
8025
|
})
|
|
@@ -8057,11 +8081,11 @@ const createSsoProvider = (options) => {
|
|
|
8057
8081
|
const updateSsoProvider = (options) => {
|
|
8058
8082
|
return createAuthEndpoint("/dash/organization/:id/sso-provider/update", {
|
|
8059
8083
|
method: "POST",
|
|
8060
|
-
use: [jwtMiddleware(options, z
|
|
8061
|
-
body: z
|
|
8062
|
-
providerId: z
|
|
8063
|
-
domain: z
|
|
8064
|
-
protocol: z
|
|
8084
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
8085
|
+
body: z.object({
|
|
8086
|
+
providerId: z.string(),
|
|
8087
|
+
domain: z.string(),
|
|
8088
|
+
protocol: z.enum(["SAML", "OIDC"]),
|
|
8065
8089
|
samlConfig: samlConfigSchema.optional(),
|
|
8066
8090
|
oidcConfig: oidcConfigSchema.optional()
|
|
8067
8091
|
})
|
|
@@ -8140,8 +8164,8 @@ const updateSsoProvider = (options) => {
|
|
|
8140
8164
|
const requestSsoVerificationToken = (options) => {
|
|
8141
8165
|
return createAuthEndpoint("/dash/organization/:id/sso-provider/request-verification-token", {
|
|
8142
8166
|
method: "POST",
|
|
8143
|
-
use: [jwtMiddleware(options, z
|
|
8144
|
-
body: z
|
|
8167
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
8168
|
+
body: z.object({ providerId: z.string() })
|
|
8145
8169
|
}, async (ctx) => {
|
|
8146
8170
|
requireOrganizationPlugin(ctx);
|
|
8147
8171
|
requireOrganizationAccess(ctx);
|
|
@@ -8188,8 +8212,8 @@ const requestSsoVerificationToken = (options) => {
|
|
|
8188
8212
|
const verifySsoProviderDomain = (options) => {
|
|
8189
8213
|
return createAuthEndpoint("/dash/organization/:id/sso-provider/verify-domain", {
|
|
8190
8214
|
method: "POST",
|
|
8191
|
-
use: [jwtMiddleware(options, z
|
|
8192
|
-
body: z
|
|
8215
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
8216
|
+
body: z.object({ providerId: z.string() })
|
|
8193
8217
|
}, async (ctx) => {
|
|
8194
8218
|
requireOrganizationPlugin(ctx);
|
|
8195
8219
|
requireOrganizationAccess(ctx);
|
|
@@ -8235,8 +8259,8 @@ const verifySsoProviderDomain = (options) => {
|
|
|
8235
8259
|
const deleteSsoProvider = (options) => {
|
|
8236
8260
|
return createAuthEndpoint("/dash/organization/:id/sso-provider/delete", {
|
|
8237
8261
|
method: "POST",
|
|
8238
|
-
use: [jwtMiddleware(options, z
|
|
8239
|
-
body: z
|
|
8262
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
8263
|
+
body: z.object({ providerId: z.string() })
|
|
8240
8264
|
}, async (ctx) => {
|
|
8241
8265
|
requireOrganizationPlugin(ctx);
|
|
8242
8266
|
requireOrganizationAccess(ctx);
|
|
@@ -8278,10 +8302,10 @@ const deleteSsoProvider = (options) => {
|
|
|
8278
8302
|
const markSsoProviderDomainVerified = (options) => {
|
|
8279
8303
|
return createAuthEndpoint("/dash/organization/:id/sso-provider/mark-domain-verified", {
|
|
8280
8304
|
method: "POST",
|
|
8281
|
-
use: [jwtMiddleware(options, z
|
|
8282
|
-
body: z
|
|
8283
|
-
providerId: z
|
|
8284
|
-
verified: z
|
|
8305
|
+
use: [jwtMiddleware(options, z.object({ organizationId: z.string() }))],
|
|
8306
|
+
body: z.object({
|
|
8307
|
+
providerId: z.string(),
|
|
8308
|
+
verified: z.literal(false).describe("Clears domain verification. Domains can only be marked verified via DNS (verify-domain).")
|
|
8285
8309
|
})
|
|
8286
8310
|
}, async (ctx) => {
|
|
8287
8311
|
requireOrganizationPlugin(ctx);
|
|
@@ -8377,7 +8401,7 @@ function buildTotpUri(params) {
|
|
|
8377
8401
|
//#region src/routes/two-factor/index.ts
|
|
8378
8402
|
const enableTwoFactor = (options) => createAuthEndpoint("/dash/enable-two-factor", {
|
|
8379
8403
|
method: "POST",
|
|
8380
|
-
use: [jwtMiddleware(options, z
|
|
8404
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))]
|
|
8381
8405
|
}, async (ctx) => {
|
|
8382
8406
|
const { userId } = ctx.context.payload;
|
|
8383
8407
|
const twoFactorPlugin = ctx.context.getPlugin("two-factor");
|
|
@@ -8438,7 +8462,7 @@ const enableTwoFactor = (options) => createAuthEndpoint("/dash/enable-two-factor
|
|
|
8438
8462
|
});
|
|
8439
8463
|
const completeTwoFactorSetup = (options) => createAuthEndpoint("/dash/complete-two-factor-setup", {
|
|
8440
8464
|
method: "POST",
|
|
8441
|
-
use: [jwtMiddleware(options, z
|
|
8465
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))]
|
|
8442
8466
|
}, async (ctx) => {
|
|
8443
8467
|
const { userId } = ctx.context.payload;
|
|
8444
8468
|
const twoFactorPlugin = ctx.context.getPlugin("two-factor");
|
|
@@ -8466,7 +8490,7 @@ const completeTwoFactorSetup = (options) => createAuthEndpoint("/dash/complete-t
|
|
|
8466
8490
|
const viewTwoFactorTotpUri = (options) => createAuthEndpoint("/dash/view-two-factor-totp-uri", {
|
|
8467
8491
|
method: "POST",
|
|
8468
8492
|
metadata: { scope: "http" },
|
|
8469
|
-
use: [jwtMiddleware(options, z
|
|
8493
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))]
|
|
8470
8494
|
}, async (ctx) => {
|
|
8471
8495
|
const { userId } = ctx.context.payload;
|
|
8472
8496
|
const twoFactorPlugin = ctx.context.getPlugin("two-factor");
|
|
@@ -8504,13 +8528,13 @@ const viewTwoFactorTotpUri = (options) => createAuthEndpoint("/dash/view-two-fac
|
|
|
8504
8528
|
});
|
|
8505
8529
|
const viewBackupCodes = (options) => createAuthEndpoint("/dash/view-backup-codes", {
|
|
8506
8530
|
method: "POST",
|
|
8507
|
-
use: [jwtMiddleware(options, z
|
|
8531
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))]
|
|
8508
8532
|
}, async () => {
|
|
8509
8533
|
throw new APIError("FORBIDDEN", { message: "Backup codes cannot be viewed after initial setup. Generate new codes instead." });
|
|
8510
8534
|
});
|
|
8511
8535
|
const disableTwoFactor = (options) => createAuthEndpoint("/dash/disable-two-factor", {
|
|
8512
8536
|
method: "POST",
|
|
8513
|
-
use: [jwtMiddleware(options, z
|
|
8537
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))]
|
|
8514
8538
|
}, async (ctx) => {
|
|
8515
8539
|
const { userId } = ctx.context.payload;
|
|
8516
8540
|
const twoFactorPlugin = ctx.context.getPlugin("two-factor");
|
|
@@ -8531,7 +8555,7 @@ const disableTwoFactor = (options) => createAuthEndpoint("/dash/disable-two-fact
|
|
|
8531
8555
|
});
|
|
8532
8556
|
const generateBackupCodes = (options) => createAuthEndpoint("/dash/generate-backup-codes", {
|
|
8533
8557
|
method: "POST",
|
|
8534
|
-
use: [jwtMiddleware(options, z
|
|
8558
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))]
|
|
8535
8559
|
}, async (ctx) => {
|
|
8536
8560
|
const { userId } = ctx.context.payload;
|
|
8537
8561
|
const twoFactorPlugin = ctx.context.getPlugin("two-factor");
|
|
@@ -8596,13 +8620,13 @@ async function resolveTwoFactorStatus(context, userId, user) {
|
|
|
8596
8620
|
}
|
|
8597
8621
|
//#endregion
|
|
8598
8622
|
//#region src/routes/users/schemas.ts
|
|
8599
|
-
const DENIED_USER_WRITE_KEYS = new Set([
|
|
8623
|
+
const DENIED_USER_WRITE_KEYS = /* @__PURE__ */ new Set([
|
|
8600
8624
|
"id",
|
|
8601
8625
|
"createdAt",
|
|
8602
8626
|
"updatedAt"
|
|
8603
8627
|
]);
|
|
8604
8628
|
/** Fields that must be changed through dedicated permission-checked endpoints. */
|
|
8605
|
-
const DENIED_SENSITIVE_USER_KEYS = new Set([
|
|
8629
|
+
const DENIED_SENSITIVE_USER_KEYS = /* @__PURE__ */ new Set([
|
|
8606
8630
|
"banned",
|
|
8607
8631
|
"banReason",
|
|
8608
8632
|
"banExpires",
|
|
@@ -8611,7 +8635,7 @@ const DENIED_SENSITIVE_USER_KEYS = new Set([
|
|
|
8611
8635
|
"twoFactorEnabled",
|
|
8612
8636
|
"twoFactorSecret"
|
|
8613
8637
|
]);
|
|
8614
|
-
const CREATE_REQUEST_ONLY_KEYS = new Set([
|
|
8638
|
+
const CREATE_REQUEST_ONLY_KEYS = /* @__PURE__ */ new Set([
|
|
8615
8639
|
"password",
|
|
8616
8640
|
"generatePassword",
|
|
8617
8641
|
"sendVerificationEmail",
|
|
@@ -8667,7 +8691,7 @@ function getWritableUserFieldNames(options, mode) {
|
|
|
8667
8691
|
*/
|
|
8668
8692
|
function pickWritableUserFields(body, options, mode) {
|
|
8669
8693
|
const allowed = getWritableUserFieldNames(options, mode);
|
|
8670
|
-
const skip = new Set([
|
|
8694
|
+
const skip = /* @__PURE__ */ new Set([
|
|
8671
8695
|
...DENIED_USER_WRITE_KEYS,
|
|
8672
8696
|
...DENIED_SENSITIVE_USER_KEYS,
|
|
8673
8697
|
...mode === "create" ? CREATE_REQUEST_ONLY_KEYS : []
|
|
@@ -8679,32 +8703,32 @@ function pickWritableUserFields(body, options, mode) {
|
|
|
8679
8703
|
}
|
|
8680
8704
|
return result;
|
|
8681
8705
|
}
|
|
8682
|
-
const BaseCreateUserCoreBodySchema = z
|
|
8683
|
-
name: z
|
|
8684
|
-
email: z
|
|
8685
|
-
image: z
|
|
8686
|
-
password: z
|
|
8687
|
-
generatePassword: z
|
|
8688
|
-
emailVerified: z
|
|
8689
|
-
sendVerificationEmail: z
|
|
8690
|
-
sendOrganizationInvite: z
|
|
8691
|
-
organizationRole: z
|
|
8692
|
-
organizationId: z
|
|
8706
|
+
const BaseCreateUserCoreBodySchema = z.object({
|
|
8707
|
+
name: z.string(),
|
|
8708
|
+
email: z.email(),
|
|
8709
|
+
image: z.string().optional(),
|
|
8710
|
+
password: z.string().optional(),
|
|
8711
|
+
generatePassword: z.boolean().optional(),
|
|
8712
|
+
emailVerified: z.boolean().optional(),
|
|
8713
|
+
sendVerificationEmail: z.boolean().optional(),
|
|
8714
|
+
sendOrganizationInvite: z.boolean().optional(),
|
|
8715
|
+
organizationRole: z.string().optional(),
|
|
8716
|
+
organizationId: z.string().optional()
|
|
8693
8717
|
});
|
|
8694
|
-
const BaseUpdateUserCoreBodySchema = z
|
|
8695
|
-
name: z
|
|
8696
|
-
email: z
|
|
8697
|
-
image: z
|
|
8698
|
-
emailVerified: z
|
|
8718
|
+
const BaseUpdateUserCoreBodySchema = z.object({
|
|
8719
|
+
name: z.string().nullable().optional(),
|
|
8720
|
+
email: z.email().optional(),
|
|
8721
|
+
image: z.string().nullable().optional(),
|
|
8722
|
+
emailVerified: z.boolean().optional()
|
|
8699
8723
|
});
|
|
8700
|
-
const CreateUserBodySchema = BaseCreateUserCoreBodySchema.catchall(z
|
|
8701
|
-
const UpdateUserBodySchema = BaseUpdateUserCoreBodySchema.catchall(z
|
|
8724
|
+
const CreateUserBodySchema = BaseCreateUserCoreBodySchema.catchall(z.unknown());
|
|
8725
|
+
const UpdateUserBodySchema = BaseUpdateUserCoreBodySchema.catchall(z.unknown());
|
|
8702
8726
|
function createSchemaForDBField(field) {
|
|
8703
8727
|
switch (field.type) {
|
|
8704
|
-
case "number": return field.required ? z
|
|
8705
|
-
case "boolean": return field.required ? z
|
|
8706
|
-
case "date": return field.required ? z
|
|
8707
|
-
default: return field.required ? z
|
|
8728
|
+
case "number": return field.required ? z.coerce.number() : z.coerce.number().optional();
|
|
8729
|
+
case "boolean": return field.required ? z.coerce.boolean() : z.coerce.boolean().optional();
|
|
8730
|
+
case "date": return field.required ? z.union([z.string().min(1), z.coerce.date()]) : z.union([z.string(), z.coerce.date()]).optional();
|
|
8731
|
+
default: return field.required ? z.string().min(1) : z.string().optional();
|
|
8708
8732
|
}
|
|
8709
8733
|
}
|
|
8710
8734
|
/**
|
|
@@ -8712,38 +8736,38 @@ function createSchemaForDBField(field) {
|
|
|
8712
8736
|
*/
|
|
8713
8737
|
function validateWritableCreateUserFields(data, options) {
|
|
8714
8738
|
const shape = {
|
|
8715
|
-
name: z
|
|
8716
|
-
email: z
|
|
8717
|
-
image: z
|
|
8718
|
-
emailVerified: z
|
|
8739
|
+
name: z.string().min(1),
|
|
8740
|
+
email: z.email(),
|
|
8741
|
+
image: z.string().optional(),
|
|
8742
|
+
emailVerified: z.boolean().optional()
|
|
8719
8743
|
};
|
|
8720
8744
|
for (const [name, field] of Object.entries(getUserInputFields(options))) {
|
|
8721
8745
|
if (!isWritableUserInputField(field)) continue;
|
|
8722
8746
|
shape[name] = createSchemaForDBField(field);
|
|
8723
8747
|
}
|
|
8724
|
-
const result = z
|
|
8748
|
+
const result = z.object(shape).strict().safeParse(data);
|
|
8725
8749
|
if (!result.success) throw result.error;
|
|
8726
8750
|
}
|
|
8727
8751
|
/**
|
|
8728
8752
|
* Ensures at least one field is present on update and validates additional field types.
|
|
8729
8753
|
*/
|
|
8730
8754
|
function validateWritableUserUpdateFields(data, options) {
|
|
8731
|
-
if (Object.keys(data).length === 0) throw new z
|
|
8755
|
+
if (Object.keys(data).length === 0) throw new z.ZodError([{
|
|
8732
8756
|
code: "custom",
|
|
8733
8757
|
message: "No valid fields to update",
|
|
8734
8758
|
path: []
|
|
8735
8759
|
}]);
|
|
8736
8760
|
const shape = {
|
|
8737
|
-
name: z
|
|
8738
|
-
email: z
|
|
8739
|
-
image: z
|
|
8740
|
-
emailVerified: z
|
|
8761
|
+
name: z.string().nullable().optional(),
|
|
8762
|
+
email: z.email().optional(),
|
|
8763
|
+
image: z.string().nullable().optional(),
|
|
8764
|
+
emailVerified: z.boolean().optional()
|
|
8741
8765
|
};
|
|
8742
8766
|
for (const [name, field] of Object.entries(getUserInputFields(options))) {
|
|
8743
8767
|
if (!isWritableUserInputField(field)) continue;
|
|
8744
8768
|
shape[name] = createSchemaForDBField(field);
|
|
8745
8769
|
}
|
|
8746
|
-
const result = z
|
|
8770
|
+
const result = z.object(shape).partial().strict().safeParse(data);
|
|
8747
8771
|
if (!result.success) throw result.error;
|
|
8748
8772
|
}
|
|
8749
8773
|
//#endregion
|
|
@@ -8766,13 +8790,13 @@ function clampUserListOffset(value, fallback = 0) {
|
|
|
8766
8790
|
if (!Number.isFinite(n)) return fallback;
|
|
8767
8791
|
return Math.max(0, Math.floor(n));
|
|
8768
8792
|
}
|
|
8769
|
-
const getUsersQuerySchema = z
|
|
8770
|
-
limit: z
|
|
8771
|
-
offset: z
|
|
8772
|
-
sortBy: z
|
|
8773
|
-
sortOrder: z
|
|
8774
|
-
where: z
|
|
8775
|
-
countWhere: z
|
|
8793
|
+
const getUsersQuerySchema = z.object({
|
|
8794
|
+
limit: z.number().or(z.string().transform(Number)).optional(),
|
|
8795
|
+
offset: z.number().or(z.string().transform(Number)).optional(),
|
|
8796
|
+
sortBy: z.string().optional(),
|
|
8797
|
+
sortOrder: z.enum(["asc", "desc"]).optional(),
|
|
8798
|
+
where: z.string().transform(parseWhereClause).optional(),
|
|
8799
|
+
countWhere: z.string().transform(parseWhereClause).optional()
|
|
8776
8800
|
}).optional();
|
|
8777
8801
|
const getUsers = (options) => {
|
|
8778
8802
|
return createAuthEndpoint("/dash/list-users", {
|
|
@@ -8809,7 +8833,7 @@ const getUsers = (options) => {
|
|
|
8809
8833
|
model: "user",
|
|
8810
8834
|
where: [{
|
|
8811
8835
|
field: "lastActiveAt",
|
|
8812
|
-
value: /* @__PURE__ */ new Date(Date.now() -
|
|
8836
|
+
value: /* @__PURE__ */ new Date(Date.now() - 12e4),
|
|
8813
8837
|
operator: "gte"
|
|
8814
8838
|
}]
|
|
8815
8839
|
}).catch((e) => {
|
|
@@ -8867,7 +8891,7 @@ const exportUsers = (options) => {
|
|
|
8867
8891
|
const deleteUser = (options) => {
|
|
8868
8892
|
return createAuthEndpoint("/dash/delete-user", {
|
|
8869
8893
|
method: "POST",
|
|
8870
|
-
use: [jwtMiddleware(options, z
|
|
8894
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))]
|
|
8871
8895
|
}, async (ctx) => {
|
|
8872
8896
|
try {
|
|
8873
8897
|
await ctx.context.adapter.delete({
|
|
@@ -8886,7 +8910,7 @@ const deleteUser = (options) => {
|
|
|
8886
8910
|
const deleteManyUsers = (options) => {
|
|
8887
8911
|
return createAuthEndpoint("/dash/delete-many-users", {
|
|
8888
8912
|
method: "POST",
|
|
8889
|
-
use: [jwtMiddleware(options, z
|
|
8913
|
+
use: [jwtMiddleware(options, z.object({ userIds: z.string().array() }))]
|
|
8890
8914
|
}, async (ctx) => {
|
|
8891
8915
|
const { userIds } = ctx.context.payload;
|
|
8892
8916
|
const deletedUserIds = /* @__PURE__ */ new Set();
|
|
@@ -8922,11 +8946,11 @@ const deleteManyUsers = (options) => {
|
|
|
8922
8946
|
const impersonateUser = (options) => {
|
|
8923
8947
|
return createAuthEndpoint("/dash/impersonate-user", {
|
|
8924
8948
|
method: "GET",
|
|
8925
|
-
query: z
|
|
8926
|
-
use: [jwtMiddleware(options, z
|
|
8927
|
-
userId: z
|
|
8949
|
+
query: z.object({ impersonation_token: z.string() }),
|
|
8950
|
+
use: [jwtMiddleware(options, z.object({
|
|
8951
|
+
userId: z.string(),
|
|
8928
8952
|
redirectUrl: safeUrlSchema,
|
|
8929
|
-
impersonatedBy: z
|
|
8953
|
+
impersonatedBy: z.string().optional()
|
|
8930
8954
|
}), async (ctx) => {
|
|
8931
8955
|
return ctx.query.impersonation_token;
|
|
8932
8956
|
})]
|
|
@@ -8936,7 +8960,7 @@ const impersonateUser = (options) => {
|
|
|
8936
8960
|
const trustedRedirectUrl = parseTrustedAuthRedirectUrl(ctx, redirectUrl);
|
|
8937
8961
|
if (!trustedRedirectUrl) throw ctx.error("BAD_REQUEST", { message: "Invalid redirect URL" });
|
|
8938
8962
|
const session = await ctx.context.internalAdapter.createSession(userId, true, {
|
|
8939
|
-
expiresAt: new Date(Date.now() +
|
|
8963
|
+
expiresAt: new Date(Date.now() + 6e5),
|
|
8940
8964
|
impersonatedBy: impersonatedBy || void 0
|
|
8941
8965
|
});
|
|
8942
8966
|
const user = await ctx.context.internalAdapter.findUserById(userId);
|
|
@@ -8951,9 +8975,9 @@ const impersonateUser = (options) => {
|
|
|
8951
8975
|
const createUser = (options) => {
|
|
8952
8976
|
return createAuthEndpoint("/dash/create-user", {
|
|
8953
8977
|
method: "POST",
|
|
8954
|
-
use: [jwtMiddleware(options, z
|
|
8955
|
-
organizationId: z
|
|
8956
|
-
organizationRole: z
|
|
8978
|
+
use: [jwtMiddleware(options, z.object({
|
|
8979
|
+
organizationId: z.string().optional(),
|
|
8980
|
+
organizationRole: z.string().optional()
|
|
8957
8981
|
}))],
|
|
8958
8982
|
body: CreateUserBodySchema
|
|
8959
8983
|
}, async (ctx) => {
|
|
@@ -8962,7 +8986,7 @@ const createUser = (options) => {
|
|
|
8962
8986
|
try {
|
|
8963
8987
|
validateWritableCreateUserFields(userData, ctx.context.options);
|
|
8964
8988
|
} catch (error) {
|
|
8965
|
-
if (error instanceof z
|
|
8989
|
+
if (error instanceof z.ZodError) throw new APIError("BAD_REQUEST", { message: error.issues[0]?.message ?? "Invalid user data" });
|
|
8966
8990
|
throw error;
|
|
8967
8991
|
}
|
|
8968
8992
|
const email = normalizeEmail(userData.email, ctx.context);
|
|
@@ -9051,8 +9075,8 @@ const createUser = (options) => {
|
|
|
9051
9075
|
const setPassword = (options) => {
|
|
9052
9076
|
return createAuthEndpoint("/dash/set-password", {
|
|
9053
9077
|
method: "POST",
|
|
9054
|
-
use: [jwtMiddleware(options, z
|
|
9055
|
-
body: z
|
|
9078
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))],
|
|
9079
|
+
body: z.object({ password: z.string().min(8) })
|
|
9056
9080
|
}, async (ctx) => {
|
|
9057
9081
|
const { userId } = ctx.context.payload;
|
|
9058
9082
|
const { password } = ctx.body;
|
|
@@ -9073,10 +9097,10 @@ const setPassword = (options) => {
|
|
|
9073
9097
|
const unlinkAccount = (options) => {
|
|
9074
9098
|
return createAuthEndpoint("/dash/unlink-account", {
|
|
9075
9099
|
method: "POST",
|
|
9076
|
-
use: [jwtMiddleware(options, z
|
|
9077
|
-
body: z
|
|
9078
|
-
providerId: z
|
|
9079
|
-
accountId: z
|
|
9100
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))],
|
|
9101
|
+
body: z.object({
|
|
9102
|
+
providerId: z.string(),
|
|
9103
|
+
accountId: z.string()
|
|
9080
9104
|
})
|
|
9081
9105
|
}, async (ctx) => {
|
|
9082
9106
|
const { userId } = ctx.context.payload;
|
|
@@ -9094,16 +9118,16 @@ const unlinkAccount = (options) => {
|
|
|
9094
9118
|
return { success: true };
|
|
9095
9119
|
});
|
|
9096
9120
|
};
|
|
9097
|
-
const getUserDetailsJwtSchema = z
|
|
9098
|
-
userId: z
|
|
9099
|
-
sessionOnly: z
|
|
9100
|
-
accountOnly: z
|
|
9121
|
+
const getUserDetailsJwtSchema = z.object({
|
|
9122
|
+
userId: z.string(),
|
|
9123
|
+
sessionOnly: z.boolean().optional(),
|
|
9124
|
+
accountOnly: z.boolean().optional()
|
|
9101
9125
|
});
|
|
9102
9126
|
const getUserDetails = (options) => {
|
|
9103
9127
|
return createAuthEndpoint("/dash/user", {
|
|
9104
9128
|
method: "GET",
|
|
9105
9129
|
use: [jwtMiddleware(options, getUserDetailsJwtSchema)],
|
|
9106
|
-
query: z
|
|
9130
|
+
query: z.object({ minimal: z.boolean().or(z.string().transform((val) => val === "true")).optional() }).optional()
|
|
9107
9131
|
}, async (ctx) => {
|
|
9108
9132
|
const { userId, sessionOnly, accountOnly } = ctx.context.payload;
|
|
9109
9133
|
const minimal = !!ctx.query?.minimal;
|
|
@@ -9200,7 +9224,7 @@ const getUserDetails = (options) => {
|
|
|
9200
9224
|
const getUserOrganizations = (options) => {
|
|
9201
9225
|
return createAuthEndpoint("/dash/user-organizations", {
|
|
9202
9226
|
method: "GET",
|
|
9203
|
-
use: [jwtMiddleware(options, z
|
|
9227
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))]
|
|
9204
9228
|
}, async (ctx) => {
|
|
9205
9229
|
const { userId } = ctx.context.payload;
|
|
9206
9230
|
if (!isOrganizationEnabled(ctx)) {
|
|
@@ -9245,7 +9269,7 @@ const getUserOrganizations = (options) => {
|
|
|
9245
9269
|
};
|
|
9246
9270
|
const updateUser = (options) => createAuthEndpoint("/dash/update-user", {
|
|
9247
9271
|
method: "POST",
|
|
9248
|
-
use: [jwtMiddleware(options, z
|
|
9272
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))],
|
|
9249
9273
|
body: UpdateUserBodySchema
|
|
9250
9274
|
}, async (ctx) => {
|
|
9251
9275
|
const userId = ctx.context.payload?.userId;
|
|
@@ -9254,7 +9278,7 @@ const updateUser = (options) => createAuthEndpoint("/dash/update-user", {
|
|
|
9254
9278
|
try {
|
|
9255
9279
|
validateWritableUserUpdateFields(updateData, ctx.context.options);
|
|
9256
9280
|
} catch (error) {
|
|
9257
|
-
if (error instanceof z
|
|
9281
|
+
if (error instanceof z.ZodError) throw new APIError("BAD_REQUEST", { message: error.issues[0]?.message ?? "Invalid user data" });
|
|
9258
9282
|
throw error;
|
|
9259
9283
|
}
|
|
9260
9284
|
const user = await ctx.context.internalAdapter.updateUser(userId, {
|
|
@@ -9340,12 +9364,12 @@ const getUserStats = (options) => createAuthEndpoint("/dash/user-stats", {
|
|
|
9340
9364
|
use: [jwtMiddleware(options)]
|
|
9341
9365
|
}, async (ctx) => {
|
|
9342
9366
|
const now = /* @__PURE__ */ new Date();
|
|
9343
|
-
const oneDayAgo = /* @__PURE__ */ new Date(now.getTime() -
|
|
9344
|
-
const twoDaysAgo = /* @__PURE__ */ new Date(now.getTime() -
|
|
9345
|
-
const oneWeekAgo = /* @__PURE__ */ new Date(now.getTime() -
|
|
9346
|
-
const twoWeeksAgo = /* @__PURE__ */ new Date(now.getTime() -
|
|
9347
|
-
const oneMonthAgo = /* @__PURE__ */ new Date(now.getTime() -
|
|
9348
|
-
const twoMonthsAgo = /* @__PURE__ */ new Date(now.getTime() -
|
|
9367
|
+
const oneDayAgo = /* @__PURE__ */ new Date(now.getTime() - 864e5);
|
|
9368
|
+
const twoDaysAgo = /* @__PURE__ */ new Date(now.getTime() - 1728e5);
|
|
9369
|
+
const oneWeekAgo = /* @__PURE__ */ new Date(now.getTime() - 6048e5);
|
|
9370
|
+
const twoWeeksAgo = /* @__PURE__ */ new Date(now.getTime() - 12096e5);
|
|
9371
|
+
const oneMonthAgo = /* @__PURE__ */ new Date(now.getTime() - 2592e6);
|
|
9372
|
+
const twoMonthsAgo = /* @__PURE__ */ new Date(now.getTime() - 5184e6);
|
|
9349
9373
|
const activityTrackingEnabled = !!options.activityTracking?.enabled;
|
|
9350
9374
|
const storeInSecondaryStorageOnly = isSessionInSecondaryStorageOnly(ctx.context);
|
|
9351
9375
|
const [rDailySignups, rPrevDaySignups, rWeeklySignups, rPrevWeekSignups, rMonthlySignups, rPrevMonthSignups, rTotalUsers, rActiveDaily, rActivePrevDay, rActiveWeekly, rActivePrevWeek, rActiveMonthly, rActivePrevMonth] = await withConcurrency([
|
|
@@ -9480,7 +9504,7 @@ const getUserStats = (options) => createAuthEndpoint("/dash/user-stats", {
|
|
|
9480
9504
|
const getUserGraphData = (options) => createAuthEndpoint("/dash/user-graph-data", {
|
|
9481
9505
|
method: "GET",
|
|
9482
9506
|
use: [jwtMiddleware(options)],
|
|
9483
|
-
query: z
|
|
9507
|
+
query: z.object({ period: z.enum([
|
|
9484
9508
|
"daily",
|
|
9485
9509
|
"weekly",
|
|
9486
9510
|
"monthly"
|
|
@@ -9491,7 +9515,7 @@ const getUserGraphData = (options) => createAuthEndpoint("/dash/user-graph-data"
|
|
|
9491
9515
|
const activityTrackingEnabled = !!options.activityTracking?.enabled;
|
|
9492
9516
|
const storeInSecondaryStorageOnly = isSessionInSecondaryStorageOnly(ctx.context);
|
|
9493
9517
|
const intervals = period === "daily" ? 7 : period === "weekly" ? 8 : 6;
|
|
9494
|
-
const msPerInterval = period === "daily" ?
|
|
9518
|
+
const msPerInterval = period === "daily" ? 864e5 : period === "weekly" ? 6048e5 : 2592e6;
|
|
9495
9519
|
const intervalData = [];
|
|
9496
9520
|
for (let i = intervals - 1; i >= 0; i--) {
|
|
9497
9521
|
const endDate = new Date(now.getTime() - i * msPerInterval);
|
|
@@ -9552,7 +9576,7 @@ const getUserGraphData = (options) => createAuthEndpoint("/dash/user-graph-data"
|
|
|
9552
9576
|
const getUserRetentionData = (options) => createAuthEndpoint("/dash/user-retention-data", {
|
|
9553
9577
|
method: "GET",
|
|
9554
9578
|
use: [jwtMiddleware(options)],
|
|
9555
|
-
query: z
|
|
9579
|
+
query: z.object({ period: z.enum([
|
|
9556
9580
|
"daily",
|
|
9557
9581
|
"weekly",
|
|
9558
9582
|
"monthly"
|
|
@@ -9724,11 +9748,11 @@ const getUserRetentionData = (options) => createAuthEndpoint("/dash/user-retenti
|
|
|
9724
9748
|
});
|
|
9725
9749
|
const banUser = (options) => createAuthEndpoint("/dash/ban-user", {
|
|
9726
9750
|
method: "POST",
|
|
9727
|
-
use: [jwtMiddleware(options, z
|
|
9728
|
-
body: z
|
|
9729
|
-
banReason: z
|
|
9730
|
-
banExpires: z
|
|
9731
|
-
deleteAllSessions: z
|
|
9751
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))],
|
|
9752
|
+
body: z.object({
|
|
9753
|
+
banReason: z.string().optional(),
|
|
9754
|
+
banExpires: z.number().optional(),
|
|
9755
|
+
deleteAllSessions: z.boolean().optional().default(true)
|
|
9732
9756
|
})
|
|
9733
9757
|
}, async (ctx) => {
|
|
9734
9758
|
const { userId } = ctx.context.payload;
|
|
@@ -9746,11 +9770,11 @@ const banUser = (options) => createAuthEndpoint("/dash/ban-user", {
|
|
|
9746
9770
|
const banManyUsers = (options) => {
|
|
9747
9771
|
return createAuthEndpoint("/dash/ban-many-users", {
|
|
9748
9772
|
method: "POST",
|
|
9749
|
-
use: [jwtMiddleware(options, z
|
|
9750
|
-
body: z
|
|
9751
|
-
banReason: z
|
|
9752
|
-
banExpires: z
|
|
9753
|
-
deleteAllSessions: z
|
|
9773
|
+
use: [jwtMiddleware(options, z.object({ userIds: z.string().array() }))],
|
|
9774
|
+
body: z.object({
|
|
9775
|
+
banReason: z.string().optional(),
|
|
9776
|
+
banExpires: z.number().optional(),
|
|
9777
|
+
deleteAllSessions: z.boolean().optional().default(true)
|
|
9754
9778
|
})
|
|
9755
9779
|
}, async (ctx) => {
|
|
9756
9780
|
const { userIds } = ctx.context.payload;
|
|
@@ -9800,7 +9824,7 @@ const banManyUsers = (options) => {
|
|
|
9800
9824
|
};
|
|
9801
9825
|
const unbanUser = (options) => createAuthEndpoint("/dash/unban-user", {
|
|
9802
9826
|
method: "POST",
|
|
9803
|
-
use: [jwtMiddleware(options, z
|
|
9827
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))]
|
|
9804
9828
|
}, async (ctx) => {
|
|
9805
9829
|
const { userId } = ctx.context.payload;
|
|
9806
9830
|
if (!await ctx.context.internalAdapter.findUserById(userId)) throw new APIError("NOT_FOUND", { message: "User not found" });
|
|
@@ -9814,8 +9838,8 @@ const unbanUser = (options) => createAuthEndpoint("/dash/unban-user", {
|
|
|
9814
9838
|
});
|
|
9815
9839
|
const sendVerificationEmail = (options) => createAuthEndpoint("/dash/send-verification-email", {
|
|
9816
9840
|
method: "POST",
|
|
9817
|
-
use: [jwtMiddleware(options, z
|
|
9818
|
-
body: z
|
|
9841
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))],
|
|
9842
|
+
body: z.object({ callbackUrl: safeUrlSchema })
|
|
9819
9843
|
}, async (ctx) => {
|
|
9820
9844
|
const { userId } = ctx.context.payload;
|
|
9821
9845
|
const callbackUrl = requireTrustedAuthCallbackUrl(ctx, ctx.body.callbackUrl);
|
|
@@ -9824,20 +9848,21 @@ const sendVerificationEmail = (options) => createAuthEndpoint("/dash/send-verifi
|
|
|
9824
9848
|
if (user.emailVerified) throw ctx.error("BAD_REQUEST", { message: "Email is already verified" });
|
|
9825
9849
|
if (!user.email) throw ctx.error("BAD_REQUEST", { message: "User has no associated email address" });
|
|
9826
9850
|
if (!ctx.context.options.emailVerification?.sendVerificationEmail) throw ctx.error("BAD_REQUEST", { message: "Email verification is not enabled" });
|
|
9827
|
-
|
|
9851
|
+
const modifiedCtx = {
|
|
9828
9852
|
...ctx,
|
|
9829
9853
|
body: {
|
|
9830
9854
|
...ctx.body,
|
|
9831
9855
|
callbackURL: callbackUrl
|
|
9832
9856
|
}
|
|
9833
|
-
}
|
|
9857
|
+
};
|
|
9858
|
+
await sendVerificationEmailFn(modifiedCtx, user);
|
|
9834
9859
|
return { success: true };
|
|
9835
9860
|
});
|
|
9836
9861
|
const sendManyVerificationEmails = (options) => {
|
|
9837
9862
|
return createAuthEndpoint("/dash/send-many-verification-emails", {
|
|
9838
9863
|
method: "POST",
|
|
9839
|
-
use: [jwtMiddleware(options, z
|
|
9840
|
-
body: z
|
|
9864
|
+
use: [jwtMiddleware(options, z.object({ userIds: z.string().array() }))],
|
|
9865
|
+
body: z.object({ callbackUrl: safeUrlSchema })
|
|
9841
9866
|
}, async (ctx) => {
|
|
9842
9867
|
if (!ctx.context.options.emailVerification?.sendVerificationEmail) throw ctx.error("BAD_REQUEST", { message: "Email verification is not enabled" });
|
|
9843
9868
|
const { userIds } = ctx.context.payload;
|
|
@@ -9881,9 +9906,10 @@ const sendManyVerificationEmails = (options) => {
|
|
|
9881
9906
|
success: true,
|
|
9882
9907
|
id: user.id
|
|
9883
9908
|
};
|
|
9884
|
-
}))) if (result.status === "fulfilled")
|
|
9885
|
-
|
|
9886
|
-
|
|
9909
|
+
}))) if (result.status === "fulfilled") {
|
|
9910
|
+
if (result.value.success) sentEmailUserIds.add(result.value.id);
|
|
9911
|
+
else skippedEmailUserIds.add(result.value.id);
|
|
9912
|
+
} else for (const { id } of users) skippedEmailUserIds.add(id);
|
|
9887
9913
|
}, { concurrency: 2 });
|
|
9888
9914
|
const end = performance.now();
|
|
9889
9915
|
console.log(`Time taken to send verification emails to ${sentEmailUserIds.size} users: ${Math.round((end - start) / 1e3)}s`, skippedEmailUserIds.size > 0 ? `Skipped: ${skippedEmailUserIds.size}` : "");
|
|
@@ -9896,8 +9922,8 @@ const sendManyVerificationEmails = (options) => {
|
|
|
9896
9922
|
};
|
|
9897
9923
|
const sendResetPasswordEmail = (options) => createAuthEndpoint("/dash/send-reset-password-email", {
|
|
9898
9924
|
method: "POST",
|
|
9899
|
-
use: [jwtMiddleware(options, z
|
|
9900
|
-
body: z
|
|
9925
|
+
use: [jwtMiddleware(options, z.object({ userId: z.string() }))],
|
|
9926
|
+
body: z.object({ callbackUrl: safeUrlSchema })
|
|
9901
9927
|
}, async (ctx) => {
|
|
9902
9928
|
const { userId } = ctx.context.payload;
|
|
9903
9929
|
const callbackUrl = requireTrustedAuthCallbackUrl(ctx, ctx.body.callbackUrl);
|
|
@@ -9910,19 +9936,17 @@ const sendResetPasswordEmail = (options) => createAuthEndpoint("/dash/send-reset
|
|
|
9910
9936
|
});
|
|
9911
9937
|
//#endregion
|
|
9912
9938
|
//#region src/pow.ts
|
|
9939
|
+
/**
|
|
9940
|
+
* Proof of Work Challenge System - Client Side
|
|
9941
|
+
*
|
|
9942
|
+
* Client-side PoW solver and encoding utilities.
|
|
9943
|
+
* Server-side challenge generation and verification moved to Infra API.
|
|
9944
|
+
*/
|
|
9913
9945
|
/** Default difficulty in bits (18 = ~500ms solve time) */
|
|
9914
9946
|
const DEFAULT_DIFFICULTY = 18;
|
|
9915
9947
|
/** Challenge TTL in seconds */
|
|
9916
9948
|
const CHALLENGE_TTL = 60;
|
|
9917
9949
|
/**
|
|
9918
|
-
* SHA-256 hash function that works in both Node.js and browser
|
|
9919
|
-
*/
|
|
9920
|
-
async function sha256(message) {
|
|
9921
|
-
const msgBuffer = new TextEncoder().encode(message);
|
|
9922
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
|
|
9923
|
-
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
9924
|
-
}
|
|
9925
|
-
/**
|
|
9926
9950
|
* Check if a hash has the required number of leading zero bits
|
|
9927
9951
|
*/
|
|
9928
9952
|
function hasLeadingZeroBits(hash, bits) {
|
|
@@ -9942,7 +9966,8 @@ async function solvePoWChallenge(challenge) {
|
|
|
9942
9966
|
const { nonce, difficulty } = challenge;
|
|
9943
9967
|
let counter = 0;
|
|
9944
9968
|
while (true) {
|
|
9945
|
-
|
|
9969
|
+
const input = `${nonce}:${counter}`;
|
|
9970
|
+
if (hasLeadingZeroBits(await hash$1(input), difficulty)) return {
|
|
9946
9971
|
nonce,
|
|
9947
9972
|
counter
|
|
9948
9973
|
};
|
|
@@ -9972,7 +9997,8 @@ function encodePoWSolution(solution) {
|
|
|
9972
9997
|
* Verify a PoW solution locally (for testing purposes)
|
|
9973
9998
|
*/
|
|
9974
9999
|
async function verifyPoWSolution(nonce, counter, difficulty) {
|
|
9975
|
-
|
|
10000
|
+
const input = `${nonce}:${counter}`;
|
|
10001
|
+
return hasLeadingZeroBits(await hash$1(input), difficulty);
|
|
9976
10002
|
}
|
|
9977
10003
|
//#endregion
|
|
9978
10004
|
//#region src/sms.ts
|
|
@@ -10167,85 +10193,117 @@ const dash = (options) => {
|
|
|
10167
10193
|
const afterCreateOrganization = organizationHooks.afterCreateOrganization;
|
|
10168
10194
|
organizationHooks.afterCreateOrganization = async (...args) => {
|
|
10169
10195
|
const [{ organization, user }] = args;
|
|
10170
|
-
|
|
10196
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10197
|
+
const location = await getRequestLocation();
|
|
10198
|
+
trackOrganizationCreated(organization, trigger, location);
|
|
10171
10199
|
if (afterCreateOrganization) return afterCreateOrganization(...args);
|
|
10172
10200
|
};
|
|
10173
10201
|
const afterUpdateOrganization = organizationHooks.afterUpdateOrganization;
|
|
10174
10202
|
organizationHooks.afterUpdateOrganization = async (...args) => {
|
|
10175
10203
|
const [{ organization, user }] = args;
|
|
10176
|
-
if (organization)
|
|
10204
|
+
if (organization) {
|
|
10205
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10206
|
+
const location = await getRequestLocation();
|
|
10207
|
+
trackOrganizationUpdated(organization, trigger, location);
|
|
10208
|
+
}
|
|
10177
10209
|
if (afterUpdateOrganization) return afterUpdateOrganization(...args);
|
|
10178
10210
|
};
|
|
10179
10211
|
const afterAddMember = organizationHooks.afterAddMember;
|
|
10180
10212
|
organizationHooks.afterAddMember = async (...args) => {
|
|
10181
10213
|
const [{ organization, member, user }] = args;
|
|
10182
|
-
|
|
10214
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10215
|
+
const location = await getRequestLocation();
|
|
10216
|
+
trackOrganizationMemberAdded(organization, member, user, trigger, location);
|
|
10183
10217
|
if (afterAddMember) return afterAddMember(...args);
|
|
10184
10218
|
};
|
|
10185
10219
|
const afterRemoveMember = organizationHooks.afterRemoveMember;
|
|
10186
10220
|
organizationHooks.afterRemoveMember = async (...args) => {
|
|
10187
10221
|
const [{ organization, member, user }] = args;
|
|
10188
|
-
|
|
10222
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10223
|
+
const location = await getRequestLocation();
|
|
10224
|
+
trackOrganizationMemberRemoved(organization, member, user, trigger, location);
|
|
10189
10225
|
if (afterRemoveMember) return afterRemoveMember(...args);
|
|
10190
10226
|
};
|
|
10191
10227
|
const afterUpdateMemberRole = organizationHooks.afterUpdateMemberRole;
|
|
10192
10228
|
organizationHooks.afterUpdateMemberRole = async (...args) => {
|
|
10193
10229
|
const [{ organization, member, user, previousRole }] = args;
|
|
10194
|
-
|
|
10230
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10231
|
+
const location = await getRequestLocation();
|
|
10232
|
+
trackOrganizationMemberRoleUpdated(organization, member, user, previousRole, trigger, location);
|
|
10195
10233
|
if (afterUpdateMemberRole) return afterUpdateMemberRole(...args);
|
|
10196
10234
|
};
|
|
10197
10235
|
const afterCreateInvitation = organizationHooks.afterCreateInvitation;
|
|
10198
10236
|
organizationHooks.afterCreateInvitation = async (...args) => {
|
|
10199
10237
|
const [{ organization, invitation, inviter }] = args;
|
|
10200
|
-
|
|
10238
|
+
const trigger = getOrganizationTriggerInfo(inviter);
|
|
10239
|
+
const location = await getRequestLocation();
|
|
10240
|
+
trackOrganizationMemberInvited(organization, invitation, inviter, trigger, location);
|
|
10201
10241
|
if (afterCreateInvitation) return afterCreateInvitation(...args);
|
|
10202
10242
|
};
|
|
10203
10243
|
const afterAcceptInvitation = organizationHooks.afterAcceptInvitation;
|
|
10204
10244
|
organizationHooks.afterAcceptInvitation = async (...args) => {
|
|
10205
10245
|
const [{ organization, invitation, member, user }] = args;
|
|
10206
|
-
|
|
10246
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10247
|
+
const location = await getRequestLocation();
|
|
10248
|
+
trackOrganizationMemberInviteAccepted(organization, invitation, member, user, trigger, location);
|
|
10207
10249
|
if (afterAcceptInvitation) return afterAcceptInvitation(...args);
|
|
10208
10250
|
};
|
|
10209
10251
|
const afterRejectInvitation = organizationHooks.afterRejectInvitation;
|
|
10210
10252
|
organizationHooks.afterRejectInvitation = async (...args) => {
|
|
10211
10253
|
const [{ organization, invitation, user }] = args;
|
|
10212
|
-
|
|
10254
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10255
|
+
const location = await getRequestLocation();
|
|
10256
|
+
trackOrganizationMemberInviteRejected(organization, invitation, user, trigger, location);
|
|
10213
10257
|
if (afterRejectInvitation) return afterRejectInvitation(...args);
|
|
10214
10258
|
};
|
|
10215
10259
|
const afterCancelInvitation = organizationHooks.afterCancelInvitation;
|
|
10216
10260
|
organizationHooks.afterCancelInvitation = async (...args) => {
|
|
10217
10261
|
const [{ organization, invitation, cancelledBy }] = args;
|
|
10218
|
-
|
|
10262
|
+
const trigger = getOrganizationTriggerInfo(cancelledBy);
|
|
10263
|
+
const location = await getRequestLocation();
|
|
10264
|
+
trackOrganizationMemberInviteCanceled(organization, invitation, cancelledBy, trigger, location);
|
|
10219
10265
|
if (afterCancelInvitation) return afterCancelInvitation(...args);
|
|
10220
10266
|
};
|
|
10221
10267
|
const afterCreateTeam = organizationHooks.afterCreateTeam;
|
|
10222
10268
|
organizationHooks.afterCreateTeam = async (...args) => {
|
|
10223
10269
|
const [{ organization, team, user }] = args;
|
|
10224
|
-
|
|
10270
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10271
|
+
const location = await getRequestLocation();
|
|
10272
|
+
trackOrganizationTeamCreated(organization, team, trigger, location);
|
|
10225
10273
|
if (afterCreateTeam) return afterCreateTeam(...args);
|
|
10226
10274
|
};
|
|
10227
10275
|
const afterUpdateTeam = organizationHooks.afterUpdateTeam;
|
|
10228
10276
|
organizationHooks.afterUpdateTeam = async (...args) => {
|
|
10229
10277
|
const [{ organization, team, user }] = args;
|
|
10230
|
-
if (team)
|
|
10278
|
+
if (team) {
|
|
10279
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10280
|
+
const location = await getRequestLocation();
|
|
10281
|
+
trackOrganizationTeamUpdated(organization, team, trigger, location);
|
|
10282
|
+
}
|
|
10231
10283
|
if (afterUpdateTeam) return afterUpdateTeam(...args);
|
|
10232
10284
|
};
|
|
10233
10285
|
const afterDeleteTeam = organizationHooks.afterDeleteTeam;
|
|
10234
10286
|
organizationHooks.afterDeleteTeam = async (...args) => {
|
|
10235
10287
|
const [{ organization, team, user }] = args;
|
|
10236
|
-
|
|
10288
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10289
|
+
const location = await getRequestLocation();
|
|
10290
|
+
trackOrganizationTeamDeleted(organization, team, trigger, location);
|
|
10237
10291
|
if (afterDeleteTeam) return afterDeleteTeam(...args);
|
|
10238
10292
|
};
|
|
10239
10293
|
const afterAddTeamMember = organizationHooks.afterAddTeamMember;
|
|
10240
10294
|
organizationHooks.afterAddTeamMember = async (...args) => {
|
|
10241
10295
|
const [{ organization, team, user, teamMember }] = args;
|
|
10242
|
-
|
|
10296
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10297
|
+
const location = await getRequestLocation();
|
|
10298
|
+
trackOrganizationTeamMemberAdded(organization, team, user, teamMember, trigger, location);
|
|
10243
10299
|
if (afterAddTeamMember) return afterAddTeamMember(...args);
|
|
10244
10300
|
};
|
|
10245
10301
|
const afterRemoveTeamMember = organizationHooks.afterRemoveTeamMember;
|
|
10246
10302
|
organizationHooks.afterRemoveTeamMember = async (...args) => {
|
|
10247
10303
|
const [{ organization, team, user, teamMember }] = args;
|
|
10248
|
-
|
|
10304
|
+
const trigger = getOrganizationTriggerInfo(user);
|
|
10305
|
+
const location = await getRequestLocation();
|
|
10306
|
+
trackOrganizationTeamMemberRemoved(organization, team, user, teamMember, trigger, location);
|
|
10249
10307
|
if (afterRemoveTeamMember) return afterRemoveTeamMember(...args);
|
|
10250
10308
|
};
|
|
10251
10309
|
};
|
|
@@ -10368,7 +10426,10 @@ const dash = (options) => {
|
|
|
10368
10426
|
}
|
|
10369
10427
|
} else if (matchesAnyRoute(path, [routes.SIGN_OUT])) trackUserSignedOut(enrichedSession, trigger, ctx, location, eventUser);
|
|
10370
10428
|
else trackSessionRevoked(enrichedSession, trigger, ctx, location, eventUser);
|
|
10371
|
-
if ("impersonatedBy" in session && session.impersonatedBy)
|
|
10429
|
+
if ("impersonatedBy" in session && session.impersonatedBy) {
|
|
10430
|
+
const knownImpersonator = resolveUserFromContext(session.impersonatedBy, ctx);
|
|
10431
|
+
trackUserImpersonationStop(enrichedSession, trigger, ctx, location, eventUser, knownImpersonator);
|
|
10432
|
+
}
|
|
10372
10433
|
} }
|
|
10373
10434
|
},
|
|
10374
10435
|
account: {
|
|
@@ -10407,14 +10468,16 @@ const dash = (options) => {
|
|
|
10407
10468
|
const ctx = _ctx;
|
|
10408
10469
|
if (!ctx) return;
|
|
10409
10470
|
const path = ctx.path;
|
|
10410
|
-
const
|
|
10471
|
+
const maybeUserId = ctx.context.session?.user.id ?? "unknown";
|
|
10472
|
+
const trigger = getTriggerInfo(ctx, maybeUserId);
|
|
10411
10473
|
const location = ctx.context.location;
|
|
10412
10474
|
if (matchesAnyRoute(path, [routes.REQUEST_PASSWORD_RESET])) trackPasswordResetRequest(verification, trigger, ctx, location);
|
|
10413
10475
|
} },
|
|
10414
10476
|
delete: { async after(verification, ctx) {
|
|
10415
10477
|
if (!ctx) return;
|
|
10416
10478
|
const path = ctx.path;
|
|
10417
|
-
const
|
|
10479
|
+
const maybeUserId = ctx.context.session?.user.id ?? "unknown";
|
|
10480
|
+
const trigger = getTriggerInfo(ctx, maybeUserId);
|
|
10418
10481
|
const location = ctx.context.location;
|
|
10419
10482
|
if (matchesAnyRoute(path, [routes.RESET_PASSWORD])) trackPasswordResetRequestCompletion(verification, trigger, ctx, location);
|
|
10420
10483
|
} }
|
|
@@ -10462,7 +10525,8 @@ const dash = (options) => {
|
|
|
10462
10525
|
},
|
|
10463
10526
|
handler: createAuthMiddleware(async (_ctx) => {
|
|
10464
10527
|
const ctx = _ctx;
|
|
10465
|
-
const
|
|
10528
|
+
const maybeUserId = ctx.context.session?.user.id ?? "unknown";
|
|
10529
|
+
const trigger = getTriggerInfo(ctx, maybeUserId);
|
|
10466
10530
|
if (matchesAnyRoute(ctx.path, [routes.SEND_VERIFICATION_EMAIL, routes.DASH_SEND_VERIFICATION_EMAIL]) && ctx.context.session && !(ctx.context.returned instanceof Error)) trackEmailVerificationSent(ctx.context.session.session, ctx.context.session.user, trigger, ctx.context.location);
|
|
10467
10531
|
const body = ctx.body;
|
|
10468
10532
|
if (matchesAnyRoute(ctx.path, [routes.SIGN_IN_EMAIL, routes.SIGN_IN_EMAIL_OTP]) && ctx.context.returned instanceof Error && body?.email) trackEmailSignInAttempt(ctx, trigger, ctx.context.location);
|