@mesob/auth-hono 0.4.7 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-DssTTD4U.d.ts → index-CDgzxZzO.d.ts} +8 -1
- package/dist/{index-Cb7JZobZ.d.ts → index-Dhe5obDc.d.ts} +2 -58
- package/dist/index.d.ts +5 -5
- package/dist/index.js +591 -491
- package/dist/index.js.map +1 -1
- package/dist/lib/cleanup.d.ts +2 -2
- package/dist/lib/cookie.d.ts +3 -3
- package/dist/lib/has-role-permission.d.ts +3 -3
- package/dist/lib/iam-seed.d.ts +4 -76
- package/dist/lib/normalize-auth-response.d.ts +3 -3
- package/dist/lib/normalize-user.d.ts +3 -3
- package/dist/lib/openapi-config.d.ts +3 -3
- package/dist/lib/permission-catalog.d.ts +2 -2
- package/dist/lib/phone-validation.d.ts +3 -3
- package/dist/lib/session.d.ts +3 -3
- package/dist/lib/tenant.d.ts +3 -3
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -11,23 +11,6 @@ import { deepmerge } from "deepmerge-ts";
|
|
|
11
11
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
12
12
|
import { Pool } from "pg";
|
|
13
13
|
|
|
14
|
-
// src/db/relations.ts
|
|
15
|
-
var relations_exports = {};
|
|
16
|
-
__export(relations_exports, {
|
|
17
|
-
accountChangesInIamRelations: () => accountChangesInIamRelations,
|
|
18
|
-
accountsInIamRelations: () => accountsInIamRelations,
|
|
19
|
-
domainsInIamRelations: () => domainsInIamRelations,
|
|
20
|
-
permissionsInIamRelations: () => permissionsInIamRelations,
|
|
21
|
-
rolePermissionsInIamRelations: () => rolePermissionsInIamRelations,
|
|
22
|
-
rolesInIamRelations: () => rolesInIamRelations,
|
|
23
|
-
sessionsInIamRelations: () => sessionsInIamRelations,
|
|
24
|
-
tenantsInIamRelations: () => tenantsInIamRelations,
|
|
25
|
-
userRolesInIamRelations: () => userRolesInIamRelations,
|
|
26
|
-
usersInIamRelations: () => usersInIamRelations,
|
|
27
|
-
verificationsInIamRelations: () => verificationsInIamRelations
|
|
28
|
-
});
|
|
29
|
-
import { relations } from "drizzle-orm/relations";
|
|
30
|
-
|
|
31
14
|
// src/db/schema.ts
|
|
32
15
|
var schema_exports = {};
|
|
33
16
|
__export(schema_exports, {
|
|
@@ -333,117 +316,8 @@ var domainsInIam = iam.table("domains", {
|
|
|
333
316
|
check("domains_status_check", sql`status = ANY (ARRAY['PENDING'::text, 'ACTIVE'::text, 'DISABLED'::text, 'DELETED'::text])`)
|
|
334
317
|
]);
|
|
335
318
|
|
|
336
|
-
// src/db/relations.ts
|
|
337
|
-
var verificationsInIamRelations = relations(verificationsInIam, ({ one }) => ({
|
|
338
|
-
tenantsInIam: one(tenantsInIam, {
|
|
339
|
-
fields: [verificationsInIam.tenantId],
|
|
340
|
-
references: [tenantsInIam.id]
|
|
341
|
-
}),
|
|
342
|
-
usersInIam: one(usersInIam, {
|
|
343
|
-
fields: [verificationsInIam.userId],
|
|
344
|
-
references: [usersInIam.id]
|
|
345
|
-
})
|
|
346
|
-
}));
|
|
347
|
-
var tenantsInIamRelations = relations(tenantsInIam, ({ many }) => ({
|
|
348
|
-
verificationsInIam: many(verificationsInIam),
|
|
349
|
-
sessionsInIam: many(sessionsInIam),
|
|
350
|
-
accountChangesInIam: many(accountChangesInIam),
|
|
351
|
-
rolePermissionsInIam: many(rolePermissionsInIam),
|
|
352
|
-
accountsInIam: many(accountsInIam),
|
|
353
|
-
usersInIam: many(usersInIam),
|
|
354
|
-
rolesInIam: many(rolesInIam),
|
|
355
|
-
userRolesInIam: many(userRolesInIam),
|
|
356
|
-
domainsInIam: many(domainsInIam)
|
|
357
|
-
}));
|
|
358
|
-
var usersInIamRelations = relations(usersInIam, ({ one, many }) => ({
|
|
359
|
-
verificationsInIam: many(verificationsInIam),
|
|
360
|
-
sessionsInIam: many(sessionsInIam),
|
|
361
|
-
accountChangesInIam: many(accountChangesInIam),
|
|
362
|
-
accountsInIam: many(accountsInIam),
|
|
363
|
-
tenantsInIam: one(tenantsInIam, {
|
|
364
|
-
fields: [usersInIam.tenantId],
|
|
365
|
-
references: [tenantsInIam.id]
|
|
366
|
-
}),
|
|
367
|
-
userRolesInIam: many(userRolesInIam)
|
|
368
|
-
}));
|
|
369
|
-
var sessionsInIamRelations = relations(sessionsInIam, ({ one }) => ({
|
|
370
|
-
tenantsInIam: one(tenantsInIam, {
|
|
371
|
-
fields: [sessionsInIam.tenantId],
|
|
372
|
-
references: [tenantsInIam.id]
|
|
373
|
-
}),
|
|
374
|
-
usersInIam: one(usersInIam, {
|
|
375
|
-
fields: [sessionsInIam.userId],
|
|
376
|
-
references: [usersInIam.id]
|
|
377
|
-
})
|
|
378
|
-
}));
|
|
379
|
-
var accountChangesInIamRelations = relations(accountChangesInIam, ({ one }) => ({
|
|
380
|
-
tenantsInIam: one(tenantsInIam, {
|
|
381
|
-
fields: [accountChangesInIam.tenantId],
|
|
382
|
-
references: [tenantsInIam.id]
|
|
383
|
-
}),
|
|
384
|
-
usersInIam: one(usersInIam, {
|
|
385
|
-
fields: [accountChangesInIam.userId],
|
|
386
|
-
references: [usersInIam.id]
|
|
387
|
-
})
|
|
388
|
-
}));
|
|
389
|
-
var rolePermissionsInIamRelations = relations(rolePermissionsInIam, ({ one }) => ({
|
|
390
|
-
tenantsInIam: one(tenantsInIam, {
|
|
391
|
-
fields: [rolePermissionsInIam.tenantId],
|
|
392
|
-
references: [tenantsInIam.id]
|
|
393
|
-
}),
|
|
394
|
-
permissionsInIam: one(permissionsInIam, {
|
|
395
|
-
fields: [rolePermissionsInIam.permissionId],
|
|
396
|
-
references: [permissionsInIam.id]
|
|
397
|
-
}),
|
|
398
|
-
rolesInIam: one(rolesInIam, {
|
|
399
|
-
fields: [rolePermissionsInIam.tenantId],
|
|
400
|
-
references: [rolesInIam.tenantId]
|
|
401
|
-
})
|
|
402
|
-
}));
|
|
403
|
-
var permissionsInIamRelations = relations(permissionsInIam, ({ many }) => ({
|
|
404
|
-
rolePermissionsInIam: many(rolePermissionsInIam)
|
|
405
|
-
}));
|
|
406
|
-
var rolesInIamRelations = relations(rolesInIam, ({ one, many }) => ({
|
|
407
|
-
rolePermissionsInIam: many(rolePermissionsInIam),
|
|
408
|
-
tenantsInIam: one(tenantsInIam, {
|
|
409
|
-
fields: [rolesInIam.tenantId],
|
|
410
|
-
references: [tenantsInIam.id]
|
|
411
|
-
}),
|
|
412
|
-
userRolesInIam: many(userRolesInIam)
|
|
413
|
-
}));
|
|
414
|
-
var accountsInIamRelations = relations(accountsInIam, ({ one }) => ({
|
|
415
|
-
tenantsInIam: one(tenantsInIam, {
|
|
416
|
-
fields: [accountsInIam.tenantId],
|
|
417
|
-
references: [tenantsInIam.id]
|
|
418
|
-
}),
|
|
419
|
-
usersInIam: one(usersInIam, {
|
|
420
|
-
fields: [accountsInIam.userId],
|
|
421
|
-
references: [usersInIam.id]
|
|
422
|
-
})
|
|
423
|
-
}));
|
|
424
|
-
var userRolesInIamRelations = relations(userRolesInIam, ({ one }) => ({
|
|
425
|
-
tenantsInIam: one(tenantsInIam, {
|
|
426
|
-
fields: [userRolesInIam.tenantId],
|
|
427
|
-
references: [tenantsInIam.id]
|
|
428
|
-
}),
|
|
429
|
-
usersInIam: one(usersInIam, {
|
|
430
|
-
fields: [userRolesInIam.userId],
|
|
431
|
-
references: [usersInIam.id]
|
|
432
|
-
}),
|
|
433
|
-
rolesInIam: one(rolesInIam, {
|
|
434
|
-
fields: [userRolesInIam.tenantId],
|
|
435
|
-
references: [rolesInIam.tenantId]
|
|
436
|
-
})
|
|
437
|
-
}));
|
|
438
|
-
var domainsInIamRelations = relations(domainsInIam, ({ one }) => ({
|
|
439
|
-
tenantsInIam: one(tenantsInIam, {
|
|
440
|
-
fields: [domainsInIam.tenantId],
|
|
441
|
-
references: [tenantsInIam.id]
|
|
442
|
-
})
|
|
443
|
-
}));
|
|
444
|
-
|
|
445
319
|
// src/db/index.ts
|
|
446
|
-
var schemaConfig = { schema: { ...schema_exports
|
|
320
|
+
var schemaConfig = { schema: { ...schema_exports } };
|
|
447
321
|
var createDatabase = (connectionString) => {
|
|
448
322
|
const pool = new Pool({ connectionString });
|
|
449
323
|
return drizzle({ client: pool, ...schemaConfig });
|
|
@@ -995,6 +869,8 @@ var checkAccountResponseSchema = z.object({
|
|
|
995
869
|
verified: z.boolean(),
|
|
996
870
|
hasPassword: z.boolean(),
|
|
997
871
|
requiresPasswordSetup: z.boolean(),
|
|
872
|
+
needsVerification: z.boolean().optional(),
|
|
873
|
+
verificationId: z.string().uuid().optional(),
|
|
998
874
|
account: authAccountSchema.nullable()
|
|
999
875
|
});
|
|
1000
876
|
var updateProfileSchema = z.object({
|
|
@@ -1021,7 +897,7 @@ var pendingAccountChangeResponseSchema = z.object({
|
|
|
1021
897
|
});
|
|
1022
898
|
|
|
1023
899
|
// src/routes/auth/handler/check-account.ts
|
|
1024
|
-
import { and as
|
|
900
|
+
import { and as and6, eq as eq6, sql as sql4 } from "drizzle-orm";
|
|
1025
901
|
|
|
1026
902
|
// src/lib/tenant.ts
|
|
1027
903
|
import { HTTPException as HTTPException2 } from "hono/http-exception";
|
|
@@ -1043,79 +919,9 @@ var ensureTenantId = (config, tenantId) => {
|
|
|
1043
919
|
return config.tenant.tenantId;
|
|
1044
920
|
};
|
|
1045
921
|
|
|
1046
|
-
// src/routes/auth/
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
const config = c.get("config");
|
|
1050
|
-
const database = c.get("database");
|
|
1051
|
-
const tenantId = c.get("tenantId");
|
|
1052
|
-
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
1053
|
-
const { username } = body;
|
|
1054
|
-
const isEmail = username.includes("@");
|
|
1055
|
-
const userTypeFilter = sql4`${usersInIam.userType} @> ARRAY[${config.userType}]::text[]`;
|
|
1056
|
-
const whereClause = isEmail ? and5(
|
|
1057
|
-
eq5(usersInIam.tenantId, resolvedTenantId),
|
|
1058
|
-
userTypeFilter,
|
|
1059
|
-
sql4`lower(${usersInIam.email}) = lower(${username})`
|
|
1060
|
-
) : and5(
|
|
1061
|
-
eq5(usersInIam.tenantId, resolvedTenantId),
|
|
1062
|
-
userTypeFilter,
|
|
1063
|
-
eq5(usersInIam.phone, username)
|
|
1064
|
-
);
|
|
1065
|
-
const [result] = await database.select({
|
|
1066
|
-
fullName: usersInIam.fullName,
|
|
1067
|
-
email: usersInIam.email,
|
|
1068
|
-
phone: usersInIam.phone,
|
|
1069
|
-
verified: isEmail ? usersInIam.emailVerified : usersInIam.phoneVerified,
|
|
1070
|
-
hasPassword: sql4`exists(
|
|
1071
|
-
select 1
|
|
1072
|
-
from ${accountsInIam}
|
|
1073
|
-
where ${eq5(accountsInIam.tenantId, resolvedTenantId)}
|
|
1074
|
-
and ${eq5(accountsInIam.userId, usersInIam.id)}
|
|
1075
|
-
and ${eq5(accountsInIam.provider, "credentials")}
|
|
1076
|
-
and ${sql4`${accountsInIam.password} is not null`}
|
|
1077
|
-
)`
|
|
1078
|
-
}).from(usersInIam).where(whereClause).limit(1);
|
|
1079
|
-
const verified = result?.verified ?? false;
|
|
1080
|
-
const hasPassword = result?.hasPassword ?? false;
|
|
1081
|
-
return c.json(
|
|
1082
|
-
{
|
|
1083
|
-
exists: !!result,
|
|
1084
|
-
verified,
|
|
1085
|
-
hasPassword,
|
|
1086
|
-
requiresPasswordSetup: !!result && verified && !hasPassword,
|
|
1087
|
-
account: result ? {
|
|
1088
|
-
fullName: result.fullName,
|
|
1089
|
-
email: result.email,
|
|
1090
|
-
phone: result.phone,
|
|
1091
|
-
verified,
|
|
1092
|
-
hasPassword,
|
|
1093
|
-
requiresPasswordSetup: verified && !hasPassword
|
|
1094
|
-
} : null
|
|
1095
|
-
},
|
|
1096
|
-
200
|
|
1097
|
-
);
|
|
1098
|
-
};
|
|
1099
|
-
|
|
1100
|
-
// src/routes/auth/handler/sign-in.ts
|
|
1101
|
-
import { logger as logger2 } from "@mesob/common";
|
|
1102
|
-
import { and as and9, eq as eq9 } from "drizzle-orm";
|
|
1103
|
-
|
|
1104
|
-
// src/errors.ts
|
|
1105
|
-
var AUTH_ERRORS = {
|
|
1106
|
-
USER_NOT_FOUND: "USER_NOT_FOUND",
|
|
1107
|
-
INVALID_PASSWORD: "INVALID_PASSWORD",
|
|
1108
|
-
USER_EXISTS: "USER_EXISTS",
|
|
1109
|
-
VERIFICATION_EXPIRED: "VERIFICATION_EXPIRED",
|
|
1110
|
-
VERIFICATION_MISMATCH: "VERIFICATION_MISMATCH",
|
|
1111
|
-
VERIFICATION_NOT_FOUND: "VERIFICATION_NOT_FOUND",
|
|
1112
|
-
TOO_MANY_ATTEMPTS: "TOO_MANY_ATTEMPTS",
|
|
1113
|
-
REQUIRES_VERIFICATION: "REQUIRES_VERIFICATION",
|
|
1114
|
-
UNAUTHORIZED: "UNAUTHORIZED",
|
|
1115
|
-
ACCESS_DENIED: "ACCESS_DENIED",
|
|
1116
|
-
HAS_NO_PASSWORD: "HAS_NO_PASSWORD",
|
|
1117
|
-
PASSWORD_ALREADY_SET: "PASSWORD_ALREADY_SET"
|
|
1118
|
-
};
|
|
922
|
+
// src/routes/auth/helper/verification.ts
|
|
923
|
+
import { dayjs as dayjs2 } from "@mesob/common";
|
|
924
|
+
import { and as and5, desc, eq as eq5, gt as gt2 } from "drizzle-orm";
|
|
1119
925
|
|
|
1120
926
|
// src/lib/normalize-auth-response.ts
|
|
1121
927
|
var normalizeAuthUser = (user) => ({
|
|
@@ -1209,8 +1015,374 @@ var getRefreshedExpiresAt = ({
|
|
|
1209
1015
|
return addDuration(duration);
|
|
1210
1016
|
};
|
|
1211
1017
|
|
|
1018
|
+
// src/routes/auth/helper/verification.ts
|
|
1019
|
+
var createVerification = async ({
|
|
1020
|
+
tx,
|
|
1021
|
+
tenantId,
|
|
1022
|
+
userId,
|
|
1023
|
+
type,
|
|
1024
|
+
to,
|
|
1025
|
+
config
|
|
1026
|
+
}) => {
|
|
1027
|
+
const isPhone = type === "phone-otp-sign-up";
|
|
1028
|
+
const code = generateOtpCode(
|
|
1029
|
+
isPhone ? config.phone.otpLength : config.email.otpLength
|
|
1030
|
+
);
|
|
1031
|
+
const hashedCode = await hashToken(code, config.secret);
|
|
1032
|
+
const expiresAt = addDuration(
|
|
1033
|
+
isPhone ? config.phone.expiresIn : config.email.expiresIn
|
|
1034
|
+
);
|
|
1035
|
+
const [verification] = await tx.insert(verificationsInIam).values({
|
|
1036
|
+
tenantId,
|
|
1037
|
+
userId,
|
|
1038
|
+
type,
|
|
1039
|
+
code: hashedCode,
|
|
1040
|
+
expiresAt,
|
|
1041
|
+
to,
|
|
1042
|
+
attempt: 0
|
|
1043
|
+
}).returning();
|
|
1044
|
+
return { verificationId: verification.id, code, hash: hashedCode };
|
|
1045
|
+
};
|
|
1046
|
+
var sendVerification = async ({
|
|
1047
|
+
channel,
|
|
1048
|
+
to,
|
|
1049
|
+
code,
|
|
1050
|
+
config,
|
|
1051
|
+
hash,
|
|
1052
|
+
type
|
|
1053
|
+
}) => {
|
|
1054
|
+
if (channel === "phone" && config.phone.sendVerificationOTP) {
|
|
1055
|
+
await config.phone.sendVerificationOTP({
|
|
1056
|
+
phone: to,
|
|
1057
|
+
code,
|
|
1058
|
+
hash,
|
|
1059
|
+
type
|
|
1060
|
+
});
|
|
1061
|
+
} else if (config.email.sendVerificationOTP) {
|
|
1062
|
+
await config.email.sendVerificationOTP({
|
|
1063
|
+
email: to,
|
|
1064
|
+
code,
|
|
1065
|
+
hash,
|
|
1066
|
+
type
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
var checkVerificationResend = async ({
|
|
1071
|
+
database,
|
|
1072
|
+
tenantId,
|
|
1073
|
+
userId,
|
|
1074
|
+
type,
|
|
1075
|
+
resendInterval
|
|
1076
|
+
}) => {
|
|
1077
|
+
if (!resendInterval) {
|
|
1078
|
+
return { blocked: false };
|
|
1079
|
+
}
|
|
1080
|
+
const [verification] = await database.select({
|
|
1081
|
+
id: verificationsInIam.id,
|
|
1082
|
+
createdAt: verificationsInIam.createdAt
|
|
1083
|
+
}).from(verificationsInIam).where(
|
|
1084
|
+
and5(
|
|
1085
|
+
eq5(verificationsInIam.tenantId, tenantId),
|
|
1086
|
+
eq5(verificationsInIam.userId, userId),
|
|
1087
|
+
eq5(verificationsInIam.type, type)
|
|
1088
|
+
)
|
|
1089
|
+
).orderBy(desc(verificationsInIam.createdAt)).limit(1);
|
|
1090
|
+
if (!verification) {
|
|
1091
|
+
return { blocked: false };
|
|
1092
|
+
}
|
|
1093
|
+
const cooldownSeconds = parseDuration(resendInterval);
|
|
1094
|
+
const createdAt = dayjs2(verification.createdAt);
|
|
1095
|
+
const blocked = dayjs2().diff(createdAt, "second") < cooldownSeconds;
|
|
1096
|
+
return { blocked, verificationId: verification.id };
|
|
1097
|
+
};
|
|
1098
|
+
var ensureVerificationForCheckAccount = async ({
|
|
1099
|
+
database,
|
|
1100
|
+
tenantId,
|
|
1101
|
+
userId,
|
|
1102
|
+
type,
|
|
1103
|
+
to,
|
|
1104
|
+
config
|
|
1105
|
+
}) => {
|
|
1106
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1107
|
+
const [existing] = await database.select({
|
|
1108
|
+
id: verificationsInIam.id,
|
|
1109
|
+
expiresAt: verificationsInIam.expiresAt
|
|
1110
|
+
}).from(verificationsInIam).where(
|
|
1111
|
+
and5(
|
|
1112
|
+
eq5(verificationsInIam.tenantId, tenantId),
|
|
1113
|
+
eq5(verificationsInIam.userId, userId),
|
|
1114
|
+
eq5(verificationsInIam.type, type),
|
|
1115
|
+
gt2(verificationsInIam.expiresAt, now)
|
|
1116
|
+
)
|
|
1117
|
+
).orderBy(desc(verificationsInIam.createdAt)).limit(1);
|
|
1118
|
+
if (existing) {
|
|
1119
|
+
return { verificationId: existing.id };
|
|
1120
|
+
}
|
|
1121
|
+
await database.delete(verificationsInIam).where(
|
|
1122
|
+
and5(
|
|
1123
|
+
eq5(verificationsInIam.tenantId, tenantId),
|
|
1124
|
+
eq5(verificationsInIam.userId, userId),
|
|
1125
|
+
eq5(verificationsInIam.type, type)
|
|
1126
|
+
)
|
|
1127
|
+
);
|
|
1128
|
+
const isPhone = type === "phone-otp";
|
|
1129
|
+
const code = generateOtpCode(
|
|
1130
|
+
isPhone ? config.phone.otpLength : config.email.otpLength
|
|
1131
|
+
);
|
|
1132
|
+
const hashedCode = await hashToken(code, config.secret);
|
|
1133
|
+
const expiresAt = addDuration(
|
|
1134
|
+
isPhone ? config.phone.expiresIn : config.email.expiresIn
|
|
1135
|
+
);
|
|
1136
|
+
const [verification] = await database.insert(verificationsInIam).values({
|
|
1137
|
+
tenantId,
|
|
1138
|
+
userId,
|
|
1139
|
+
type,
|
|
1140
|
+
code: hashedCode,
|
|
1141
|
+
expiresAt,
|
|
1142
|
+
to,
|
|
1143
|
+
attempt: 0
|
|
1144
|
+
}).returning();
|
|
1145
|
+
if (isPhone && config.phone.sendVerificationOTP) {
|
|
1146
|
+
await config.phone.sendVerificationOTP({
|
|
1147
|
+
phone: to,
|
|
1148
|
+
code,
|
|
1149
|
+
hash: hashedCode,
|
|
1150
|
+
type
|
|
1151
|
+
});
|
|
1152
|
+
} else if (!isPhone && config.email.sendVerificationOTP) {
|
|
1153
|
+
await config.email.sendVerificationOTP({
|
|
1154
|
+
email: to,
|
|
1155
|
+
code,
|
|
1156
|
+
hash: hashedCode,
|
|
1157
|
+
type
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
return { verificationId: verification.id };
|
|
1161
|
+
};
|
|
1162
|
+
var handleEmailVerification = async ({
|
|
1163
|
+
c,
|
|
1164
|
+
user,
|
|
1165
|
+
config,
|
|
1166
|
+
database,
|
|
1167
|
+
tenantId
|
|
1168
|
+
}) => {
|
|
1169
|
+
if (!user.email) {
|
|
1170
|
+
return c.json({ error: "User email not found" }, 401);
|
|
1171
|
+
}
|
|
1172
|
+
await database.delete(verificationsInIam).where(
|
|
1173
|
+
and5(
|
|
1174
|
+
eq5(verificationsInIam.tenantId, tenantId),
|
|
1175
|
+
eq5(verificationsInIam.userId, user.id),
|
|
1176
|
+
eq5(verificationsInIam.type, "email-verification")
|
|
1177
|
+
)
|
|
1178
|
+
);
|
|
1179
|
+
const code = generateOtpCode(config.email.otpLength);
|
|
1180
|
+
const hashedCode = await hashToken(code, config.secret);
|
|
1181
|
+
const expiresAt = addDuration(config.email.expiresIn);
|
|
1182
|
+
const [verification] = await database.insert(verificationsInIam).values({
|
|
1183
|
+
tenantId,
|
|
1184
|
+
userId: user.id,
|
|
1185
|
+
type: "email-verification",
|
|
1186
|
+
code: hashedCode,
|
|
1187
|
+
expiresAt,
|
|
1188
|
+
to: user.email,
|
|
1189
|
+
attempt: 0
|
|
1190
|
+
}).returning({
|
|
1191
|
+
id: verificationsInIam.id,
|
|
1192
|
+
tenantId: verificationsInIam.tenantId,
|
|
1193
|
+
userId: verificationsInIam.userId,
|
|
1194
|
+
type: verificationsInIam.type,
|
|
1195
|
+
code: verificationsInIam.code,
|
|
1196
|
+
to: verificationsInIam.to,
|
|
1197
|
+
expiresAt: verificationsInIam.expiresAt,
|
|
1198
|
+
createdAt: verificationsInIam.createdAt,
|
|
1199
|
+
attempt: verificationsInIam.attempt
|
|
1200
|
+
});
|
|
1201
|
+
if (config.email.sendVerificationOTP) {
|
|
1202
|
+
await config.email.sendVerificationOTP({
|
|
1203
|
+
email: user.email,
|
|
1204
|
+
code,
|
|
1205
|
+
hash: hashedCode,
|
|
1206
|
+
type: "email-verification"
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
return c.json(
|
|
1210
|
+
{
|
|
1211
|
+
user: normalizeAuthUser(user),
|
|
1212
|
+
session: null,
|
|
1213
|
+
verificationId: verification.id,
|
|
1214
|
+
requiresVerification: true
|
|
1215
|
+
},
|
|
1216
|
+
200
|
|
1217
|
+
);
|
|
1218
|
+
};
|
|
1219
|
+
var handlePhoneVerification = async ({
|
|
1220
|
+
c,
|
|
1221
|
+
user,
|
|
1222
|
+
config,
|
|
1223
|
+
database,
|
|
1224
|
+
tenantId
|
|
1225
|
+
}) => {
|
|
1226
|
+
if (!user.phone) {
|
|
1227
|
+
return c.json({ error: "User phone not found" }, 401);
|
|
1228
|
+
}
|
|
1229
|
+
await database.delete(verificationsInIam).where(
|
|
1230
|
+
and5(
|
|
1231
|
+
eq5(verificationsInIam.tenantId, tenantId),
|
|
1232
|
+
eq5(verificationsInIam.userId, user.id),
|
|
1233
|
+
eq5(verificationsInIam.type, "phone-otp")
|
|
1234
|
+
)
|
|
1235
|
+
);
|
|
1236
|
+
const code = generateOtpCode(config.phone.otpLength);
|
|
1237
|
+
const hashedCode = await hashToken(code, config.secret);
|
|
1238
|
+
const expiresAt = addDuration(config.phone.expiresIn);
|
|
1239
|
+
const [verification] = await database.insert(verificationsInIam).values({
|
|
1240
|
+
tenantId,
|
|
1241
|
+
userId: user.id,
|
|
1242
|
+
type: "phone-otp",
|
|
1243
|
+
code: hashedCode,
|
|
1244
|
+
expiresAt,
|
|
1245
|
+
to: user.phone,
|
|
1246
|
+
attempt: 0
|
|
1247
|
+
}).returning({
|
|
1248
|
+
id: verificationsInIam.id,
|
|
1249
|
+
tenantId: verificationsInIam.tenantId,
|
|
1250
|
+
userId: verificationsInIam.userId,
|
|
1251
|
+
type: verificationsInIam.type,
|
|
1252
|
+
code: verificationsInIam.code,
|
|
1253
|
+
to: verificationsInIam.to,
|
|
1254
|
+
expiresAt: verificationsInIam.expiresAt,
|
|
1255
|
+
createdAt: verificationsInIam.createdAt,
|
|
1256
|
+
attempt: verificationsInIam.attempt
|
|
1257
|
+
});
|
|
1258
|
+
if (config.phone.sendVerificationOTP) {
|
|
1259
|
+
await config.phone.sendVerificationOTP({
|
|
1260
|
+
phone: user.phone,
|
|
1261
|
+
code,
|
|
1262
|
+
hash: hashedCode,
|
|
1263
|
+
type: "phone-otp"
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
return c.json(
|
|
1267
|
+
{
|
|
1268
|
+
user: normalizeAuthUser(user),
|
|
1269
|
+
session: null,
|
|
1270
|
+
verificationId: verification.id,
|
|
1271
|
+
requiresVerification: true
|
|
1272
|
+
},
|
|
1273
|
+
200
|
|
1274
|
+
);
|
|
1275
|
+
};
|
|
1276
|
+
|
|
1277
|
+
// src/routes/auth/handler/check-account.ts
|
|
1278
|
+
var checkAccountHandler = async (c) => {
|
|
1279
|
+
const body = c.req.valid("json");
|
|
1280
|
+
const config = c.get("config");
|
|
1281
|
+
const database = c.get("database");
|
|
1282
|
+
const tenantId = c.get("tenantId");
|
|
1283
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
1284
|
+
const { username } = body;
|
|
1285
|
+
const isEmail = username.includes("@");
|
|
1286
|
+
const disabledChannelResponse = {
|
|
1287
|
+
exists: false,
|
|
1288
|
+
verified: false,
|
|
1289
|
+
hasPassword: false,
|
|
1290
|
+
requiresPasswordSetup: false,
|
|
1291
|
+
account: null
|
|
1292
|
+
};
|
|
1293
|
+
if (isEmail && !config.email.enabled) {
|
|
1294
|
+
return c.json(disabledChannelResponse, 200);
|
|
1295
|
+
}
|
|
1296
|
+
if (!(isEmail || config.phone.enabled)) {
|
|
1297
|
+
return c.json(disabledChannelResponse, 200);
|
|
1298
|
+
}
|
|
1299
|
+
const userTypeFilter = sql4`${usersInIam.userType} @> ARRAY[${config.userType}]::text[]`;
|
|
1300
|
+
const whereClause = isEmail ? and6(
|
|
1301
|
+
eq6(usersInIam.tenantId, resolvedTenantId),
|
|
1302
|
+
userTypeFilter,
|
|
1303
|
+
sql4`lower(${usersInIam.email}) = lower(${username})`
|
|
1304
|
+
) : and6(
|
|
1305
|
+
eq6(usersInIam.tenantId, resolvedTenantId),
|
|
1306
|
+
userTypeFilter,
|
|
1307
|
+
eq6(usersInIam.phone, username)
|
|
1308
|
+
);
|
|
1309
|
+
const [result] = await database.select({
|
|
1310
|
+
id: usersInIam.id,
|
|
1311
|
+
fullName: usersInIam.fullName,
|
|
1312
|
+
email: usersInIam.email,
|
|
1313
|
+
phone: usersInIam.phone,
|
|
1314
|
+
verified: isEmail ? usersInIam.emailVerified : usersInIam.phoneVerified,
|
|
1315
|
+
hasPassword: sql4`exists(
|
|
1316
|
+
select 1
|
|
1317
|
+
from ${accountsInIam}
|
|
1318
|
+
where ${eq6(accountsInIam.tenantId, resolvedTenantId)}
|
|
1319
|
+
and ${eq6(accountsInIam.userId, usersInIam.id)}
|
|
1320
|
+
and ${eq6(accountsInIam.provider, "credentials")}
|
|
1321
|
+
and ${sql4`${accountsInIam.password} is not null`}
|
|
1322
|
+
)`
|
|
1323
|
+
}).from(usersInIam).where(whereClause).limit(1);
|
|
1324
|
+
const verified = result?.verified ?? false;
|
|
1325
|
+
const hasPassword = result?.hasPassword ?? false;
|
|
1326
|
+
let needsVerification = false;
|
|
1327
|
+
let verificationId;
|
|
1328
|
+
if (result && !verified) {
|
|
1329
|
+
const type = isEmail ? "email-verification" : "phone-otp";
|
|
1330
|
+
const to = isEmail ? result.email ?? username : result.phone ?? username;
|
|
1331
|
+
if (to) {
|
|
1332
|
+
const { verificationId: vid } = await ensureVerificationForCheckAccount({
|
|
1333
|
+
database,
|
|
1334
|
+
tenantId: resolvedTenantId,
|
|
1335
|
+
userId: result.id,
|
|
1336
|
+
type,
|
|
1337
|
+
to,
|
|
1338
|
+
config
|
|
1339
|
+
});
|
|
1340
|
+
needsVerification = true;
|
|
1341
|
+
verificationId = vid;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
return c.json(
|
|
1345
|
+
{
|
|
1346
|
+
exists: !!result,
|
|
1347
|
+
verified,
|
|
1348
|
+
hasPassword,
|
|
1349
|
+
requiresPasswordSetup: !!result && verified && !hasPassword,
|
|
1350
|
+
...needsVerification && verificationId ? { needsVerification: true, verificationId } : {},
|
|
1351
|
+
account: result ? {
|
|
1352
|
+
fullName: result.fullName,
|
|
1353
|
+
email: result.email,
|
|
1354
|
+
phone: result.phone,
|
|
1355
|
+
verified,
|
|
1356
|
+
hasPassword,
|
|
1357
|
+
requiresPasswordSetup: verified && !hasPassword
|
|
1358
|
+
} : null
|
|
1359
|
+
},
|
|
1360
|
+
200
|
|
1361
|
+
);
|
|
1362
|
+
};
|
|
1363
|
+
|
|
1364
|
+
// src/routes/auth/handler/sign-in.ts
|
|
1365
|
+
import { logger as logger2 } from "@mesob/common";
|
|
1366
|
+
import { and as and9, eq as eq9 } from "drizzle-orm";
|
|
1367
|
+
|
|
1368
|
+
// src/errors.ts
|
|
1369
|
+
var AUTH_ERRORS = {
|
|
1370
|
+
USER_NOT_FOUND: "USER_NOT_FOUND",
|
|
1371
|
+
INVALID_PASSWORD: "INVALID_PASSWORD",
|
|
1372
|
+
USER_EXISTS: "USER_EXISTS",
|
|
1373
|
+
VERIFICATION_EXPIRED: "VERIFICATION_EXPIRED",
|
|
1374
|
+
VERIFICATION_MISMATCH: "VERIFICATION_MISMATCH",
|
|
1375
|
+
VERIFICATION_NOT_FOUND: "VERIFICATION_NOT_FOUND",
|
|
1376
|
+
TOO_MANY_ATTEMPTS: "TOO_MANY_ATTEMPTS",
|
|
1377
|
+
REQUIRES_VERIFICATION: "REQUIRES_VERIFICATION",
|
|
1378
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
1379
|
+
ACCESS_DENIED: "ACCESS_DENIED",
|
|
1380
|
+
HAS_NO_PASSWORD: "HAS_NO_PASSWORD",
|
|
1381
|
+
PASSWORD_ALREADY_SET: "PASSWORD_ALREADY_SET"
|
|
1382
|
+
};
|
|
1383
|
+
|
|
1212
1384
|
// src/routes/auth/helper/session.ts
|
|
1213
|
-
import { and as
|
|
1385
|
+
import { and as and7, asc, eq as eq7, gt as gt3, inArray, sql as sql5 } from "drizzle-orm";
|
|
1214
1386
|
var createSessionRecord = async ({
|
|
1215
1387
|
tx,
|
|
1216
1388
|
tenantId,
|
|
@@ -1279,10 +1451,10 @@ var cleanupOldSessions = async ({
|
|
|
1279
1451
|
maxSessions
|
|
1280
1452
|
}) => {
|
|
1281
1453
|
const [{ count }] = await database.select({ count: sql5`count(*)` }).from(sessionsInIam).where(
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1454
|
+
and7(
|
|
1455
|
+
eq7(sessionsInIam.tenantId, tenantId),
|
|
1456
|
+
eq7(sessionsInIam.userId, userId),
|
|
1457
|
+
gt3(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1286
1458
|
)
|
|
1287
1459
|
);
|
|
1288
1460
|
if (count <= maxSessions) {
|
|
@@ -1290,19 +1462,19 @@ var cleanupOldSessions = async ({
|
|
|
1290
1462
|
}
|
|
1291
1463
|
const toDeleteCount = count - maxSessions;
|
|
1292
1464
|
const idsToDelete = await database.select({ id: sessionsInIam.id }).from(sessionsInIam).where(
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1465
|
+
and7(
|
|
1466
|
+
eq7(sessionsInIam.tenantId, tenantId),
|
|
1467
|
+
eq7(sessionsInIam.userId, userId),
|
|
1468
|
+
gt3(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1297
1469
|
)
|
|
1298
1470
|
).orderBy(asc(sessionsInIam.createdAt)).limit(toDeleteCount);
|
|
1299
1471
|
if (!idsToDelete.length) {
|
|
1300
1472
|
return;
|
|
1301
1473
|
}
|
|
1302
1474
|
await database.delete(sessionsInIam).where(
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1475
|
+
and7(
|
|
1476
|
+
eq7(sessionsInIam.tenantId, tenantId),
|
|
1477
|
+
eq7(sessionsInIam.userId, userId),
|
|
1306
1478
|
inArray(
|
|
1307
1479
|
sessionsInIam.id,
|
|
1308
1480
|
idsToDelete.map((s) => s.id)
|
|
@@ -1312,17 +1484,17 @@ var cleanupOldSessions = async ({
|
|
|
1312
1484
|
};
|
|
1313
1485
|
|
|
1314
1486
|
// src/routes/auth/helper/user.ts
|
|
1315
|
-
import { and as
|
|
1487
|
+
import { and as and8, eq as eq8, gt as gt4, sql as sql6 } from "drizzle-orm";
|
|
1316
1488
|
var checkExistingUserStatus = async ({
|
|
1317
1489
|
tx,
|
|
1318
1490
|
identifier,
|
|
1319
1491
|
tenantId,
|
|
1320
1492
|
isEmail
|
|
1321
1493
|
}) => {
|
|
1322
|
-
const whereClause = isEmail ?
|
|
1323
|
-
|
|
1494
|
+
const whereClause = isEmail ? and8(
|
|
1495
|
+
eq8(usersInIam.tenantId, tenantId),
|
|
1324
1496
|
sql6`lower(${usersInIam.email}) = lower(${identifier})`
|
|
1325
|
-
) :
|
|
1497
|
+
) : and8(eq8(usersInIam.tenantId, tenantId), eq8(usersInIam.phone, identifier));
|
|
1326
1498
|
const [existingUser] = await tx.select().from(usersInIam).where(whereClause).limit(1);
|
|
1327
1499
|
if (!existingUser) {
|
|
1328
1500
|
return { action: "proceed" };
|
|
@@ -1332,10 +1504,10 @@ var checkExistingUserStatus = async ({
|
|
|
1332
1504
|
return { action: "error", code: AUTH_ERRORS.USER_EXISTS };
|
|
1333
1505
|
}
|
|
1334
1506
|
const [pendingVerification] = await tx.select().from(verificationsInIam).where(
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1507
|
+
and8(
|
|
1508
|
+
eq8(verificationsInIam.userId, existingUser.id),
|
|
1509
|
+
eq8(verificationsInIam.tenantId, tenantId),
|
|
1510
|
+
gt4(verificationsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1339
1511
|
)
|
|
1340
1512
|
).limit(1);
|
|
1341
1513
|
if (pendingVerification) {
|
|
@@ -1354,18 +1526,18 @@ var deleteUnverifiedUser = async ({
|
|
|
1354
1526
|
tenantId
|
|
1355
1527
|
}) => {
|
|
1356
1528
|
await tx.delete(verificationsInIam).where(
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1529
|
+
and8(
|
|
1530
|
+
eq8(verificationsInIam.userId, userId),
|
|
1531
|
+
eq8(verificationsInIam.tenantId, tenantId)
|
|
1360
1532
|
)
|
|
1361
1533
|
);
|
|
1362
1534
|
await tx.delete(accountsInIam).where(
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1535
|
+
and8(
|
|
1536
|
+
eq8(accountsInIam.userId, userId),
|
|
1537
|
+
eq8(accountsInIam.tenantId, tenantId)
|
|
1366
1538
|
)
|
|
1367
1539
|
);
|
|
1368
|
-
await tx.delete(usersInIam).where(
|
|
1540
|
+
await tx.delete(usersInIam).where(and8(eq8(usersInIam.id, userId), eq8(usersInIam.tenantId, tenantId)));
|
|
1369
1541
|
};
|
|
1370
1542
|
var createUserWithAccount = async ({
|
|
1371
1543
|
tx,
|
|
@@ -1405,14 +1577,14 @@ var fetchUserForLogin = async ({
|
|
|
1405
1577
|
userType
|
|
1406
1578
|
}) => {
|
|
1407
1579
|
const userTypeFilter = sql6`${usersInIam.userType} @> ARRAY[${userType}]::text[]`;
|
|
1408
|
-
const whereClause = isEmail ?
|
|
1409
|
-
|
|
1580
|
+
const whereClause = isEmail ? and8(
|
|
1581
|
+
eq8(usersInIam.tenantId, tenantId),
|
|
1410
1582
|
userTypeFilter,
|
|
1411
1583
|
sql6`lower(${usersInIam.email}) = lower(${identifier})`
|
|
1412
|
-
) :
|
|
1413
|
-
|
|
1584
|
+
) : and8(
|
|
1585
|
+
eq8(usersInIam.tenantId, tenantId),
|
|
1414
1586
|
userTypeFilter,
|
|
1415
|
-
|
|
1587
|
+
eq8(usersInIam.phone, identifier)
|
|
1416
1588
|
);
|
|
1417
1589
|
const [row] = await database.select({
|
|
1418
1590
|
id: usersInIam.id,
|
|
@@ -1430,9 +1602,9 @@ var fetchUserForLogin = async ({
|
|
|
1430
1602
|
hasPassword: sql6`exists(
|
|
1431
1603
|
select 1
|
|
1432
1604
|
from ${accountsInIam}
|
|
1433
|
-
where ${
|
|
1434
|
-
and ${
|
|
1435
|
-
and ${
|
|
1605
|
+
where ${eq8(accountsInIam.tenantId, tenantId)}
|
|
1606
|
+
and ${eq8(accountsInIam.userId, usersInIam.id)}
|
|
1607
|
+
and ${eq8(accountsInIam.provider, "credentials")}
|
|
1436
1608
|
and ${sql6`${accountsInIam.password} is not null`}
|
|
1437
1609
|
)`
|
|
1438
1610
|
}).from(usersInIam).where(whereClause).limit(1);
|
|
@@ -1457,207 +1629,10 @@ var fetchUserByIdWithRoles = async ({
|
|
|
1457
1629
|
bannedUntil: usersInIam.bannedUntil,
|
|
1458
1630
|
loginAttempt: usersInIam.loginAttempt,
|
|
1459
1631
|
...getUserAuthSelect(tenantId)
|
|
1460
|
-
}).from(usersInIam).where(
|
|
1632
|
+
}).from(usersInIam).where(and8(eq8(usersInIam.id, userId), eq8(usersInIam.tenantId, tenantId))).limit(1);
|
|
1461
1633
|
return result || null;
|
|
1462
1634
|
};
|
|
1463
1635
|
|
|
1464
|
-
// src/routes/auth/helper/verification.ts
|
|
1465
|
-
import { dayjs as dayjs2 } from "@mesob/common";
|
|
1466
|
-
import { and as and8, desc, eq as eq8 } from "drizzle-orm";
|
|
1467
|
-
var createVerification = async ({
|
|
1468
|
-
tx,
|
|
1469
|
-
tenantId,
|
|
1470
|
-
userId,
|
|
1471
|
-
type,
|
|
1472
|
-
to,
|
|
1473
|
-
config
|
|
1474
|
-
}) => {
|
|
1475
|
-
const isPhone = type === "phone-otp-sign-up";
|
|
1476
|
-
const code = generateOtpCode(
|
|
1477
|
-
isPhone ? config.phone.otpLength : config.email.otpLength
|
|
1478
|
-
);
|
|
1479
|
-
const hashedCode = await hashToken(code, config.secret);
|
|
1480
|
-
const expiresAt = addDuration(
|
|
1481
|
-
isPhone ? config.phone.expiresIn : config.email.expiresIn
|
|
1482
|
-
);
|
|
1483
|
-
const [verification] = await tx.insert(verificationsInIam).values({
|
|
1484
|
-
tenantId,
|
|
1485
|
-
userId,
|
|
1486
|
-
type,
|
|
1487
|
-
code: hashedCode,
|
|
1488
|
-
expiresAt,
|
|
1489
|
-
to,
|
|
1490
|
-
attempt: 0
|
|
1491
|
-
}).returning();
|
|
1492
|
-
return { verificationId: verification.id, code, hash: hashedCode };
|
|
1493
|
-
};
|
|
1494
|
-
var sendVerification = async ({
|
|
1495
|
-
channel,
|
|
1496
|
-
to,
|
|
1497
|
-
code,
|
|
1498
|
-
config,
|
|
1499
|
-
hash,
|
|
1500
|
-
type
|
|
1501
|
-
}) => {
|
|
1502
|
-
if (channel === "phone" && config.phone.sendVerificationOTP) {
|
|
1503
|
-
await config.phone.sendVerificationOTP({
|
|
1504
|
-
phone: to,
|
|
1505
|
-
code,
|
|
1506
|
-
hash,
|
|
1507
|
-
type
|
|
1508
|
-
});
|
|
1509
|
-
} else if (config.email.sendVerificationOTP) {
|
|
1510
|
-
await config.email.sendVerificationOTP({
|
|
1511
|
-
email: to,
|
|
1512
|
-
code,
|
|
1513
|
-
hash,
|
|
1514
|
-
type
|
|
1515
|
-
});
|
|
1516
|
-
}
|
|
1517
|
-
};
|
|
1518
|
-
var checkVerificationResend = async ({
|
|
1519
|
-
database,
|
|
1520
|
-
tenantId,
|
|
1521
|
-
userId,
|
|
1522
|
-
type,
|
|
1523
|
-
resendInterval
|
|
1524
|
-
}) => {
|
|
1525
|
-
if (!resendInterval) {
|
|
1526
|
-
return { blocked: false };
|
|
1527
|
-
}
|
|
1528
|
-
const [verification] = await database.select({
|
|
1529
|
-
id: verificationsInIam.id,
|
|
1530
|
-
createdAt: verificationsInIam.createdAt
|
|
1531
|
-
}).from(verificationsInIam).where(
|
|
1532
|
-
and8(
|
|
1533
|
-
eq8(verificationsInIam.tenantId, tenantId),
|
|
1534
|
-
eq8(verificationsInIam.userId, userId),
|
|
1535
|
-
eq8(verificationsInIam.type, type)
|
|
1536
|
-
)
|
|
1537
|
-
).orderBy(desc(verificationsInIam.createdAt)).limit(1);
|
|
1538
|
-
if (!verification) {
|
|
1539
|
-
return { blocked: false };
|
|
1540
|
-
}
|
|
1541
|
-
const cooldownSeconds = parseDuration(resendInterval);
|
|
1542
|
-
const createdAt = dayjs2(verification.createdAt);
|
|
1543
|
-
const blocked = dayjs2().diff(createdAt, "second") < cooldownSeconds;
|
|
1544
|
-
return { blocked, verificationId: verification.id };
|
|
1545
|
-
};
|
|
1546
|
-
var handleEmailVerification = async ({
|
|
1547
|
-
c,
|
|
1548
|
-
user,
|
|
1549
|
-
config,
|
|
1550
|
-
database,
|
|
1551
|
-
tenantId
|
|
1552
|
-
}) => {
|
|
1553
|
-
if (!user.email) {
|
|
1554
|
-
return c.json({ error: "User email not found" }, 401);
|
|
1555
|
-
}
|
|
1556
|
-
await database.delete(verificationsInIam).where(
|
|
1557
|
-
and8(
|
|
1558
|
-
eq8(verificationsInIam.tenantId, tenantId),
|
|
1559
|
-
eq8(verificationsInIam.userId, user.id),
|
|
1560
|
-
eq8(verificationsInIam.type, "email-verification")
|
|
1561
|
-
)
|
|
1562
|
-
);
|
|
1563
|
-
const code = generateOtpCode(config.email.otpLength);
|
|
1564
|
-
const hashedCode = await hashToken(code, config.secret);
|
|
1565
|
-
const expiresAt = addDuration(config.email.expiresIn);
|
|
1566
|
-
const [verification] = await database.insert(verificationsInIam).values({
|
|
1567
|
-
tenantId,
|
|
1568
|
-
userId: user.id,
|
|
1569
|
-
type: "email-verification",
|
|
1570
|
-
code: hashedCode,
|
|
1571
|
-
expiresAt,
|
|
1572
|
-
to: user.email,
|
|
1573
|
-
attempt: 0
|
|
1574
|
-
}).returning({
|
|
1575
|
-
id: verificationsInIam.id,
|
|
1576
|
-
tenantId: verificationsInIam.tenantId,
|
|
1577
|
-
userId: verificationsInIam.userId,
|
|
1578
|
-
type: verificationsInIam.type,
|
|
1579
|
-
code: verificationsInIam.code,
|
|
1580
|
-
to: verificationsInIam.to,
|
|
1581
|
-
expiresAt: verificationsInIam.expiresAt,
|
|
1582
|
-
createdAt: verificationsInIam.createdAt,
|
|
1583
|
-
attempt: verificationsInIam.attempt
|
|
1584
|
-
});
|
|
1585
|
-
if (config.email.sendVerificationOTP) {
|
|
1586
|
-
await config.email.sendVerificationOTP({
|
|
1587
|
-
email: user.email,
|
|
1588
|
-
code,
|
|
1589
|
-
hash: hashedCode,
|
|
1590
|
-
type: "email-verification"
|
|
1591
|
-
});
|
|
1592
|
-
}
|
|
1593
|
-
return c.json(
|
|
1594
|
-
{
|
|
1595
|
-
user: normalizeAuthUser(user),
|
|
1596
|
-
session: null,
|
|
1597
|
-
verificationId: verification.id,
|
|
1598
|
-
requiresVerification: true
|
|
1599
|
-
},
|
|
1600
|
-
200
|
|
1601
|
-
);
|
|
1602
|
-
};
|
|
1603
|
-
var handlePhoneVerification = async ({
|
|
1604
|
-
c,
|
|
1605
|
-
user,
|
|
1606
|
-
config,
|
|
1607
|
-
database,
|
|
1608
|
-
tenantId
|
|
1609
|
-
}) => {
|
|
1610
|
-
if (!user.phone) {
|
|
1611
|
-
return c.json({ error: "User phone not found" }, 401);
|
|
1612
|
-
}
|
|
1613
|
-
await database.delete(verificationsInIam).where(
|
|
1614
|
-
and8(
|
|
1615
|
-
eq8(verificationsInIam.tenantId, tenantId),
|
|
1616
|
-
eq8(verificationsInIam.userId, user.id),
|
|
1617
|
-
eq8(verificationsInIam.type, "phone-otp")
|
|
1618
|
-
)
|
|
1619
|
-
);
|
|
1620
|
-
const code = generateOtpCode(config.phone.otpLength);
|
|
1621
|
-
const hashedCode = await hashToken(code, config.secret);
|
|
1622
|
-
const expiresAt = addDuration(config.phone.expiresIn);
|
|
1623
|
-
const [verification] = await database.insert(verificationsInIam).values({
|
|
1624
|
-
tenantId,
|
|
1625
|
-
userId: user.id,
|
|
1626
|
-
type: "phone-otp",
|
|
1627
|
-
code: hashedCode,
|
|
1628
|
-
expiresAt,
|
|
1629
|
-
to: user.phone,
|
|
1630
|
-
attempt: 0
|
|
1631
|
-
}).returning({
|
|
1632
|
-
id: verificationsInIam.id,
|
|
1633
|
-
tenantId: verificationsInIam.tenantId,
|
|
1634
|
-
userId: verificationsInIam.userId,
|
|
1635
|
-
type: verificationsInIam.type,
|
|
1636
|
-
code: verificationsInIam.code,
|
|
1637
|
-
to: verificationsInIam.to,
|
|
1638
|
-
expiresAt: verificationsInIam.expiresAt,
|
|
1639
|
-
createdAt: verificationsInIam.createdAt,
|
|
1640
|
-
attempt: verificationsInIam.attempt
|
|
1641
|
-
});
|
|
1642
|
-
if (config.phone.sendVerificationOTP) {
|
|
1643
|
-
await config.phone.sendVerificationOTP({
|
|
1644
|
-
phone: user.phone,
|
|
1645
|
-
code,
|
|
1646
|
-
hash: hashedCode,
|
|
1647
|
-
type: "phone-otp"
|
|
1648
|
-
});
|
|
1649
|
-
}
|
|
1650
|
-
return c.json(
|
|
1651
|
-
{
|
|
1652
|
-
user: normalizeAuthUser(user),
|
|
1653
|
-
session: null,
|
|
1654
|
-
verificationId: verification.id,
|
|
1655
|
-
requiresVerification: true
|
|
1656
|
-
},
|
|
1657
|
-
200
|
|
1658
|
-
);
|
|
1659
|
-
};
|
|
1660
|
-
|
|
1661
1636
|
// src/routes/auth/handler/sign-in.ts
|
|
1662
1637
|
var signInHandler = (
|
|
1663
1638
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: auth flow combines lockout, verification, and session creation
|
|
@@ -1821,7 +1796,7 @@ var signInHandler = (
|
|
|
1821
1796
|
);
|
|
1822
1797
|
|
|
1823
1798
|
// src/routes/auth/handler/sign-out.ts
|
|
1824
|
-
import { and as and10, eq as eq10, gt as
|
|
1799
|
+
import { and as and10, eq as eq10, gt as gt5 } from "drizzle-orm";
|
|
1825
1800
|
import { getCookie } from "hono/cookie";
|
|
1826
1801
|
var signOutHandler = async (c) => {
|
|
1827
1802
|
const config = c.get("config");
|
|
@@ -1845,7 +1820,7 @@ var signOutHandler = async (c) => {
|
|
|
1845
1820
|
}).from(sessionsInIam).where(
|
|
1846
1821
|
and10(
|
|
1847
1822
|
eq10(sessionsInIam.token, hashedToken),
|
|
1848
|
-
|
|
1823
|
+
gt5(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1849
1824
|
)
|
|
1850
1825
|
).limit(1);
|
|
1851
1826
|
if (session) {
|
|
@@ -1884,6 +1859,20 @@ var SignUpError = class extends Error {
|
|
|
1884
1859
|
this.status = status;
|
|
1885
1860
|
}
|
|
1886
1861
|
};
|
|
1862
|
+
var normalizeSignupDomain = (value) => {
|
|
1863
|
+
return value.trim().toLowerCase().replace(/^@/, "");
|
|
1864
|
+
};
|
|
1865
|
+
var isAllowedSignupEmail = (email, allowedDomains) => {
|
|
1866
|
+
const domain = email.split("@")[1]?.toLowerCase();
|
|
1867
|
+
if (!domain) {
|
|
1868
|
+
return false;
|
|
1869
|
+
}
|
|
1870
|
+
const normalizedDomains = allowedDomains.map(normalizeSignupDomain).filter(Boolean);
|
|
1871
|
+
if (normalizedDomains.length === 0) {
|
|
1872
|
+
return true;
|
|
1873
|
+
}
|
|
1874
|
+
return normalizedDomains.includes(domain);
|
|
1875
|
+
};
|
|
1887
1876
|
var signUpHandler = async (c) => {
|
|
1888
1877
|
const body = c.req.valid("json");
|
|
1889
1878
|
const config = c.get("config");
|
|
@@ -1895,7 +1884,19 @@ var signUpHandler = async (c) => {
|
|
|
1895
1884
|
if (!identifier) {
|
|
1896
1885
|
return c.json({ error: "Either email or phone is required" }, 409);
|
|
1897
1886
|
}
|
|
1887
|
+
if (config.signUp && !config.signUp.enabled) {
|
|
1888
|
+
return c.json({ error: "Sign up is disabled" }, 403);
|
|
1889
|
+
}
|
|
1898
1890
|
const isEmail = identifier.includes("@");
|
|
1891
|
+
if (isEmail && config.signUp && !config.signUp.emailEnabled) {
|
|
1892
|
+
return c.json({ error: "Email sign up is disabled" }, 403);
|
|
1893
|
+
}
|
|
1894
|
+
if (!isEmail && config.signUp && !config.signUp.phoneEnabled) {
|
|
1895
|
+
return c.json({ error: "Phone sign up is disabled" }, 403);
|
|
1896
|
+
}
|
|
1897
|
+
if (isEmail && config.signUp?.allowedEmailDomains && !isAllowedSignupEmail(identifier, config.signUp.allowedEmailDomains)) {
|
|
1898
|
+
return c.json({ error: "Email domain is not allowed for sign up" }, 403);
|
|
1899
|
+
}
|
|
1899
1900
|
if (phone) {
|
|
1900
1901
|
const phoneValidator = createPhoneField(config);
|
|
1901
1902
|
if (!phoneValidator.validate(phone)) {
|
|
@@ -2832,7 +2833,7 @@ var email_route_default = emailRoutes;
|
|
|
2832
2833
|
import { createRoute as createRoute4, OpenAPIHono as OpenAPIHono4 } from "@hono/zod-openapi";
|
|
2833
2834
|
|
|
2834
2835
|
// src/routes/password/handler/change.ts
|
|
2835
|
-
import { and as and18, eq as eq18, gt as
|
|
2836
|
+
import { and as and18, eq as eq18, gt as gt6, ne } from "drizzle-orm";
|
|
2836
2837
|
import { getCookie as getCookie2 } from "hono/cookie";
|
|
2837
2838
|
var changePasswordHandler = async (c) => {
|
|
2838
2839
|
const body = c.req.valid("json");
|
|
@@ -2878,7 +2879,7 @@ var changePasswordHandler = async (c) => {
|
|
|
2878
2879
|
and18(
|
|
2879
2880
|
eq18(sessionsInIam.tenantId, resolvedTenantId),
|
|
2880
2881
|
eq18(sessionsInIam.userId, userId),
|
|
2881
|
-
|
|
2882
|
+
gt6(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString()),
|
|
2882
2883
|
ne(sessionsInIam.token, hashedToken)
|
|
2883
2884
|
)
|
|
2884
2885
|
);
|
|
@@ -3075,7 +3076,7 @@ var forgotPasswordHandler = async (c) => {
|
|
|
3075
3076
|
};
|
|
3076
3077
|
|
|
3077
3078
|
// src/routes/password/handler/reset.ts
|
|
3078
|
-
import { and as and20, eq as eq20, gt as
|
|
3079
|
+
import { and as and20, eq as eq20, gt as gt7 } from "drizzle-orm";
|
|
3079
3080
|
|
|
3080
3081
|
// src/routes/password/helper/session.ts
|
|
3081
3082
|
var createPasswordResetSession = async ({
|
|
@@ -3155,7 +3156,7 @@ var resetPasswordHandler = async (c) => {
|
|
|
3155
3156
|
and20(
|
|
3156
3157
|
eq20(sessionsInIam.tenantId, resolvedTenantId),
|
|
3157
3158
|
eq20(sessionsInIam.userId, verification.userId),
|
|
3158
|
-
|
|
3159
|
+
gt7(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
3159
3160
|
)
|
|
3160
3161
|
);
|
|
3161
3162
|
await database.delete(verificationsInIam).where(eq20(verificationsInIam.id, verificationId));
|
|
@@ -4025,6 +4026,14 @@ var phoneVerificationRequestHandler = async (c) => {
|
|
|
4025
4026
|
return c.json({ error: "Phone authentication is disabled" }, 400);
|
|
4026
4027
|
}
|
|
4027
4028
|
const { phone, context } = body;
|
|
4029
|
+
if (context === "sign-up" && config.signUp) {
|
|
4030
|
+
if (!config.signUp.enabled) {
|
|
4031
|
+
return c.json({ error: "Sign up is disabled" }, 400);
|
|
4032
|
+
}
|
|
4033
|
+
if (!config.signUp.phoneEnabled) {
|
|
4034
|
+
return c.json({ error: "Phone sign up is disabled" }, 400);
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4028
4037
|
if (!phone) {
|
|
4029
4038
|
return c.json({ error: "Phone required" }, 400);
|
|
4030
4039
|
}
|
|
@@ -4221,7 +4230,7 @@ import { createRoute as createRoute7, OpenAPIHono as OpenAPIHono7 } from "@hono/
|
|
|
4221
4230
|
import { z as z4 } from "zod";
|
|
4222
4231
|
|
|
4223
4232
|
// src/routes/profile/handler/account-change-pending.ts
|
|
4224
|
-
import { and as and26, eq as eq27, gt as
|
|
4233
|
+
import { and as and26, eq as eq27, gt as gt8, sql as sql13 } from "drizzle-orm";
|
|
4225
4234
|
var accountChangePendingHandler = async (c) => {
|
|
4226
4235
|
const config = c.get("config");
|
|
4227
4236
|
const database = c.get("database");
|
|
@@ -4259,7 +4268,7 @@ var accountChangePendingHandler = async (c) => {
|
|
|
4259
4268
|
eq27(verificationsInIam.userId, userId),
|
|
4260
4269
|
eq27(verificationsInIam.type, "email-verification"),
|
|
4261
4270
|
eq27(verificationsInIam.to, accountChange.newEmail),
|
|
4262
|
-
|
|
4271
|
+
gt8(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
|
|
4263
4272
|
)
|
|
4264
4273
|
).limit(1);
|
|
4265
4274
|
verification = v || null;
|
|
@@ -4274,7 +4283,7 @@ var accountChangePendingHandler = async (c) => {
|
|
|
4274
4283
|
eq27(verificationsInIam.userId, userId),
|
|
4275
4284
|
eq27(verificationsInIam.type, "phone-otp-change-phone"),
|
|
4276
4285
|
eq27(verificationsInIam.to, accountChange.newPhone),
|
|
4277
|
-
|
|
4286
|
+
gt8(verificationsInIam.expiresAt, sql13`CURRENT_TIMESTAMP`)
|
|
4278
4287
|
)
|
|
4279
4288
|
).limit(1);
|
|
4280
4289
|
verification = v || null;
|
|
@@ -6959,6 +6968,9 @@ var banUserHandler = async (c) => {
|
|
|
6959
6968
|
return c.json({ user: normalizeUser(userWithRoles) }, 200);
|
|
6960
6969
|
};
|
|
6961
6970
|
|
|
6971
|
+
// src/routes/users/helper/invite.ts
|
|
6972
|
+
import { and as and52, eq as eq57 } from "drizzle-orm";
|
|
6973
|
+
|
|
6962
6974
|
// src/routes/users/helper/user.ts
|
|
6963
6975
|
import { and as and51, eq as eq56, sql as sql26 } from "drizzle-orm";
|
|
6964
6976
|
var checkUserExists = async ({
|
|
@@ -7028,7 +7040,89 @@ var inviteUser = (
|
|
|
7028
7040
|
isEmail
|
|
7029
7041
|
});
|
|
7030
7042
|
if (existing) {
|
|
7031
|
-
|
|
7043
|
+
const identifierVerified = isEmail ? existing.emailVerified : existing.phoneVerified;
|
|
7044
|
+
const hasInviteType = Array.isArray(existing.userType) && existing.userType.includes(config.userType);
|
|
7045
|
+
if (identifierVerified && hasInviteType) {
|
|
7046
|
+
throw new Error("User already exists");
|
|
7047
|
+
}
|
|
7048
|
+
const updates = {};
|
|
7049
|
+
if (!identifierVerified) {
|
|
7050
|
+
if (payload.emailVerified !== void 0 && payload.email) {
|
|
7051
|
+
updates.emailVerified = Boolean(payload.emailVerified);
|
|
7052
|
+
}
|
|
7053
|
+
if (payload.phoneVerified !== void 0 && payload.phone) {
|
|
7054
|
+
updates.phoneVerified = Boolean(payload.phoneVerified);
|
|
7055
|
+
}
|
|
7056
|
+
}
|
|
7057
|
+
if (!hasInviteType) {
|
|
7058
|
+
updates.userType = [config.userType];
|
|
7059
|
+
}
|
|
7060
|
+
let user2;
|
|
7061
|
+
if (Object.keys(updates).length > 0) {
|
|
7062
|
+
const [updated] = await database.update(usersInIam).set(updates).where(eq57(usersInIam.id, existing.id)).returning();
|
|
7063
|
+
if (!updated) {
|
|
7064
|
+
throw new Error("User update failed");
|
|
7065
|
+
}
|
|
7066
|
+
user2 = updated;
|
|
7067
|
+
} else {
|
|
7068
|
+
user2 = existing;
|
|
7069
|
+
}
|
|
7070
|
+
const [credAccount] = await database.select().from(accountsInIam).where(
|
|
7071
|
+
and52(
|
|
7072
|
+
eq57(accountsInIam.tenantId, tenantId),
|
|
7073
|
+
eq57(accountsInIam.userId, user2.id),
|
|
7074
|
+
eq57(accountsInIam.provider, "credentials")
|
|
7075
|
+
)
|
|
7076
|
+
).limit(1);
|
|
7077
|
+
const hasPassword2 = Boolean(credAccount?.password);
|
|
7078
|
+
const resolvedInviteUrl2 = resolveInviteUrl({
|
|
7079
|
+
inviteUrl: payload.inviteUrl,
|
|
7080
|
+
identifier,
|
|
7081
|
+
hasPassword: hasPassword2
|
|
7082
|
+
});
|
|
7083
|
+
const delivery2 = {};
|
|
7084
|
+
if (payload.sendVia?.includes("email")) {
|
|
7085
|
+
if (payload.email && config.email.sendInvitation) {
|
|
7086
|
+
try {
|
|
7087
|
+
await config.email.sendInvitation({
|
|
7088
|
+
email: payload.email,
|
|
7089
|
+
fullName: user2.fullName,
|
|
7090
|
+
identifier,
|
|
7091
|
+
inviteUrl: resolvedInviteUrl2,
|
|
7092
|
+
tenantId
|
|
7093
|
+
});
|
|
7094
|
+
delivery2.email = "sent";
|
|
7095
|
+
} catch {
|
|
7096
|
+
delivery2.email = "failed";
|
|
7097
|
+
}
|
|
7098
|
+
} else {
|
|
7099
|
+
delivery2.email = "skipped";
|
|
7100
|
+
}
|
|
7101
|
+
}
|
|
7102
|
+
if (payload.sendVia?.includes("sms")) {
|
|
7103
|
+
if (payload.phone && config.phone.sendInvitation) {
|
|
7104
|
+
try {
|
|
7105
|
+
await config.phone.sendInvitation({
|
|
7106
|
+
phone: payload.phone,
|
|
7107
|
+
fullName: user2.fullName,
|
|
7108
|
+
identifier,
|
|
7109
|
+
inviteUrl: resolvedInviteUrl2,
|
|
7110
|
+
tenantId
|
|
7111
|
+
});
|
|
7112
|
+
delivery2.sms = "sent";
|
|
7113
|
+
} catch {
|
|
7114
|
+
delivery2.sms = "failed";
|
|
7115
|
+
}
|
|
7116
|
+
} else {
|
|
7117
|
+
delivery2.sms = "skipped";
|
|
7118
|
+
}
|
|
7119
|
+
}
|
|
7120
|
+
return {
|
|
7121
|
+
user: normalizeUser(user2),
|
|
7122
|
+
delivery: delivery2,
|
|
7123
|
+
inviteUrl: resolvedInviteUrl2,
|
|
7124
|
+
hasPassword: hasPassword2
|
|
7125
|
+
};
|
|
7032
7126
|
}
|
|
7033
7127
|
const userHandle = payload.handle || generateHandle();
|
|
7034
7128
|
const existingHandle = await checkHandleExists({
|
|
@@ -7213,16 +7307,16 @@ var createUserHandler = async (c) => {
|
|
|
7213
7307
|
};
|
|
7214
7308
|
|
|
7215
7309
|
// src/routes/users/handler/delete-user.ts
|
|
7216
|
-
import { and as
|
|
7310
|
+
import { and as and53, eq as eq58 } from "drizzle-orm";
|
|
7217
7311
|
var deleteUserHandler = async (c) => {
|
|
7218
7312
|
const { id } = c.req.valid("param");
|
|
7219
7313
|
const database = c.get("database");
|
|
7220
7314
|
const tenantId = c.get("tenantId");
|
|
7221
|
-
const [existing] = await database.select().from(usersInIam).where(
|
|
7315
|
+
const [existing] = await database.select().from(usersInIam).where(and53(eq58(usersInIam.id, id), eq58(usersInIam.tenantId, tenantId))).limit(1);
|
|
7222
7316
|
if (!existing) {
|
|
7223
7317
|
return c.json({ error: "User not found" }, 404);
|
|
7224
7318
|
}
|
|
7225
|
-
await database.delete(usersInIam).where(
|
|
7319
|
+
await database.delete(usersInIam).where(and53(eq58(usersInIam.id, id), eq58(usersInIam.tenantId, tenantId)));
|
|
7226
7320
|
return c.json({ message: "User deleted" }, 200);
|
|
7227
7321
|
};
|
|
7228
7322
|
|
|
@@ -7266,7 +7360,7 @@ var inviteUserHandler = async (c) => {
|
|
|
7266
7360
|
};
|
|
7267
7361
|
|
|
7268
7362
|
// src/routes/users/handler/list-users.ts
|
|
7269
|
-
import { and as
|
|
7363
|
+
import { and as and54, asc as asc5, desc as desc5, eq as eq59, gt as gt9, ilike as ilike4, inArray as inArray6, or as or4, sql as sql27 } from "drizzle-orm";
|
|
7270
7364
|
var userSelect = {
|
|
7271
7365
|
id: usersInIam.id,
|
|
7272
7366
|
tenantId: usersInIam.tenantId,
|
|
@@ -7319,7 +7413,7 @@ var listUsersHandler = async (c) => {
|
|
|
7319
7413
|
const limit = query.limit || 20;
|
|
7320
7414
|
const offset = (page - 1) * limit;
|
|
7321
7415
|
const userTypeFilter = query.userType && query.userType !== "all" ? query.userType : null;
|
|
7322
|
-
const conditions = [
|
|
7416
|
+
const conditions = [eq59(usersInIam.tenantId, tenantId)];
|
|
7323
7417
|
if (userTypeFilter) {
|
|
7324
7418
|
conditions.push(
|
|
7325
7419
|
sql27`${usersInIam.userType} @> ARRAY[${userTypeFilter}]::text[]`
|
|
@@ -7347,19 +7441,19 @@ var listUsersHandler = async (c) => {
|
|
|
7347
7441
|
}
|
|
7348
7442
|
if (query.filter === "verified") {
|
|
7349
7443
|
const verifiedCond = or4(
|
|
7350
|
-
|
|
7351
|
-
|
|
7444
|
+
eq59(usersInIam.emailVerified, true),
|
|
7445
|
+
eq59(usersInIam.phoneVerified, true)
|
|
7352
7446
|
);
|
|
7353
7447
|
if (verifiedCond) {
|
|
7354
7448
|
conditions.push(verifiedCond);
|
|
7355
7449
|
}
|
|
7356
7450
|
} else if (query.filter === "unverified") {
|
|
7357
|
-
conditions.push(
|
|
7358
|
-
conditions.push(
|
|
7451
|
+
conditions.push(eq59(usersInIam.emailVerified, false));
|
|
7452
|
+
conditions.push(eq59(usersInIam.phoneVerified, false));
|
|
7359
7453
|
}
|
|
7360
7454
|
const orderDir = query.order === "asc" ? asc5 : desc5;
|
|
7361
7455
|
const sortCol = query.sort && sortColumnMap4[query.sort] ? sortColumnMap4[query.sort] : usersInIam.fullName;
|
|
7362
|
-
const whereClause =
|
|
7456
|
+
const whereClause = and54(...conditions);
|
|
7363
7457
|
const [users, totalResult] = await Promise.all([
|
|
7364
7458
|
database.select(userSelect).from(usersInIam).where(whereClause).orderBy(orderDir(sortCol)).limit(limit).offset(offset),
|
|
7365
7459
|
database.select({ count: sql27`count(*)` }).from(usersInIam).where(whereClause)
|
|
@@ -7370,10 +7464,10 @@ var listUsersHandler = async (c) => {
|
|
|
7370
7464
|
userId: sessionsInIam.userId,
|
|
7371
7465
|
count: sql27`count(*)::int`.as("count")
|
|
7372
7466
|
}).from(sessionsInIam).where(
|
|
7373
|
-
|
|
7374
|
-
|
|
7467
|
+
and54(
|
|
7468
|
+
eq59(sessionsInIam.tenantId, tenantId),
|
|
7375
7469
|
inArray6(sessionsInIam.userId, userIds),
|
|
7376
|
-
|
|
7470
|
+
gt9(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
7377
7471
|
)
|
|
7378
7472
|
).groupBy(sessionsInIam.userId) : [];
|
|
7379
7473
|
const sessionCountByUser = new Map(
|
|
@@ -7385,13 +7479,13 @@ var listUsersHandler = async (c) => {
|
|
|
7385
7479
|
name: rolesInIam.name
|
|
7386
7480
|
}).from(userRolesInIam).innerJoin(
|
|
7387
7481
|
rolesInIam,
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7482
|
+
and54(
|
|
7483
|
+
eq59(rolesInIam.tenantId, userRolesInIam.tenantId),
|
|
7484
|
+
eq59(rolesInIam.id, userRolesInIam.roleId)
|
|
7391
7485
|
)
|
|
7392
7486
|
).where(
|
|
7393
|
-
|
|
7394
|
-
|
|
7487
|
+
and54(
|
|
7488
|
+
eq59(userRolesInIam.tenantId, tenantId),
|
|
7395
7489
|
inArray6(userRolesInIam.userId, userIds)
|
|
7396
7490
|
)
|
|
7397
7491
|
) : [];
|
|
@@ -7421,13 +7515,13 @@ var listUsersHandler = async (c) => {
|
|
|
7421
7515
|
};
|
|
7422
7516
|
|
|
7423
7517
|
// src/routes/users/handler/search-users.ts
|
|
7424
|
-
import { and as
|
|
7518
|
+
import { and as and55, eq as eq60, ilike as ilike5, or as or5 } from "drizzle-orm";
|
|
7425
7519
|
var searchUsersHandler = async (c) => {
|
|
7426
7520
|
const query = c.req.valid("query");
|
|
7427
7521
|
const database = c.get("database");
|
|
7428
7522
|
const tenantId = c.get("tenantId");
|
|
7429
7523
|
const limit = query.limit || 20;
|
|
7430
|
-
const conditions = [
|
|
7524
|
+
const conditions = [eq60(usersInIam.tenantId, tenantId)];
|
|
7431
7525
|
if (query.search && query.search.trim().length > 0) {
|
|
7432
7526
|
const searchCondition = or5(
|
|
7433
7527
|
ilike5(usersInIam.fullName, `%${query.search}%`),
|
|
@@ -7445,25 +7539,25 @@ var searchUsersHandler = async (c) => {
|
|
|
7445
7539
|
phone: usersInIam.phone,
|
|
7446
7540
|
handle: usersInIam.handle,
|
|
7447
7541
|
image: usersInIam.image
|
|
7448
|
-
}).from(usersInIam).where(
|
|
7542
|
+
}).from(usersInIam).where(and55(...conditions)).limit(limit);
|
|
7449
7543
|
return c.json({ users }, 200);
|
|
7450
7544
|
};
|
|
7451
7545
|
|
|
7452
7546
|
// src/routes/users/handler/update-user.ts
|
|
7453
|
-
import { and as
|
|
7547
|
+
import { and as and56, eq as eq61, inArray as inArray7, sql as sql28 } from "drizzle-orm";
|
|
7454
7548
|
var updateUserHandler = async (c) => {
|
|
7455
7549
|
const { id } = c.req.valid("param");
|
|
7456
7550
|
const body = c.req.valid("json");
|
|
7457
7551
|
const database = c.get("database");
|
|
7458
7552
|
const tenantId = c.get("tenantId");
|
|
7459
|
-
const [existing] = await database.select().from(usersInIam).where(
|
|
7553
|
+
const [existing] = await database.select().from(usersInIam).where(and56(eq61(usersInIam.id, id), eq61(usersInIam.tenantId, tenantId))).limit(1);
|
|
7460
7554
|
if (!existing) {
|
|
7461
7555
|
return c.json({ error: "User not found" }, 404);
|
|
7462
7556
|
}
|
|
7463
7557
|
if (body.handle && body.handle !== existing.handle) {
|
|
7464
7558
|
const [handleExists] = await database.select().from(usersInIam).where(
|
|
7465
|
-
|
|
7466
|
-
|
|
7559
|
+
and56(
|
|
7560
|
+
eq61(usersInIam.tenantId, tenantId),
|
|
7467
7561
|
sql28`lower(${usersInIam.handle}) = lower(${body.handle})`
|
|
7468
7562
|
)
|
|
7469
7563
|
).limit(1);
|
|
@@ -7496,7 +7590,7 @@ var updateUserHandler = async (c) => {
|
|
|
7496
7590
|
const [updated] = await database.update(usersInIam).set({
|
|
7497
7591
|
...updateData,
|
|
7498
7592
|
updatedAt: sql28`CURRENT_TIMESTAMP`
|
|
7499
|
-
}).where(
|
|
7593
|
+
}).where(and56(eq61(usersInIam.id, id), eq61(usersInIam.tenantId, tenantId))).returning({
|
|
7500
7594
|
id: usersInIam.id,
|
|
7501
7595
|
tenantId: usersInIam.tenantId,
|
|
7502
7596
|
fullName: usersInIam.fullName,
|
|
@@ -7515,8 +7609,8 @@ var updateUserHandler = async (c) => {
|
|
|
7515
7609
|
const roleIds = body.roleIds;
|
|
7516
7610
|
if (roleIds.length > 0) {
|
|
7517
7611
|
const validRoles = await database.select({ id: rolesInIam.id, code: rolesInIam.code }).from(rolesInIam).where(
|
|
7518
|
-
|
|
7519
|
-
|
|
7612
|
+
and56(
|
|
7613
|
+
eq61(rolesInIam.tenantId, tenantId),
|
|
7520
7614
|
inArray7(rolesInIam.id, roleIds)
|
|
7521
7615
|
)
|
|
7522
7616
|
);
|
|
@@ -7525,9 +7619,9 @@ var updateUserHandler = async (c) => {
|
|
|
7525
7619
|
);
|
|
7526
7620
|
const toInsert = roleIds.filter((rid) => assignableIds.has(rid));
|
|
7527
7621
|
await database.delete(userRolesInIam).where(
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7622
|
+
and56(
|
|
7623
|
+
eq61(userRolesInIam.tenantId, tenantId),
|
|
7624
|
+
eq61(userRolesInIam.userId, id)
|
|
7531
7625
|
)
|
|
7532
7626
|
);
|
|
7533
7627
|
if (toInsert.length > 0) {
|
|
@@ -7541,9 +7635,9 @@ var updateUserHandler = async (c) => {
|
|
|
7541
7635
|
}
|
|
7542
7636
|
} else {
|
|
7543
7637
|
await database.delete(userRolesInIam).where(
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7638
|
+
and56(
|
|
7639
|
+
eq61(userRolesInIam.tenantId, tenantId),
|
|
7640
|
+
eq61(userRolesInIam.userId, id)
|
|
7547
7641
|
)
|
|
7548
7642
|
);
|
|
7549
7643
|
}
|
|
@@ -7957,31 +8051,31 @@ var users_route_default = userRoutes;
|
|
|
7957
8051
|
import { createRoute as createRoute15, OpenAPIHono as OpenAPIHono15 } from "@hono/zod-openapi";
|
|
7958
8052
|
|
|
7959
8053
|
// src/routes/verifications/handler/invalidate-verification.ts
|
|
7960
|
-
import { and as
|
|
8054
|
+
import { and as and57, eq as eq62 } from "drizzle-orm";
|
|
7961
8055
|
var invalidateVerificationHandler = async (c) => {
|
|
7962
8056
|
const { id } = c.req.valid("param");
|
|
7963
8057
|
const database = c.get("database");
|
|
7964
8058
|
const tenantId = c.get("tenantId");
|
|
7965
8059
|
const [existing] = await database.select().from(verificationsInIam).where(
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
8060
|
+
and57(
|
|
8061
|
+
eq62(verificationsInIam.id, id),
|
|
8062
|
+
eq62(verificationsInIam.tenantId, tenantId)
|
|
7969
8063
|
)
|
|
7970
8064
|
).limit(1);
|
|
7971
8065
|
if (!existing) {
|
|
7972
8066
|
return c.json({ error: "Verification not found" }, 404);
|
|
7973
8067
|
}
|
|
7974
8068
|
await database.delete(verificationsInIam).where(
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
|
|
8069
|
+
and57(
|
|
8070
|
+
eq62(verificationsInIam.id, id),
|
|
8071
|
+
eq62(verificationsInIam.tenantId, tenantId)
|
|
7978
8072
|
)
|
|
7979
8073
|
);
|
|
7980
8074
|
return c.json({ message: "Verification invalidated" }, 200);
|
|
7981
8075
|
};
|
|
7982
8076
|
|
|
7983
8077
|
// src/routes/verifications/handler/list-verifications.ts
|
|
7984
|
-
import { and as
|
|
8078
|
+
import { and as and58, eq as eq63, sql as sql29 } from "drizzle-orm";
|
|
7985
8079
|
var listVerificationsHandler = async (c) => {
|
|
7986
8080
|
const query = c.req.valid("query");
|
|
7987
8081
|
const database = c.get("database");
|
|
@@ -7989,12 +8083,12 @@ var listVerificationsHandler = async (c) => {
|
|
|
7989
8083
|
const page = query.page || 1;
|
|
7990
8084
|
const limit = query.limit || 20;
|
|
7991
8085
|
const offset = (page - 1) * limit;
|
|
7992
|
-
const conditions = [
|
|
8086
|
+
const conditions = [eq63(verificationsInIam.tenantId, tenantId)];
|
|
7993
8087
|
if (query.userId) {
|
|
7994
|
-
conditions.push(
|
|
8088
|
+
conditions.push(eq63(verificationsInIam.userId, query.userId));
|
|
7995
8089
|
}
|
|
7996
8090
|
if (query.type) {
|
|
7997
|
-
conditions.push(
|
|
8091
|
+
conditions.push(eq63(verificationsInIam.type, query.type));
|
|
7998
8092
|
}
|
|
7999
8093
|
if (query.status) {
|
|
8000
8094
|
if (query.status === "active") {
|
|
@@ -8008,8 +8102,8 @@ var listVerificationsHandler = async (c) => {
|
|
|
8008
8102
|
}
|
|
8009
8103
|
}
|
|
8010
8104
|
const [verifications, totalResult] = await Promise.all([
|
|
8011
|
-
database.select().from(verificationsInIam).where(
|
|
8012
|
-
database.select({ count: sql29`count(*)` }).from(verificationsInIam).where(
|
|
8105
|
+
database.select().from(verificationsInIam).where(and58(...conditions)).limit(limit).offset(offset),
|
|
8106
|
+
database.select({ count: sql29`count(*)` }).from(verificationsInIam).where(and58(...conditions))
|
|
8013
8107
|
]);
|
|
8014
8108
|
const total = Number(totalResult[0]?.count || 0);
|
|
8015
8109
|
return c.json({ verifications, total, page, limit }, 200);
|
|
@@ -8519,6 +8613,12 @@ var defaultAuthConfig = {
|
|
|
8519
8613
|
...defaultConfig,
|
|
8520
8614
|
phoneRegex: defaultPhoneRegex
|
|
8521
8615
|
},
|
|
8616
|
+
signUp: {
|
|
8617
|
+
enabled: true,
|
|
8618
|
+
emailEnabled: true,
|
|
8619
|
+
phoneEnabled: true,
|
|
8620
|
+
allowedEmailDomains: []
|
|
8621
|
+
},
|
|
8522
8622
|
security: {
|
|
8523
8623
|
maxLoginAttempts: 5,
|
|
8524
8624
|
lockoutDuration: "15m"
|