@mesob/auth-hono 0.0.3 → 0.0.4
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-CwcbaCwi.d.ts → index-BzGjOP5Z.d.ts} +0 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +762 -141
- package/dist/index.js.map +1 -1
- package/dist/lib/tenant.d.ts +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -467,8 +467,8 @@ var findUserById = (db, tenantId, userId) => {
|
|
|
467
467
|
|
|
468
468
|
// src/handler.ts
|
|
469
469
|
import { OpenAPIHono as OpenAPIHono2 } from "@hono/zod-openapi";
|
|
470
|
-
import { getCookie as
|
|
471
|
-
import { HTTPException as
|
|
470
|
+
import { getCookie as getCookie3 } from "hono/cookie";
|
|
471
|
+
import { HTTPException as HTTPException16 } from "hono/http-exception";
|
|
472
472
|
|
|
473
473
|
// src/lib/crypto.ts
|
|
474
474
|
import { scrypt } from "@noble/hashes/scrypt.js";
|
|
@@ -643,6 +643,9 @@ var changePasswordSchema = z.object({
|
|
|
643
643
|
currentPassword: passwordField,
|
|
644
644
|
newPassword: passwordField
|
|
645
645
|
});
|
|
646
|
+
var verifyPasswordSchema = z.object({
|
|
647
|
+
password: passwordField
|
|
648
|
+
});
|
|
646
649
|
var messageWithVerificationIdSchema = messageSchema.extend({
|
|
647
650
|
verificationId: z.string().uuid().optional()
|
|
648
651
|
});
|
|
@@ -652,9 +655,175 @@ var checkUserSchema = z.object({
|
|
|
652
655
|
var checkUserResponseSchema = z.object({
|
|
653
656
|
exists: z.boolean()
|
|
654
657
|
});
|
|
658
|
+
var updateProfileSchema = z.object({
|
|
659
|
+
fullName: z.string().min(1).max(255).optional().describe("User full name")
|
|
660
|
+
});
|
|
661
|
+
var updateEmailSchema = z.object({
|
|
662
|
+
email: z.string().email().describe("New email address")
|
|
663
|
+
});
|
|
664
|
+
var updatePhoneSchema = z.object({
|
|
665
|
+
phone: z.string().min(6).max(30).describe("New phone number")
|
|
666
|
+
});
|
|
667
|
+
var profileResponseSchema = z.object({
|
|
668
|
+
user: userSchema.describe("Updated user")
|
|
669
|
+
});
|
|
670
|
+
var pendingAccountChangeSchema = z.object({
|
|
671
|
+
changeType: z.enum(["email", "phone"]),
|
|
672
|
+
newEmail: z.string().email().nullable(),
|
|
673
|
+
newPhone: z.string().nullable(),
|
|
674
|
+
expiresAt: z.string().datetime()
|
|
675
|
+
});
|
|
676
|
+
var pendingAccountChangeResponseSchema = z.object({
|
|
677
|
+
accountChange: pendingAccountChangeSchema.nullable(),
|
|
678
|
+
verificationId: z.string().uuid().nullable()
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
// src/routes/handler/account-change-pending.ts
|
|
682
|
+
import { HTTPException as HTTPException2 } from "hono/http-exception";
|
|
683
|
+
|
|
684
|
+
// src/db/orm/iam/account-changes/expire-pending-account-changes.ts
|
|
685
|
+
import { and as and3, eq as eq3, lte } from "drizzle-orm";
|
|
686
|
+
var expirePendingAccountChanges = (db, tenantId, userId) => {
|
|
687
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
688
|
+
return db.update(accountChangesInIam).set({
|
|
689
|
+
status: "expired",
|
|
690
|
+
updatedAt: now
|
|
691
|
+
}).where(
|
|
692
|
+
and3(
|
|
693
|
+
eq3(accountChangesInIam.tenantId, tenantId),
|
|
694
|
+
eq3(accountChangesInIam.userId, userId),
|
|
695
|
+
eq3(accountChangesInIam.status, "pending"),
|
|
696
|
+
lte(accountChangesInIam.expiresAt, now)
|
|
697
|
+
)
|
|
698
|
+
);
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
// src/db/orm/iam/account-changes/find-pending-account-change.ts
|
|
702
|
+
import { and as and4, desc, eq as eq4, gt as gt2 } from "drizzle-orm";
|
|
703
|
+
var findPendingAccountChange = async (db, tenantId, userId) => {
|
|
704
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
705
|
+
return await db.select({
|
|
706
|
+
changeType: accountChangesInIam.changeType,
|
|
707
|
+
newEmail: accountChangesInIam.newEmail,
|
|
708
|
+
newPhone: accountChangesInIam.newPhone,
|
|
709
|
+
expiresAt: accountChangesInIam.expiresAt
|
|
710
|
+
}).from(accountChangesInIam).where(
|
|
711
|
+
and4(
|
|
712
|
+
eq4(accountChangesInIam.tenantId, tenantId),
|
|
713
|
+
eq4(accountChangesInIam.userId, userId),
|
|
714
|
+
eq4(accountChangesInIam.status, "pending"),
|
|
715
|
+
gt2(accountChangesInIam.expiresAt, now)
|
|
716
|
+
)
|
|
717
|
+
).orderBy(desc(accountChangesInIam.createdAt)).limit(1).then(([row]) => {
|
|
718
|
+
if (!row) {
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
if (row.changeType !== "email" && row.changeType !== "phone") {
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
return {
|
|
725
|
+
changeType: row.changeType,
|
|
726
|
+
newEmail: row.newEmail ?? null,
|
|
727
|
+
newPhone: row.newPhone ?? null,
|
|
728
|
+
expiresAt: row.expiresAt
|
|
729
|
+
};
|
|
730
|
+
});
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
// src/db/orm/iam/verifications/find-active-verification-id.ts
|
|
734
|
+
import { and as and5, desc as desc2, eq as eq5, gt as gt3 } from "drizzle-orm";
|
|
735
|
+
var findActiveVerificationId = async (db, tenantId, userId, type, to) => {
|
|
736
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
737
|
+
return await db.select({
|
|
738
|
+
verificationId: verificationsInIam.id,
|
|
739
|
+
expiresAt: verificationsInIam.expiresAt
|
|
740
|
+
}).from(verificationsInIam).where(
|
|
741
|
+
and5(
|
|
742
|
+
eq5(verificationsInIam.tenantId, tenantId),
|
|
743
|
+
eq5(verificationsInIam.userId, userId),
|
|
744
|
+
eq5(verificationsInIam.type, type),
|
|
745
|
+
eq5(verificationsInIam.to, to),
|
|
746
|
+
gt3(verificationsInIam.expiresAt, now)
|
|
747
|
+
)
|
|
748
|
+
).orderBy(desc2(verificationsInIam.createdAt)).limit(1).then(([row]) => row ? row : null);
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
// src/errors.ts
|
|
752
|
+
var AUTH_ERRORS = {
|
|
753
|
+
USER_NOT_FOUND: "USER_NOT_FOUND",
|
|
754
|
+
INVALID_PASSWORD: "INVALID_PASSWORD",
|
|
755
|
+
USER_EXISTS: "USER_EXISTS",
|
|
756
|
+
VERIFICATION_EXPIRED: "VERIFICATION_EXPIRED",
|
|
757
|
+
VERIFICATION_MISMATCH: "VERIFICATION_MISMATCH",
|
|
758
|
+
VERIFICATION_NOT_FOUND: "VERIFICATION_NOT_FOUND",
|
|
759
|
+
TOO_MANY_ATTEMPTS: "TOO_MANY_ATTEMPTS",
|
|
760
|
+
REQUIRES_VERIFICATION: "REQUIRES_VERIFICATION",
|
|
761
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
762
|
+
ACCESS_DENIED: "ACCESS_DENIED",
|
|
763
|
+
HAS_NO_PASSWORD: "HAS_NO_PASSWORD"
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
// src/lib/tenant.ts
|
|
767
|
+
import { HTTPException } from "hono/http-exception";
|
|
768
|
+
var ensureTenantId = (config, tenantId) => {
|
|
769
|
+
if (config.enableTenant) {
|
|
770
|
+
if (!tenantId) {
|
|
771
|
+
throw new HTTPException(400, {
|
|
772
|
+
message: "Missing tenantId. Tenant isolation is enabled."
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
return tenantId;
|
|
776
|
+
}
|
|
777
|
+
if (!config.tenantId) {
|
|
778
|
+
throw new HTTPException(500, {
|
|
779
|
+
message: "tenantId must be provided in config when enableTenant is false."
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
return config.tenantId;
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
// src/routes/handler/account-change-pending.ts
|
|
786
|
+
var accountChangePendingHandler = async (c) => {
|
|
787
|
+
const { config, database, tenantId, userId } = c.var;
|
|
788
|
+
if (!userId) {
|
|
789
|
+
throw new HTTPException2(401, { message: AUTH_ERRORS.UNAUTHORIZED });
|
|
790
|
+
}
|
|
791
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
792
|
+
await expirePendingAccountChanges(database, resolvedTenantId, userId);
|
|
793
|
+
const accountChange = await findPendingAccountChange(database, resolvedTenantId, userId);
|
|
794
|
+
if (!accountChange) {
|
|
795
|
+
return c.json({ accountChange: null, verificationId: null });
|
|
796
|
+
}
|
|
797
|
+
let verification = null;
|
|
798
|
+
if (accountChange.changeType === "email" && accountChange.newEmail) {
|
|
799
|
+
verification = await findActiveVerificationId(
|
|
800
|
+
database,
|
|
801
|
+
resolvedTenantId,
|
|
802
|
+
userId,
|
|
803
|
+
"email-verification",
|
|
804
|
+
accountChange.newEmail
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
if (accountChange.changeType === "phone" && accountChange.newPhone) {
|
|
808
|
+
verification = await findActiveVerificationId(
|
|
809
|
+
database,
|
|
810
|
+
resolvedTenantId,
|
|
811
|
+
userId,
|
|
812
|
+
"phone-otp-change-phone",
|
|
813
|
+
accountChange.newPhone
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
if (!verification) {
|
|
817
|
+
return c.json({ accountChange: null, verificationId: null });
|
|
818
|
+
}
|
|
819
|
+
return c.json({
|
|
820
|
+
accountChange,
|
|
821
|
+
verificationId: verification.verificationId
|
|
822
|
+
});
|
|
823
|
+
};
|
|
655
824
|
|
|
656
825
|
// src/db/orm/iam/users/find-user-by-email.ts
|
|
657
|
-
import { and as
|
|
826
|
+
import { and as and6, eq as eq6, sql as sql2 } from "drizzle-orm";
|
|
658
827
|
var findUserByEmail = (db, tenantId, email) => {
|
|
659
828
|
return db.select({
|
|
660
829
|
id: usersInIam.id,
|
|
@@ -668,15 +837,15 @@ var findUserByEmail = (db, tenantId, email) => {
|
|
|
668
837
|
phoneVerified: usersInIam.phoneVerified,
|
|
669
838
|
lastSignInAt: usersInIam.lastSignInAt
|
|
670
839
|
}).from(usersInIam).where(
|
|
671
|
-
|
|
672
|
-
|
|
840
|
+
and6(
|
|
841
|
+
eq6(usersInIam.tenantId, tenantId),
|
|
673
842
|
sql2`lower(${usersInIam.email}) = lower(${email})`
|
|
674
843
|
)
|
|
675
844
|
).limit(1).then(([user]) => user || null);
|
|
676
845
|
};
|
|
677
846
|
|
|
678
847
|
// src/db/orm/iam/users/find-user-by-phone.ts
|
|
679
|
-
import { and as
|
|
848
|
+
import { and as and7, eq as eq7 } from "drizzle-orm";
|
|
680
849
|
var findUserByPhone = (db, tenantId, phone) => {
|
|
681
850
|
return db.select({
|
|
682
851
|
id: usersInIam.id,
|
|
@@ -689,7 +858,7 @@ var findUserByPhone = (db, tenantId, phone) => {
|
|
|
689
858
|
emailVerified: usersInIam.emailVerified,
|
|
690
859
|
phoneVerified: usersInIam.phoneVerified,
|
|
691
860
|
lastSignInAt: usersInIam.lastSignInAt
|
|
692
|
-
}).from(usersInIam).where(
|
|
861
|
+
}).from(usersInIam).where(and7(eq7(usersInIam.tenantId, tenantId), eq7(usersInIam.phone, phone))).limit(1).then(([user]) => user || null);
|
|
693
862
|
};
|
|
694
863
|
|
|
695
864
|
// src/db/orm/iam/users/find-user-by-identifier.ts
|
|
@@ -703,25 +872,6 @@ var findUserByIdentifier = async (db, tenantId, identifier) => {
|
|
|
703
872
|
return { user, type: "phone" };
|
|
704
873
|
};
|
|
705
874
|
|
|
706
|
-
// src/lib/tenant.ts
|
|
707
|
-
import { HTTPException } from "hono/http-exception";
|
|
708
|
-
var ensureTenantId = (config, tenantId) => {
|
|
709
|
-
if (config.enableTenant) {
|
|
710
|
-
if (!tenantId) {
|
|
711
|
-
throw new HTTPException(400, {
|
|
712
|
-
message: "Missing tenantId. Tenant isolation is enabled."
|
|
713
|
-
});
|
|
714
|
-
}
|
|
715
|
-
return tenantId;
|
|
716
|
-
}
|
|
717
|
-
if (!config.tenantId) {
|
|
718
|
-
throw new HTTPException(500, {
|
|
719
|
-
message: "tenantId must be provided in config when enableTenant is false."
|
|
720
|
-
});
|
|
721
|
-
}
|
|
722
|
-
return config.tenantId;
|
|
723
|
-
};
|
|
724
|
-
|
|
725
875
|
// src/routes/handler/check-user.ts
|
|
726
876
|
var checkUserHandler = async (c) => {
|
|
727
877
|
const body = c.req.valid("json");
|
|
@@ -738,7 +888,7 @@ var checkUserHandler = async (c) => {
|
|
|
738
888
|
|
|
739
889
|
// src/routes/handler/email-verification-confirm.ts
|
|
740
890
|
import { setCookie } from "hono/cookie";
|
|
741
|
-
import { HTTPException as
|
|
891
|
+
import { HTTPException as HTTPException3 } from "hono/http-exception";
|
|
742
892
|
|
|
743
893
|
// src/db/orm/iam/sessions/insert-session.ts
|
|
744
894
|
var insertSession = (db, data) => {
|
|
@@ -763,22 +913,22 @@ var insertSession = (db, data) => {
|
|
|
763
913
|
};
|
|
764
914
|
|
|
765
915
|
// src/db/orm/iam/users/update-user-verified.ts
|
|
766
|
-
import { and as
|
|
916
|
+
import { and as and8, eq as eq8 } from "drizzle-orm";
|
|
767
917
|
var updateUserVerified = (db, tenantId, userId, type) => {
|
|
768
918
|
return db.update(usersInIam).set({
|
|
769
919
|
[type === "email" ? "emailVerified" : "phoneVerified"]: true,
|
|
770
920
|
lastSignInAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
771
|
-
}).where(
|
|
921
|
+
}).where(and8(eq8(usersInIam.id, userId), eq8(usersInIam.tenantId, tenantId)));
|
|
772
922
|
};
|
|
773
923
|
|
|
774
924
|
// src/db/orm/iam/verifications/consume-verification.ts
|
|
775
|
-
import { eq as
|
|
925
|
+
import { eq as eq9 } from "drizzle-orm";
|
|
776
926
|
var consumeVerification = (db, verificationId) => {
|
|
777
|
-
return db.delete(verificationsInIam).where(
|
|
927
|
+
return db.delete(verificationsInIam).where(eq9(verificationsInIam.id, verificationId));
|
|
778
928
|
};
|
|
779
929
|
|
|
780
930
|
// src/db/orm/iam/verifications/find-verification-by-id.ts
|
|
781
|
-
import { eq as
|
|
931
|
+
import { eq as eq10 } from "drizzle-orm";
|
|
782
932
|
var findVerificationById = (db, verificationId) => {
|
|
783
933
|
return db.select({
|
|
784
934
|
id: verificationsInIam.id,
|
|
@@ -790,32 +940,17 @@ var findVerificationById = (db, verificationId) => {
|
|
|
790
940
|
expiresAt: verificationsInIam.expiresAt,
|
|
791
941
|
createdAt: verificationsInIam.createdAt,
|
|
792
942
|
attempt: verificationsInIam.attempt
|
|
793
|
-
}).from(verificationsInIam).where(
|
|
943
|
+
}).from(verificationsInIam).where(eq10(verificationsInIam.id, verificationId)).limit(1).then(([verification]) => verification || null);
|
|
794
944
|
};
|
|
795
945
|
|
|
796
946
|
// src/db/orm/iam/verifications/update-verification-attempt.ts
|
|
797
|
-
import { eq as
|
|
947
|
+
import { eq as eq11 } from "drizzle-orm";
|
|
798
948
|
var updateVerificationAttempt = async (db, verificationId) => {
|
|
799
949
|
const verification = await findVerificationById(db, verificationId);
|
|
800
950
|
if (!verification) {
|
|
801
951
|
return;
|
|
802
952
|
}
|
|
803
|
-
await db.update(verificationsInIam).set({ attempt: (verification.attempt || 0) + 1 }).where(
|
|
804
|
-
};
|
|
805
|
-
|
|
806
|
-
// src/errors.ts
|
|
807
|
-
var AUTH_ERRORS = {
|
|
808
|
-
USER_NOT_FOUND: "USER_NOT_FOUND",
|
|
809
|
-
INVALID_PASSWORD: "INVALID_PASSWORD",
|
|
810
|
-
USER_EXISTS: "USER_EXISTS",
|
|
811
|
-
VERIFICATION_EXPIRED: "VERIFICATION_EXPIRED",
|
|
812
|
-
VERIFICATION_MISMATCH: "VERIFICATION_MISMATCH",
|
|
813
|
-
VERIFICATION_NOT_FOUND: "VERIFICATION_NOT_FOUND",
|
|
814
|
-
TOO_MANY_ATTEMPTS: "TOO_MANY_ATTEMPTS",
|
|
815
|
-
REQUIRES_VERIFICATION: "REQUIRES_VERIFICATION",
|
|
816
|
-
UNAUTHORIZED: "UNAUTHORIZED",
|
|
817
|
-
ACCESS_DENIED: "ACCESS_DENIED",
|
|
818
|
-
HAS_NO_PASSWORD: "HAS_NO_PASSWORD"
|
|
953
|
+
await db.update(verificationsInIam).set({ attempt: (verification.attempt || 0) + 1 }).where(eq11(verificationsInIam.id, verificationId));
|
|
819
954
|
};
|
|
820
955
|
|
|
821
956
|
// src/lib/session.ts
|
|
@@ -860,19 +995,19 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
860
995
|
const { verificationId, code } = body;
|
|
861
996
|
const verification = await findVerificationById(database, verificationId);
|
|
862
997
|
if (!verification) {
|
|
863
|
-
throw new
|
|
998
|
+
throw new HTTPException3(400, {
|
|
864
999
|
message: AUTH_ERRORS.VERIFICATION_NOT_FOUND
|
|
865
1000
|
});
|
|
866
1001
|
}
|
|
867
1002
|
if (new Date(verification.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
868
|
-
throw new
|
|
1003
|
+
throw new HTTPException3(400, {
|
|
869
1004
|
message: AUTH_ERRORS.VERIFICATION_EXPIRED
|
|
870
1005
|
});
|
|
871
1006
|
}
|
|
872
1007
|
const hashedCode = await hashToken(code, config.secret);
|
|
873
1008
|
if (verification.code !== hashedCode) {
|
|
874
1009
|
await updateVerificationAttempt(database, verificationId);
|
|
875
|
-
throw new
|
|
1010
|
+
throw new HTTPException3(400, {
|
|
876
1011
|
message: AUTH_ERRORS.VERIFICATION_MISMATCH
|
|
877
1012
|
});
|
|
878
1013
|
}
|
|
@@ -889,7 +1024,7 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
889
1024
|
verification.userId
|
|
890
1025
|
);
|
|
891
1026
|
if (!user) {
|
|
892
|
-
throw new
|
|
1027
|
+
throw new HTTPException3(500, { message: "User not found" });
|
|
893
1028
|
}
|
|
894
1029
|
const sessionToken = crypto.randomUUID();
|
|
895
1030
|
const hashedToken = await hashToken(sessionToken, config.secret);
|
|
@@ -925,16 +1060,52 @@ var emailVerificationConfirmHandler = async (c) => {
|
|
|
925
1060
|
};
|
|
926
1061
|
|
|
927
1062
|
// src/routes/handler/email-verification-request.ts
|
|
928
|
-
import { HTTPException as
|
|
1063
|
+
import { HTTPException as HTTPException4 } from "hono/http-exception";
|
|
1064
|
+
|
|
1065
|
+
// src/db/orm/iam/account-changes/cancel-pending-account-changes.ts
|
|
1066
|
+
import { and as and9, eq as eq12 } from "drizzle-orm";
|
|
1067
|
+
var cancelPendingAccountChanges = (db, tenantId, userId, changeType) => {
|
|
1068
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1069
|
+
return db.update(accountChangesInIam).set({
|
|
1070
|
+
status: "cancelled",
|
|
1071
|
+
cancelledAt: now,
|
|
1072
|
+
updatedAt: now,
|
|
1073
|
+
reason: "replaced"
|
|
1074
|
+
}).where(
|
|
1075
|
+
and9(
|
|
1076
|
+
eq12(accountChangesInIam.tenantId, tenantId),
|
|
1077
|
+
eq12(accountChangesInIam.userId, userId),
|
|
1078
|
+
eq12(accountChangesInIam.changeType, changeType),
|
|
1079
|
+
eq12(accountChangesInIam.status, "pending")
|
|
1080
|
+
)
|
|
1081
|
+
);
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
// src/db/orm/iam/account-changes/insert-pending-email-change.ts
|
|
1085
|
+
var insertPendingEmailChange = (db, data) => {
|
|
1086
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1087
|
+
return db.insert(accountChangesInIam).values({
|
|
1088
|
+
tenantId: data.tenantId,
|
|
1089
|
+
userId: data.userId,
|
|
1090
|
+
changeType: "email",
|
|
1091
|
+
oldEmail: data.oldEmail,
|
|
1092
|
+
newEmail: data.newEmail,
|
|
1093
|
+
oldPhone: null,
|
|
1094
|
+
newPhone: null,
|
|
1095
|
+
status: "pending",
|
|
1096
|
+
expiresAt: data.expiresAt,
|
|
1097
|
+
updatedAt: now
|
|
1098
|
+
});
|
|
1099
|
+
};
|
|
929
1100
|
|
|
930
1101
|
// src/db/orm/iam/verifications/delete-verifications-by-user-and-type.ts
|
|
931
|
-
import { and as
|
|
1102
|
+
import { and as and10, eq as eq13 } from "drizzle-orm";
|
|
932
1103
|
var deleteVerificationsByUserAndType = (db, tenantId, userId, type) => {
|
|
933
1104
|
return db.delete(verificationsInIam).where(
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1105
|
+
and10(
|
|
1106
|
+
eq13(verificationsInIam.tenantId, tenantId),
|
|
1107
|
+
eq13(verificationsInIam.userId, userId),
|
|
1108
|
+
eq13(verificationsInIam.type, type)
|
|
938
1109
|
)
|
|
939
1110
|
);
|
|
940
1111
|
};
|
|
@@ -999,8 +1170,6 @@ var ResendEmailProvider = class {
|
|
|
999
1170
|
}
|
|
1000
1171
|
async sendVerificationEmail(email, code, tenantName) {
|
|
1001
1172
|
const subject = this.config.verificationSubject || `Verify your email${tenantName ? ` for ${tenantName}` : ""}`;
|
|
1002
|
-
const verificationPath = this.config.verificationPath || "/verify-email";
|
|
1003
|
-
const verificationUrl = `${this.config.frontendBaseUrl}${verificationPath}?token=${code}`;
|
|
1004
1173
|
const html = `
|
|
1005
1174
|
<!DOCTYPE html>
|
|
1006
1175
|
<html>
|
|
@@ -1016,12 +1185,7 @@ var ResendEmailProvider = class {
|
|
|
1016
1185
|
<div style="background: #f3f4f6; padding: 20px; text-align: center; font-size: 32px; font-weight: bold; letter-spacing: 8px; margin: 20px 0;">
|
|
1017
1186
|
${code}
|
|
1018
1187
|
</div>
|
|
1019
|
-
|
|
1020
|
-
<p>
|
|
1021
|
-
<a href="${verificationUrl}" style="background: #2563eb; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; display: inline-block;">
|
|
1022
|
-
Verify Email
|
|
1023
|
-
</a>
|
|
1024
|
-
</p>
|
|
1188
|
+
|
|
1025
1189
|
<p style="color: #6b7280; font-size: 14px; margin-top: 30px;">
|
|
1026
1190
|
This code will expire in 1 hour. If you didn't request this, please ignore this email.
|
|
1027
1191
|
</p>
|
|
@@ -1031,8 +1195,6 @@ var ResendEmailProvider = class {
|
|
|
1031
1195
|
`;
|
|
1032
1196
|
const text2 = `Your verification code is: ${code}
|
|
1033
1197
|
|
|
1034
|
-
Or visit: ${verificationUrl}
|
|
1035
|
-
|
|
1036
1198
|
This code will expire in 1 hour.`;
|
|
1037
1199
|
await this.sendEmail({
|
|
1038
1200
|
to: [email],
|
|
@@ -1043,8 +1205,6 @@ This code will expire in 1 hour.`;
|
|
|
1043
1205
|
}
|
|
1044
1206
|
async sendPasswordResetEmail(email, code, tenantName) {
|
|
1045
1207
|
const subject = this.config.resetPasswordSubject || `Reset your password${tenantName ? ` for ${tenantName}` : ""}`;
|
|
1046
|
-
const resetPath = this.config.resetPasswordPath || "/reset-password";
|
|
1047
|
-
const resetUrl = `${this.config.frontendBaseUrl}${resetPath}?token=${code}`;
|
|
1048
1208
|
const html = `
|
|
1049
1209
|
<!DOCTYPE html>
|
|
1050
1210
|
<html>
|
|
@@ -1060,12 +1220,6 @@ This code will expire in 1 hour.`;
|
|
|
1060
1220
|
<div style="background: #f3f4f6; padding: 20px; text-align: center; font-size: 32px; font-weight: bold; letter-spacing: 8px; margin: 20px 0;">
|
|
1061
1221
|
${code}
|
|
1062
1222
|
</div>
|
|
1063
|
-
<p>Or click the link below:</p>
|
|
1064
|
-
<p>
|
|
1065
|
-
<a href="${resetUrl}" style="background: #2563eb; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; display: inline-block;">
|
|
1066
|
-
Reset Password
|
|
1067
|
-
</a>
|
|
1068
|
-
</p>
|
|
1069
1223
|
<p style="color: #6b7280; font-size: 14px; margin-top: 30px;">
|
|
1070
1224
|
This code will expire in 1 hour. If you didn't request this, please ignore this email.
|
|
1071
1225
|
</p>
|
|
@@ -1075,8 +1229,6 @@ This code will expire in 1 hour.`;
|
|
|
1075
1229
|
`;
|
|
1076
1230
|
const text2 = `Your password reset code is: ${code}
|
|
1077
1231
|
|
|
1078
|
-
Or visit: ${resetUrl}
|
|
1079
|
-
|
|
1080
1232
|
This code will expire in 1 hour.`;
|
|
1081
1233
|
await this.sendEmail({
|
|
1082
1234
|
to: [email],
|
|
@@ -1108,7 +1260,7 @@ var emailVerificationRequestHandler = async (c) => {
|
|
|
1108
1260
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
1109
1261
|
const email = body.email || user?.email;
|
|
1110
1262
|
if (!email) {
|
|
1111
|
-
throw new
|
|
1263
|
+
throw new HTTPException4(400, { message: "Email required" });
|
|
1112
1264
|
}
|
|
1113
1265
|
let userId = user?.id;
|
|
1114
1266
|
if (!userId) {
|
|
@@ -1118,7 +1270,7 @@ var emailVerificationRequestHandler = async (c) => {
|
|
|
1118
1270
|
email
|
|
1119
1271
|
);
|
|
1120
1272
|
if (!lookup.user) {
|
|
1121
|
-
throw new
|
|
1273
|
+
throw new HTTPException4(404, { message: AUTH_ERRORS.USER_NOT_FOUND });
|
|
1122
1274
|
}
|
|
1123
1275
|
userId = lookup.user.id;
|
|
1124
1276
|
}
|
|
@@ -1139,26 +1291,42 @@ var emailVerificationRequestHandler = async (c) => {
|
|
|
1139
1291
|
expiresAt,
|
|
1140
1292
|
to: email
|
|
1141
1293
|
});
|
|
1294
|
+
if (user?.id && body.email) {
|
|
1295
|
+
await cancelPendingAccountChanges(
|
|
1296
|
+
database,
|
|
1297
|
+
resolvedTenantId,
|
|
1298
|
+
user.id,
|
|
1299
|
+
"email"
|
|
1300
|
+
);
|
|
1301
|
+
await insertPendingEmailChange(database, {
|
|
1302
|
+
tenantId: resolvedTenantId,
|
|
1303
|
+
userId: user.id,
|
|
1304
|
+
oldEmail: user.email ?? "",
|
|
1305
|
+
newEmail: body.email,
|
|
1306
|
+
expiresAt: verification.expiresAt
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1142
1309
|
const emailProvider = new ResendEmailProvider(config.email.resend);
|
|
1143
1310
|
await emailProvider.sendVerificationEmail(email, code);
|
|
1144
1311
|
return c.json({ verificationId: verification.id });
|
|
1145
1312
|
};
|
|
1146
1313
|
|
|
1147
1314
|
// src/routes/handler/me.ts
|
|
1148
|
-
import { HTTPException as
|
|
1315
|
+
import { HTTPException as HTTPException5 } from "hono/http-exception";
|
|
1149
1316
|
var meHandler = (c) => {
|
|
1150
1317
|
const { user } = c.var;
|
|
1151
1318
|
if (!user) {
|
|
1152
|
-
throw new
|
|
1319
|
+
throw new HTTPException5(401, { message: "Unauthorized" });
|
|
1153
1320
|
}
|
|
1154
1321
|
return c.json({ user });
|
|
1155
1322
|
};
|
|
1156
1323
|
|
|
1157
1324
|
// src/routes/handler/password-change.ts
|
|
1158
|
-
import {
|
|
1325
|
+
import { getCookie } from "hono/cookie";
|
|
1326
|
+
import { HTTPException as HTTPException6 } from "hono/http-exception";
|
|
1159
1327
|
|
|
1160
1328
|
// src/db/orm/iam/accounts/find-account-by-provider.ts
|
|
1161
|
-
import { and as
|
|
1329
|
+
import { and as and11, eq as eq14 } from "drizzle-orm";
|
|
1162
1330
|
var findAccountByProvider = (db, tenantId, userId, provider) => {
|
|
1163
1331
|
return db.select({
|
|
1164
1332
|
id: accountsInIam.id,
|
|
@@ -1168,34 +1336,34 @@ var findAccountByProvider = (db, tenantId, userId, provider) => {
|
|
|
1168
1336
|
providerAccountId: accountsInIam.providerAccountId,
|
|
1169
1337
|
password: accountsInIam.password
|
|
1170
1338
|
}).from(accountsInIam).where(
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1339
|
+
and11(
|
|
1340
|
+
eq14(accountsInIam.tenantId, tenantId),
|
|
1341
|
+
eq14(accountsInIam.userId, userId),
|
|
1342
|
+
eq14(accountsInIam.provider, provider)
|
|
1175
1343
|
)
|
|
1176
1344
|
).limit(1).then(([account]) => account || null);
|
|
1177
1345
|
};
|
|
1178
1346
|
|
|
1179
1347
|
// src/db/orm/iam/accounts/update-account-password.ts
|
|
1180
|
-
import { and as
|
|
1348
|
+
import { and as and12, eq as eq15 } from "drizzle-orm";
|
|
1181
1349
|
var updateAccountPassword = (db, tenantId, userId, password) => {
|
|
1182
1350
|
return db.update(accountsInIam).set({ password }).where(
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1351
|
+
and12(
|
|
1352
|
+
eq15(accountsInIam.tenantId, tenantId),
|
|
1353
|
+
eq15(accountsInIam.userId, userId),
|
|
1354
|
+
eq15(accountsInIam.provider, "credentials")
|
|
1187
1355
|
)
|
|
1188
1356
|
);
|
|
1189
1357
|
};
|
|
1190
1358
|
|
|
1191
1359
|
// src/db/orm/iam/sessions/delete-session-by-id.ts
|
|
1192
|
-
import { eq as
|
|
1360
|
+
import { eq as eq16 } from "drizzle-orm";
|
|
1193
1361
|
var deleteSessionById = (db, sessionId) => {
|
|
1194
|
-
return db.delete(sessionsInIam).where(
|
|
1362
|
+
return db.delete(sessionsInIam).where(eq16(sessionsInIam.id, sessionId));
|
|
1195
1363
|
};
|
|
1196
1364
|
|
|
1197
1365
|
// src/db/orm/iam/sessions/list-sessions-for-user.ts
|
|
1198
|
-
import { and as
|
|
1366
|
+
import { and as and13, asc, eq as eq17, gt as gt4 } from "drizzle-orm";
|
|
1199
1367
|
var listSessionsForUser = (db, tenantId, userId) => {
|
|
1200
1368
|
return db.select({
|
|
1201
1369
|
id: sessionsInIam.id,
|
|
@@ -1207,10 +1375,10 @@ var listSessionsForUser = (db, tenantId, userId) => {
|
|
|
1207
1375
|
userAgent: sessionsInIam.userAgent,
|
|
1208
1376
|
ip: sessionsInIam.ip
|
|
1209
1377
|
}).from(sessionsInIam).where(
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1378
|
+
and13(
|
|
1379
|
+
eq17(sessionsInIam.tenantId, tenantId),
|
|
1380
|
+
eq17(sessionsInIam.userId, userId),
|
|
1381
|
+
gt4(sessionsInIam.expiresAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
1214
1382
|
)
|
|
1215
1383
|
).orderBy(asc(sessionsInIam.createdAt)).then((sessions) => sessions);
|
|
1216
1384
|
};
|
|
@@ -1220,10 +1388,10 @@ var changePasswordHandler = async (c) => {
|
|
|
1220
1388
|
const body = c.req.valid("json");
|
|
1221
1389
|
const { config, database, tenantId, userId, user } = c.var;
|
|
1222
1390
|
if (!userId) {
|
|
1223
|
-
throw new
|
|
1391
|
+
throw new HTTPException6(401, { message: AUTH_ERRORS.UNAUTHORIZED });
|
|
1224
1392
|
}
|
|
1225
1393
|
if (!user) {
|
|
1226
|
-
throw new
|
|
1394
|
+
throw new HTTPException6(401, { message: AUTH_ERRORS.UNAUTHORIZED });
|
|
1227
1395
|
}
|
|
1228
1396
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
1229
1397
|
const { currentPassword, newPassword } = body;
|
|
@@ -1234,11 +1402,11 @@ var changePasswordHandler = async (c) => {
|
|
|
1234
1402
|
"credentials"
|
|
1235
1403
|
);
|
|
1236
1404
|
if (!account?.password) {
|
|
1237
|
-
throw new
|
|
1405
|
+
throw new HTTPException6(401, { message: AUTH_ERRORS.HAS_NO_PASSWORD });
|
|
1238
1406
|
}
|
|
1239
1407
|
const passwordValid = await verifyPassword(currentPassword, account.password);
|
|
1240
1408
|
if (!passwordValid) {
|
|
1241
|
-
throw new
|
|
1409
|
+
throw new HTTPException6(401, { message: AUTH_ERRORS.INVALID_PASSWORD });
|
|
1242
1410
|
}
|
|
1243
1411
|
const passwordHash = await hashPassword(newPassword);
|
|
1244
1412
|
await updateAccountPassword(database, resolvedTenantId, userId, passwordHash);
|
|
@@ -1247,7 +1415,7 @@ var changePasswordHandler = async (c) => {
|
|
|
1247
1415
|
resolvedTenantId,
|
|
1248
1416
|
userId
|
|
1249
1417
|
);
|
|
1250
|
-
const currentSessionToken = c
|
|
1418
|
+
const currentSessionToken = getCookie(c, "session_token");
|
|
1251
1419
|
if (currentSessionToken) {
|
|
1252
1420
|
const hashedToken = await hashToken(currentSessionToken, config.secret);
|
|
1253
1421
|
const currentSession = await findSessionByToken(database, hashedToken);
|
|
@@ -1372,7 +1540,7 @@ var forgotPasswordHandler = async (c) => {
|
|
|
1372
1540
|
|
|
1373
1541
|
// src/routes/handler/password-reset.ts
|
|
1374
1542
|
import { setCookie as setCookie2 } from "hono/cookie";
|
|
1375
|
-
import { HTTPException as
|
|
1543
|
+
import { HTTPException as HTTPException7 } from "hono/http-exception";
|
|
1376
1544
|
var resetPasswordHandler = async (c) => {
|
|
1377
1545
|
const body = c.req.valid("json");
|
|
1378
1546
|
const { config, database, tenantId } = c.var;
|
|
@@ -1380,17 +1548,17 @@ var resetPasswordHandler = async (c) => {
|
|
|
1380
1548
|
const { verificationId, code, password } = body;
|
|
1381
1549
|
const verification = await findVerificationById(database, verificationId);
|
|
1382
1550
|
if (!verification) {
|
|
1383
|
-
throw new
|
|
1551
|
+
throw new HTTPException7(400, {
|
|
1384
1552
|
message: AUTH_ERRORS.VERIFICATION_NOT_FOUND
|
|
1385
1553
|
});
|
|
1386
1554
|
}
|
|
1387
1555
|
if (new Date(verification.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
1388
|
-
throw new
|
|
1556
|
+
throw new HTTPException7(400, { message: AUTH_ERRORS.VERIFICATION_EXPIRED });
|
|
1389
1557
|
}
|
|
1390
1558
|
const hashedCode = await hashToken(code, config.secret);
|
|
1391
1559
|
if (verification.code !== hashedCode) {
|
|
1392
1560
|
await updateVerificationAttempt(database, verificationId);
|
|
1393
|
-
throw new
|
|
1561
|
+
throw new HTTPException7(400, {
|
|
1394
1562
|
message: AUTH_ERRORS.VERIFICATION_MISMATCH
|
|
1395
1563
|
});
|
|
1396
1564
|
}
|
|
@@ -1428,7 +1596,7 @@ var resetPasswordHandler = async (c) => {
|
|
|
1428
1596
|
verification.userId
|
|
1429
1597
|
);
|
|
1430
1598
|
if (!user) {
|
|
1431
|
-
throw new
|
|
1599
|
+
throw new HTTPException7(500, { message: "User not found" });
|
|
1432
1600
|
}
|
|
1433
1601
|
setCookie2(c, "session_token", sessionToken, {
|
|
1434
1602
|
httpOnly: true,
|
|
@@ -1451,9 +1619,38 @@ var resetPasswordHandler = async (c) => {
|
|
|
1451
1619
|
});
|
|
1452
1620
|
};
|
|
1453
1621
|
|
|
1622
|
+
// src/routes/handler/password-verify.ts
|
|
1623
|
+
import { HTTPException as HTTPException8 } from "hono/http-exception";
|
|
1624
|
+
var verifyPasswordHandler = async (c) => {
|
|
1625
|
+
const body = c.req.valid("json");
|
|
1626
|
+
const { config, database, tenantId, userId, user } = c.var;
|
|
1627
|
+
if (!userId) {
|
|
1628
|
+
throw new HTTPException8(401, { message: AUTH_ERRORS.UNAUTHORIZED });
|
|
1629
|
+
}
|
|
1630
|
+
if (!user) {
|
|
1631
|
+
throw new HTTPException8(401, { message: AUTH_ERRORS.UNAUTHORIZED });
|
|
1632
|
+
}
|
|
1633
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
1634
|
+
const { password } = body;
|
|
1635
|
+
const account = await findAccountByProvider(
|
|
1636
|
+
database,
|
|
1637
|
+
resolvedTenantId,
|
|
1638
|
+
userId,
|
|
1639
|
+
"credentials"
|
|
1640
|
+
);
|
|
1641
|
+
if (!account?.password) {
|
|
1642
|
+
throw new HTTPException8(401, { message: AUTH_ERRORS.HAS_NO_PASSWORD });
|
|
1643
|
+
}
|
|
1644
|
+
const passwordValid = await verifyPassword(password, account.password);
|
|
1645
|
+
if (!passwordValid) {
|
|
1646
|
+
throw new HTTPException8(401, { message: AUTH_ERRORS.INVALID_PASSWORD });
|
|
1647
|
+
}
|
|
1648
|
+
return c.json({ message: "Password verified" });
|
|
1649
|
+
};
|
|
1650
|
+
|
|
1454
1651
|
// src/routes/handler/phone-verification-confirm.ts
|
|
1455
1652
|
import { setCookie as setCookie3 } from "hono/cookie";
|
|
1456
|
-
import { HTTPException as
|
|
1653
|
+
import { HTTPException as HTTPException9 } from "hono/http-exception";
|
|
1457
1654
|
var phoneVerificationConfirmHandler = async (c) => {
|
|
1458
1655
|
const body = c.req.valid("json");
|
|
1459
1656
|
const { config, database, tenantId } = c.var;
|
|
@@ -1461,17 +1658,17 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
1461
1658
|
const { verificationId, code, context } = body;
|
|
1462
1659
|
const verification = await findVerificationById(database, verificationId);
|
|
1463
1660
|
if (!verification) {
|
|
1464
|
-
throw new
|
|
1661
|
+
throw new HTTPException9(400, {
|
|
1465
1662
|
message: AUTH_ERRORS.VERIFICATION_NOT_FOUND
|
|
1466
1663
|
});
|
|
1467
1664
|
}
|
|
1468
1665
|
if (new Date(verification.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
1469
|
-
throw new
|
|
1666
|
+
throw new HTTPException9(400, { message: AUTH_ERRORS.VERIFICATION_EXPIRED });
|
|
1470
1667
|
}
|
|
1471
1668
|
const hashedCode = await hashToken(code, config.secret);
|
|
1472
1669
|
if (verification.code !== hashedCode) {
|
|
1473
1670
|
await updateVerificationAttempt(database, verificationId);
|
|
1474
|
-
throw new
|
|
1671
|
+
throw new HTTPException9(400, {
|
|
1475
1672
|
message: AUTH_ERRORS.VERIFICATION_MISMATCH
|
|
1476
1673
|
});
|
|
1477
1674
|
}
|
|
@@ -1500,7 +1697,7 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
1500
1697
|
}
|
|
1501
1698
|
const user = verification.userId ? await findUserById(database, resolvedTenantId, verification.userId) : null;
|
|
1502
1699
|
if (!user) {
|
|
1503
|
-
throw new
|
|
1700
|
+
throw new HTTPException9(500, { message: "User not found" });
|
|
1504
1701
|
}
|
|
1505
1702
|
if (context === "sign-in" || context === "change-phone" || context === "sign-up") {
|
|
1506
1703
|
const sessionToken = crypto.randomUUID();
|
|
@@ -1545,14 +1742,33 @@ var phoneVerificationConfirmHandler = async (c) => {
|
|
|
1545
1742
|
};
|
|
1546
1743
|
|
|
1547
1744
|
// src/routes/handler/phone-verification-request.ts
|
|
1548
|
-
import { HTTPException as
|
|
1745
|
+
import { HTTPException as HTTPException10 } from "hono/http-exception";
|
|
1746
|
+
|
|
1747
|
+
// src/db/orm/iam/account-changes/insert-pending-phone-change.ts
|
|
1748
|
+
var insertPendingPhoneChange = (db, data) => {
|
|
1749
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1750
|
+
return db.insert(accountChangesInIam).values({
|
|
1751
|
+
tenantId: data.tenantId,
|
|
1752
|
+
userId: data.userId,
|
|
1753
|
+
changeType: "phone",
|
|
1754
|
+
oldPhone: data.oldPhone,
|
|
1755
|
+
newPhone: data.newPhone,
|
|
1756
|
+
oldEmail: null,
|
|
1757
|
+
newEmail: null,
|
|
1758
|
+
status: "pending",
|
|
1759
|
+
expiresAt: data.expiresAt,
|
|
1760
|
+
updatedAt: now
|
|
1761
|
+
});
|
|
1762
|
+
};
|
|
1763
|
+
|
|
1764
|
+
// src/routes/handler/phone-verification-request.ts
|
|
1549
1765
|
var phoneVerificationRequestHandler = async (c) => {
|
|
1550
1766
|
const body = c.req.valid("json");
|
|
1551
1767
|
const { config, database, tenantId, user } = c.var;
|
|
1552
1768
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
1553
1769
|
const { phone, context } = body;
|
|
1554
1770
|
if (!phone) {
|
|
1555
|
-
throw new
|
|
1771
|
+
throw new HTTPException10(400, { message: "Phone required" });
|
|
1556
1772
|
}
|
|
1557
1773
|
let userId = user?.id;
|
|
1558
1774
|
if (!userId) {
|
|
@@ -1562,12 +1778,12 @@ var phoneVerificationRequestHandler = async (c) => {
|
|
|
1562
1778
|
phone
|
|
1563
1779
|
);
|
|
1564
1780
|
if (!lookup.user) {
|
|
1565
|
-
throw new
|
|
1781
|
+
throw new HTTPException10(404, { message: AUTH_ERRORS.USER_NOT_FOUND });
|
|
1566
1782
|
}
|
|
1567
1783
|
userId = lookup.user.id;
|
|
1568
1784
|
}
|
|
1569
1785
|
if (!userId) {
|
|
1570
|
-
throw new
|
|
1786
|
+
throw new HTTPException10(404, { message: AUTH_ERRORS.USER_NOT_FOUND });
|
|
1571
1787
|
}
|
|
1572
1788
|
await deleteVerificationsByUserAndType(
|
|
1573
1789
|
database,
|
|
@@ -1586,6 +1802,21 @@ var phoneVerificationRequestHandler = async (c) => {
|
|
|
1586
1802
|
expiresAt,
|
|
1587
1803
|
to: phone
|
|
1588
1804
|
});
|
|
1805
|
+
if (context === "change-phone" && user?.id) {
|
|
1806
|
+
await cancelPendingAccountChanges(
|
|
1807
|
+
database,
|
|
1808
|
+
resolvedTenantId,
|
|
1809
|
+
user.id,
|
|
1810
|
+
"phone"
|
|
1811
|
+
);
|
|
1812
|
+
await insertPendingPhoneChange(database, {
|
|
1813
|
+
tenantId: resolvedTenantId,
|
|
1814
|
+
userId: user.id,
|
|
1815
|
+
oldPhone: user.phone ?? "",
|
|
1816
|
+
newPhone: phone,
|
|
1817
|
+
expiresAt: verification.expiresAt
|
|
1818
|
+
});
|
|
1819
|
+
}
|
|
1589
1820
|
const smsProvider = new AfroSmsProvider(config.phone.smsConfig);
|
|
1590
1821
|
await smsProvider.sendVerificationSms(phone, code);
|
|
1591
1822
|
return c.json({ verificationId: verification.id });
|
|
@@ -1608,7 +1839,7 @@ var sessionHandler = (c) => {
|
|
|
1608
1839
|
|
|
1609
1840
|
// src/routes/handler/sign-in.ts
|
|
1610
1841
|
import { setCookie as setCookie4 } from "hono/cookie";
|
|
1611
|
-
import { HTTPException as
|
|
1842
|
+
import { HTTPException as HTTPException11 } from "hono/http-exception";
|
|
1612
1843
|
|
|
1613
1844
|
// src/db/orm/iam/sessions/delete-oldest-sessions.ts
|
|
1614
1845
|
var deleteOldestSessions = async (db, tenantId, userId, keepCount) => {
|
|
@@ -1623,9 +1854,9 @@ var deleteOldestSessions = async (db, tenantId, userId, keepCount) => {
|
|
|
1623
1854
|
};
|
|
1624
1855
|
|
|
1625
1856
|
// src/db/orm/iam/users/update-last-sign-in.ts
|
|
1626
|
-
import { and as
|
|
1857
|
+
import { and as and14, eq as eq18 } from "drizzle-orm";
|
|
1627
1858
|
var updateLastSignIn = (db, tenantId, userId) => {
|
|
1628
|
-
return db.update(usersInIam).set({ lastSignInAt: (/* @__PURE__ */ new Date()).toISOString(), loginAttempt: 0 }).where(
|
|
1859
|
+
return db.update(usersInIam).set({ lastSignInAt: (/* @__PURE__ */ new Date()).toISOString(), loginAttempt: 0 }).where(and14(eq18(usersInIam.id, userId), eq18(usersInIam.tenantId, tenantId)));
|
|
1629
1860
|
};
|
|
1630
1861
|
|
|
1631
1862
|
// src/routes/handler/sign-in.ts
|
|
@@ -1640,7 +1871,7 @@ var signInHandler = async (c) => {
|
|
|
1640
1871
|
identifier
|
|
1641
1872
|
);
|
|
1642
1873
|
if (!lookup.user) {
|
|
1643
|
-
throw new
|
|
1874
|
+
throw new HTTPException11(401, { message: AUTH_ERRORS.USER_NOT_FOUND });
|
|
1644
1875
|
}
|
|
1645
1876
|
const account = await findAccountByProvider(
|
|
1646
1877
|
database,
|
|
@@ -1649,13 +1880,14 @@ var signInHandler = async (c) => {
|
|
|
1649
1880
|
"credentials"
|
|
1650
1881
|
);
|
|
1651
1882
|
if (!account?.password) {
|
|
1652
|
-
throw new
|
|
1883
|
+
throw new HTTPException11(401, { message: AUTH_ERRORS.HAS_NO_PASSWORD });
|
|
1653
1884
|
}
|
|
1654
1885
|
const passwordValid = await verifyPassword(password, account.password);
|
|
1655
1886
|
if (!passwordValid) {
|
|
1656
|
-
throw new
|
|
1887
|
+
throw new HTTPException11(401, { message: AUTH_ERRORS.INVALID_PASSWORD });
|
|
1657
1888
|
}
|
|
1658
1889
|
const isEmail = lookup.type === "email";
|
|
1890
|
+
const isPhone = lookup.type === "phone";
|
|
1659
1891
|
const isVerified = isEmail ? lookup.user.emailVerified : lookup.user.phoneVerified;
|
|
1660
1892
|
if (isEmail && config.email.verificationRequired && !isVerified) {
|
|
1661
1893
|
const code = generateOtpCode(6);
|
|
@@ -1676,7 +1908,7 @@ var signInHandler = async (c) => {
|
|
|
1676
1908
|
requiresVerification: true
|
|
1677
1909
|
});
|
|
1678
1910
|
}
|
|
1679
|
-
if (
|
|
1911
|
+
if (isPhone && config.phone.verificationRequired && !isVerified) {
|
|
1680
1912
|
const code = generateOtpCode(config.phone.otpLength);
|
|
1681
1913
|
const hashedCode = await hashToken(code, config.secret);
|
|
1682
1914
|
const expiresAt2 = addDuration(config.phone.otpExpiresIn);
|
|
@@ -1738,11 +1970,11 @@ var signInHandler = async (c) => {
|
|
|
1738
1970
|
};
|
|
1739
1971
|
|
|
1740
1972
|
// src/routes/handler/sign-out.ts
|
|
1741
|
-
import { deleteCookie, getCookie } from "hono/cookie";
|
|
1973
|
+
import { deleteCookie, getCookie as getCookie2 } from "hono/cookie";
|
|
1742
1974
|
var signOutHandler = async (c) => {
|
|
1743
1975
|
const { config, database, tenantId } = c.var;
|
|
1744
1976
|
ensureTenantId(config, tenantId);
|
|
1745
|
-
const sessionToken =
|
|
1977
|
+
const sessionToken = getCookie2(c, "session_token");
|
|
1746
1978
|
if (!sessionToken) {
|
|
1747
1979
|
return c.json({ message: "Signed out" });
|
|
1748
1980
|
}
|
|
@@ -1763,7 +1995,7 @@ var signOutHandler = async (c) => {
|
|
|
1763
1995
|
|
|
1764
1996
|
// src/routes/handler/sign-up.ts
|
|
1765
1997
|
import { setCookie as setCookie5 } from "hono/cookie";
|
|
1766
|
-
import { HTTPException as
|
|
1998
|
+
import { HTTPException as HTTPException12 } from "hono/http-exception";
|
|
1767
1999
|
|
|
1768
2000
|
// src/db/orm/iam/accounts/insert-credentials-account.ts
|
|
1769
2001
|
var insertCredentialsAccount = (db, data) => {
|
|
@@ -1777,7 +2009,7 @@ var insertCredentialsAccount = (db, data) => {
|
|
|
1777
2009
|
};
|
|
1778
2010
|
|
|
1779
2011
|
// src/db/orm/iam/users/find-user-by-handle.ts
|
|
1780
|
-
import { and as
|
|
2012
|
+
import { and as and15, eq as eq19, sql as sql3 } from "drizzle-orm";
|
|
1781
2013
|
var findUserByHandle = (db, tenantId, handle) => {
|
|
1782
2014
|
return db.select({
|
|
1783
2015
|
id: usersInIam.id,
|
|
@@ -1791,8 +2023,8 @@ var findUserByHandle = (db, tenantId, handle) => {
|
|
|
1791
2023
|
phoneVerified: usersInIam.phoneVerified,
|
|
1792
2024
|
lastSignInAt: usersInIam.lastSignInAt
|
|
1793
2025
|
}).from(usersInIam).where(
|
|
1794
|
-
|
|
1795
|
-
|
|
2026
|
+
and15(
|
|
2027
|
+
eq19(usersInIam.tenantId, tenantId),
|
|
1796
2028
|
sql3`lower(${usersInIam.handle}) = lower(${handle})`
|
|
1797
2029
|
)
|
|
1798
2030
|
).limit(1).then(([user]) => user || null);
|
|
@@ -1830,7 +2062,7 @@ var signUpHandler = async (c) => {
|
|
|
1830
2062
|
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
1831
2063
|
const { email, phone, password, fullName, handle } = body;
|
|
1832
2064
|
if (!(email || phone)) {
|
|
1833
|
-
throw new
|
|
2065
|
+
throw new HTTPException12(400, {
|
|
1834
2066
|
message: "Either email or phone is required"
|
|
1835
2067
|
});
|
|
1836
2068
|
}
|
|
@@ -1841,7 +2073,7 @@ var signUpHandler = async (c) => {
|
|
|
1841
2073
|
identifier
|
|
1842
2074
|
);
|
|
1843
2075
|
if (existing.user) {
|
|
1844
|
-
throw new
|
|
2076
|
+
throw new HTTPException12(409, { message: AUTH_ERRORS.USER_EXISTS });
|
|
1845
2077
|
}
|
|
1846
2078
|
const userHandle = handle || generateHandle(email || phone);
|
|
1847
2079
|
const existingHandle = await findUserByHandle(
|
|
@@ -1850,7 +2082,7 @@ var signUpHandler = async (c) => {
|
|
|
1850
2082
|
userHandle
|
|
1851
2083
|
);
|
|
1852
2084
|
if (existingHandle) {
|
|
1853
|
-
throw new
|
|
2085
|
+
throw new HTTPException12(409, { message: "Handle already taken" });
|
|
1854
2086
|
}
|
|
1855
2087
|
const user = await insertUser(database, {
|
|
1856
2088
|
tenantId: resolvedTenantId,
|
|
@@ -1943,6 +2175,227 @@ var signUpHandler = async (c) => {
|
|
|
1943
2175
|
});
|
|
1944
2176
|
};
|
|
1945
2177
|
|
|
2178
|
+
// src/routes/handler/update-email.ts
|
|
2179
|
+
import { HTTPException as HTTPException13 } from "hono/http-exception";
|
|
2180
|
+
|
|
2181
|
+
// src/db/orm/iam/account-changes/mark-pending-account-change-applied.ts
|
|
2182
|
+
import { and as and16, eq as eq20 } from "drizzle-orm";
|
|
2183
|
+
var markPendingAccountChangeApplied = (db, tenantId, userId, changeType, newValue) => {
|
|
2184
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2185
|
+
const valueCondition = changeType === "email" ? eq20(accountChangesInIam.newEmail, newValue) : eq20(accountChangesInIam.newPhone, newValue);
|
|
2186
|
+
return db.update(accountChangesInIam).set({
|
|
2187
|
+
status: "applied",
|
|
2188
|
+
confirmedAt: now,
|
|
2189
|
+
updatedAt: now
|
|
2190
|
+
}).where(
|
|
2191
|
+
and16(
|
|
2192
|
+
eq20(accountChangesInIam.tenantId, tenantId),
|
|
2193
|
+
eq20(accountChangesInIam.userId, userId),
|
|
2194
|
+
eq20(accountChangesInIam.changeType, changeType),
|
|
2195
|
+
eq20(accountChangesInIam.status, "pending"),
|
|
2196
|
+
valueCondition
|
|
2197
|
+
)
|
|
2198
|
+
);
|
|
2199
|
+
};
|
|
2200
|
+
|
|
2201
|
+
// src/db/orm/iam/accounts/update-credentials-provider-account-id.ts
|
|
2202
|
+
import { and as and17, eq as eq21 } from "drizzle-orm";
|
|
2203
|
+
var updateCredentialsProviderAccountId = async (db, tenantId, userId, providerAccountId) => {
|
|
2204
|
+
const updated = await db.update(accountsInIam).set({ providerAccountId }).where(
|
|
2205
|
+
and17(
|
|
2206
|
+
eq21(accountsInIam.tenantId, tenantId),
|
|
2207
|
+
eq21(accountsInIam.userId, userId),
|
|
2208
|
+
eq21(accountsInIam.provider, "credentials")
|
|
2209
|
+
)
|
|
2210
|
+
).returning({ id: accountsInIam.id }).then(([row]) => row?.id);
|
|
2211
|
+
return Boolean(updated);
|
|
2212
|
+
};
|
|
2213
|
+
|
|
2214
|
+
// src/db/orm/iam/sessions/delete-other-sessions.ts
|
|
2215
|
+
import { and as and18, eq as eq22, ne } from "drizzle-orm";
|
|
2216
|
+
var deleteOtherSessions = (db, tenantId, userId, currentSessionId) => {
|
|
2217
|
+
return db.delete(sessionsInIam).where(
|
|
2218
|
+
and18(
|
|
2219
|
+
eq22(sessionsInIam.tenantId, tenantId),
|
|
2220
|
+
eq22(sessionsInIam.userId, userId),
|
|
2221
|
+
ne(sessionsInIam.id, currentSessionId)
|
|
2222
|
+
)
|
|
2223
|
+
);
|
|
2224
|
+
};
|
|
2225
|
+
|
|
2226
|
+
// src/db/orm/iam/users/update-user-email.ts
|
|
2227
|
+
import { and as and19, eq as eq23, sql as sql4 } from "drizzle-orm";
|
|
2228
|
+
var updateUserEmail = (db, tenantId, userId, email) => {
|
|
2229
|
+
return db.update(usersInIam).set({
|
|
2230
|
+
email,
|
|
2231
|
+
emailVerified: true,
|
|
2232
|
+
updatedAt: sql4`CURRENT_TIMESTAMP`
|
|
2233
|
+
}).where(and19(eq23(usersInIam.id, userId), eq23(usersInIam.tenantId, tenantId))).returning({
|
|
2234
|
+
id: usersInIam.id,
|
|
2235
|
+
tenantId: usersInIam.tenantId,
|
|
2236
|
+
fullName: usersInIam.fullName,
|
|
2237
|
+
email: usersInIam.email,
|
|
2238
|
+
phone: usersInIam.phone,
|
|
2239
|
+
handle: usersInIam.handle,
|
|
2240
|
+
image: usersInIam.image,
|
|
2241
|
+
emailVerified: usersInIam.emailVerified,
|
|
2242
|
+
phoneVerified: usersInIam.phoneVerified,
|
|
2243
|
+
lastSignInAt: usersInIam.lastSignInAt
|
|
2244
|
+
}).then(([user]) => user || null);
|
|
2245
|
+
};
|
|
2246
|
+
|
|
2247
|
+
// src/routes/handler/update-email.ts
|
|
2248
|
+
var updateEmailHandler = async (c) => {
|
|
2249
|
+
const body = c.req.valid("json");
|
|
2250
|
+
const { config, database, tenantId, userId, user, session } = c.var;
|
|
2251
|
+
if (!userId) {
|
|
2252
|
+
throw new HTTPException13(401, { message: AUTH_ERRORS.UNAUTHORIZED });
|
|
2253
|
+
}
|
|
2254
|
+
if (!user) {
|
|
2255
|
+
throw new HTTPException13(401, { message: AUTH_ERRORS.UNAUTHORIZED });
|
|
2256
|
+
}
|
|
2257
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
2258
|
+
if (user.email && session?.id) {
|
|
2259
|
+
await deleteOtherSessions(database, resolvedTenantId, userId, session.id);
|
|
2260
|
+
}
|
|
2261
|
+
const updatedUser = await updateUserEmail(
|
|
2262
|
+
database,
|
|
2263
|
+
resolvedTenantId,
|
|
2264
|
+
userId,
|
|
2265
|
+
body.email
|
|
2266
|
+
);
|
|
2267
|
+
if (!updatedUser) {
|
|
2268
|
+
throw new HTTPException13(404, { message: "User not found" });
|
|
2269
|
+
}
|
|
2270
|
+
await markPendingAccountChangeApplied(
|
|
2271
|
+
database,
|
|
2272
|
+
resolvedTenantId,
|
|
2273
|
+
userId,
|
|
2274
|
+
"email",
|
|
2275
|
+
body.email
|
|
2276
|
+
);
|
|
2277
|
+
await updateCredentialsProviderAccountId(
|
|
2278
|
+
database,
|
|
2279
|
+
resolvedTenantId,
|
|
2280
|
+
userId,
|
|
2281
|
+
body.email
|
|
2282
|
+
);
|
|
2283
|
+
return c.json({ user: updatedUser }, 200);
|
|
2284
|
+
};
|
|
2285
|
+
|
|
2286
|
+
// src/routes/handler/update-phone.ts
|
|
2287
|
+
import { HTTPException as HTTPException14 } from "hono/http-exception";
|
|
2288
|
+
|
|
2289
|
+
// src/db/orm/iam/users/update-user-phone.ts
|
|
2290
|
+
import { and as and20, eq as eq24, sql as sql5 } from "drizzle-orm";
|
|
2291
|
+
var updateUserPhone = (db, tenantId, userId, phone) => {
|
|
2292
|
+
return db.update(usersInIam).set({
|
|
2293
|
+
phone,
|
|
2294
|
+
phoneVerified: true,
|
|
2295
|
+
updatedAt: sql5`CURRENT_TIMESTAMP`
|
|
2296
|
+
}).where(and20(eq24(usersInIam.id, userId), eq24(usersInIam.tenantId, tenantId))).returning({
|
|
2297
|
+
id: usersInIam.id,
|
|
2298
|
+
tenantId: usersInIam.tenantId,
|
|
2299
|
+
fullName: usersInIam.fullName,
|
|
2300
|
+
email: usersInIam.email,
|
|
2301
|
+
phone: usersInIam.phone,
|
|
2302
|
+
handle: usersInIam.handle,
|
|
2303
|
+
image: usersInIam.image,
|
|
2304
|
+
emailVerified: usersInIam.emailVerified,
|
|
2305
|
+
phoneVerified: usersInIam.phoneVerified,
|
|
2306
|
+
lastSignInAt: usersInIam.lastSignInAt
|
|
2307
|
+
}).then(([user]) => user || null);
|
|
2308
|
+
};
|
|
2309
|
+
|
|
2310
|
+
// src/routes/handler/update-phone.ts
|
|
2311
|
+
var updatePhoneHandler = async (c) => {
|
|
2312
|
+
const body = c.req.valid("json");
|
|
2313
|
+
const { config, database, tenantId, userId, user, session } = c.var;
|
|
2314
|
+
if (!userId) {
|
|
2315
|
+
throw new HTTPException14(401, { message: AUTH_ERRORS.UNAUTHORIZED });
|
|
2316
|
+
}
|
|
2317
|
+
if (!user) {
|
|
2318
|
+
throw new HTTPException14(401, { message: AUTH_ERRORS.UNAUTHORIZED });
|
|
2319
|
+
}
|
|
2320
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
2321
|
+
if (user.phone && session?.id) {
|
|
2322
|
+
await deleteOtherSessions(database, resolvedTenantId, userId, session.id);
|
|
2323
|
+
}
|
|
2324
|
+
const updatedUser = await updateUserPhone(
|
|
2325
|
+
database,
|
|
2326
|
+
resolvedTenantId,
|
|
2327
|
+
userId,
|
|
2328
|
+
body.phone
|
|
2329
|
+
);
|
|
2330
|
+
if (!updatedUser) {
|
|
2331
|
+
throw new HTTPException14(404, { message: "User not found" });
|
|
2332
|
+
}
|
|
2333
|
+
await markPendingAccountChangeApplied(
|
|
2334
|
+
database,
|
|
2335
|
+
resolvedTenantId,
|
|
2336
|
+
userId,
|
|
2337
|
+
"phone",
|
|
2338
|
+
body.phone
|
|
2339
|
+
);
|
|
2340
|
+
await updateCredentialsProviderAccountId(
|
|
2341
|
+
database,
|
|
2342
|
+
resolvedTenantId,
|
|
2343
|
+
userId,
|
|
2344
|
+
body.phone
|
|
2345
|
+
);
|
|
2346
|
+
return c.json({ user: updatedUser }, 200);
|
|
2347
|
+
};
|
|
2348
|
+
|
|
2349
|
+
// src/routes/handler/update-profile.ts
|
|
2350
|
+
import { HTTPException as HTTPException15 } from "hono/http-exception";
|
|
2351
|
+
|
|
2352
|
+
// src/db/orm/iam/users/update-user-profile.ts
|
|
2353
|
+
import { and as and21, eq as eq25, sql as sql6 } from "drizzle-orm";
|
|
2354
|
+
var updateUserProfile = async (db, tenantId, userId, data) => {
|
|
2355
|
+
const updateData = {};
|
|
2356
|
+
if (data.fullName !== void 0) {
|
|
2357
|
+
updateData.fullName = data.fullName;
|
|
2358
|
+
}
|
|
2359
|
+
return await db.update(usersInIam).set({
|
|
2360
|
+
...updateData,
|
|
2361
|
+
updatedAt: sql6`CURRENT_TIMESTAMP`
|
|
2362
|
+
}).where(and21(eq25(usersInIam.id, userId), eq25(usersInIam.tenantId, tenantId))).returning({
|
|
2363
|
+
id: usersInIam.id,
|
|
2364
|
+
tenantId: usersInIam.tenantId,
|
|
2365
|
+
fullName: usersInIam.fullName,
|
|
2366
|
+
email: usersInIam.email,
|
|
2367
|
+
phone: usersInIam.phone,
|
|
2368
|
+
handle: usersInIam.handle,
|
|
2369
|
+
image: usersInIam.image,
|
|
2370
|
+
emailVerified: usersInIam.emailVerified,
|
|
2371
|
+
phoneVerified: usersInIam.phoneVerified,
|
|
2372
|
+
lastSignInAt: usersInIam.lastSignInAt
|
|
2373
|
+
}).then(([user]) => user || null);
|
|
2374
|
+
};
|
|
2375
|
+
|
|
2376
|
+
// src/routes/handler/update-profile.ts
|
|
2377
|
+
var updateProfileHandler = async (c) => {
|
|
2378
|
+
const body = c.req.valid("json");
|
|
2379
|
+
const { config, database, tenantId, userId, user } = c.var;
|
|
2380
|
+
if (!userId) {
|
|
2381
|
+
throw new HTTPException15(401, { message: AUTH_ERRORS.UNAUTHORIZED });
|
|
2382
|
+
}
|
|
2383
|
+
if (!user) {
|
|
2384
|
+
throw new HTTPException15(401, { message: AUTH_ERRORS.UNAUTHORIZED });
|
|
2385
|
+
}
|
|
2386
|
+
const resolvedTenantId = ensureTenantId(config, tenantId);
|
|
2387
|
+
const updatedUser = await updateUserProfile(
|
|
2388
|
+
database,
|
|
2389
|
+
resolvedTenantId,
|
|
2390
|
+
userId,
|
|
2391
|
+
body
|
|
2392
|
+
);
|
|
2393
|
+
if (!updatedUser) {
|
|
2394
|
+
throw new HTTPException15(404, { message: "User not found" });
|
|
2395
|
+
}
|
|
2396
|
+
return c.json({ user: updatedUser }, 200);
|
|
2397
|
+
};
|
|
2398
|
+
|
|
1946
2399
|
// src/routes/auth.route.ts
|
|
1947
2400
|
var signUpRoute = createRoute({
|
|
1948
2401
|
method: "post",
|
|
@@ -2088,6 +2541,26 @@ var sessionRoute = createRoute({
|
|
|
2088
2541
|
}
|
|
2089
2542
|
}
|
|
2090
2543
|
});
|
|
2544
|
+
var accountChangePendingRoute = createRoute({
|
|
2545
|
+
method: "get",
|
|
2546
|
+
path: "/account-change/pending",
|
|
2547
|
+
tags: ["Profile"],
|
|
2548
|
+
summary: "Get pending account change (email/phone)",
|
|
2549
|
+
responses: {
|
|
2550
|
+
200: {
|
|
2551
|
+
content: {
|
|
2552
|
+
"application/json": {
|
|
2553
|
+
schema: pendingAccountChangeResponseSchema
|
|
2554
|
+
}
|
|
2555
|
+
},
|
|
2556
|
+
description: "Pending account change"
|
|
2557
|
+
},
|
|
2558
|
+
401: {
|
|
2559
|
+
content: { "application/json": { schema: errorResponseSchema } },
|
|
2560
|
+
description: "Unauthorized"
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
});
|
|
2091
2564
|
var emailVerificationRequestRoute = createRoute({
|
|
2092
2565
|
method: "post",
|
|
2093
2566
|
path: "/email/verification/request",
|
|
@@ -2238,6 +2711,31 @@ var resetPasswordRoute = createRoute({
|
|
|
2238
2711
|
}
|
|
2239
2712
|
}
|
|
2240
2713
|
});
|
|
2714
|
+
var verifyPasswordRoute = createRoute({
|
|
2715
|
+
method: "post",
|
|
2716
|
+
path: "/password/verify",
|
|
2717
|
+
tags: ["Password"],
|
|
2718
|
+
summary: "Verify password",
|
|
2719
|
+
request: {
|
|
2720
|
+
body: {
|
|
2721
|
+
content: {
|
|
2722
|
+
"application/json": {
|
|
2723
|
+
schema: verifyPasswordSchema
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
},
|
|
2728
|
+
responses: {
|
|
2729
|
+
200: {
|
|
2730
|
+
content: { "application/json": { schema: messageSchema } },
|
|
2731
|
+
description: "Password verified"
|
|
2732
|
+
},
|
|
2733
|
+
401: {
|
|
2734
|
+
content: { "application/json": { schema: errorResponseSchema } },
|
|
2735
|
+
description: "Invalid password"
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
});
|
|
2241
2739
|
var changePasswordRoute = createRoute({
|
|
2242
2740
|
method: "post",
|
|
2243
2741
|
path: "/password/change",
|
|
@@ -2263,8 +2761,131 @@ var changePasswordRoute = createRoute({
|
|
|
2263
2761
|
}
|
|
2264
2762
|
}
|
|
2265
2763
|
});
|
|
2764
|
+
var updateProfileRoute = createRoute({
|
|
2765
|
+
method: "put",
|
|
2766
|
+
path: "/profile",
|
|
2767
|
+
tags: ["Profile"],
|
|
2768
|
+
summary: "Update user profile",
|
|
2769
|
+
request: {
|
|
2770
|
+
body: {
|
|
2771
|
+
content: {
|
|
2772
|
+
"application/json": {
|
|
2773
|
+
schema: updateProfileSchema
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
},
|
|
2778
|
+
responses: {
|
|
2779
|
+
200: {
|
|
2780
|
+
content: {
|
|
2781
|
+
"application/json": {
|
|
2782
|
+
schema: profileResponseSchema
|
|
2783
|
+
}
|
|
2784
|
+
},
|
|
2785
|
+
description: "Profile updated"
|
|
2786
|
+
},
|
|
2787
|
+
401: {
|
|
2788
|
+
content: {
|
|
2789
|
+
"application/json": {
|
|
2790
|
+
schema: errorResponseSchema
|
|
2791
|
+
}
|
|
2792
|
+
},
|
|
2793
|
+
description: "Unauthorized"
|
|
2794
|
+
},
|
|
2795
|
+
404: {
|
|
2796
|
+
content: {
|
|
2797
|
+
"application/json": {
|
|
2798
|
+
schema: errorResponseSchema
|
|
2799
|
+
}
|
|
2800
|
+
},
|
|
2801
|
+
description: "User not found"
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
});
|
|
2805
|
+
var updateEmailRoute = createRoute({
|
|
2806
|
+
method: "put",
|
|
2807
|
+
path: "/profile/email",
|
|
2808
|
+
tags: ["Profile"],
|
|
2809
|
+
summary: "Update user email",
|
|
2810
|
+
request: {
|
|
2811
|
+
body: {
|
|
2812
|
+
content: {
|
|
2813
|
+
"application/json": {
|
|
2814
|
+
schema: updateEmailSchema
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
},
|
|
2819
|
+
responses: {
|
|
2820
|
+
200: {
|
|
2821
|
+
content: {
|
|
2822
|
+
"application/json": {
|
|
2823
|
+
schema: profileResponseSchema
|
|
2824
|
+
}
|
|
2825
|
+
},
|
|
2826
|
+
description: "Email updated"
|
|
2827
|
+
},
|
|
2828
|
+
401: {
|
|
2829
|
+
content: {
|
|
2830
|
+
"application/json": {
|
|
2831
|
+
schema: errorResponseSchema
|
|
2832
|
+
}
|
|
2833
|
+
},
|
|
2834
|
+
description: "Unauthorized"
|
|
2835
|
+
},
|
|
2836
|
+
404: {
|
|
2837
|
+
content: {
|
|
2838
|
+
"application/json": {
|
|
2839
|
+
schema: errorResponseSchema
|
|
2840
|
+
}
|
|
2841
|
+
},
|
|
2842
|
+
description: "User not found"
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
});
|
|
2846
|
+
var updatePhoneRoute = createRoute({
|
|
2847
|
+
method: "put",
|
|
2848
|
+
path: "/profile/phone",
|
|
2849
|
+
tags: ["Profile"],
|
|
2850
|
+
summary: "Update user phone",
|
|
2851
|
+
request: {
|
|
2852
|
+
body: {
|
|
2853
|
+
content: {
|
|
2854
|
+
"application/json": {
|
|
2855
|
+
schema: updatePhoneSchema
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
},
|
|
2860
|
+
responses: {
|
|
2861
|
+
200: {
|
|
2862
|
+
content: {
|
|
2863
|
+
"application/json": {
|
|
2864
|
+
schema: profileResponseSchema
|
|
2865
|
+
}
|
|
2866
|
+
},
|
|
2867
|
+
description: "Phone updated"
|
|
2868
|
+
},
|
|
2869
|
+
401: {
|
|
2870
|
+
content: {
|
|
2871
|
+
"application/json": {
|
|
2872
|
+
schema: errorResponseSchema
|
|
2873
|
+
}
|
|
2874
|
+
},
|
|
2875
|
+
description: "Unauthorized"
|
|
2876
|
+
},
|
|
2877
|
+
404: {
|
|
2878
|
+
content: {
|
|
2879
|
+
"application/json": {
|
|
2880
|
+
schema: errorResponseSchema
|
|
2881
|
+
}
|
|
2882
|
+
},
|
|
2883
|
+
description: "User not found"
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
});
|
|
2266
2887
|
var createAuthRoutes = () => {
|
|
2267
|
-
const authRoutes = new OpenAPIHono().openapi(signUpRoute, signUpHandler).openapi(signInRoute, signInHandler).openapi(checkUserRoute, checkUserHandler).openapi(signOutRoute, signOutHandler).openapi(meRoute, meHandler).openapi(sessionRoute, sessionHandler).openapi(emailVerificationRequestRoute, emailVerificationRequestHandler).openapi(emailVerificationConfirmRoute, emailVerificationConfirmHandler).openapi(phoneVerificationRequestRoute, phoneVerificationRequestHandler).openapi(phoneVerificationConfirmRoute, phoneVerificationConfirmHandler).openapi(forgotPasswordRoute, forgotPasswordHandler).openapi(resetPasswordRoute, resetPasswordHandler).openapi(changePasswordRoute, changePasswordHandler);
|
|
2888
|
+
const authRoutes = new OpenAPIHono().openapi(signUpRoute, signUpHandler).openapi(signInRoute, signInHandler).openapi(checkUserRoute, checkUserHandler).openapi(signOutRoute, signOutHandler).openapi(meRoute, meHandler).openapi(sessionRoute, sessionHandler).openapi(accountChangePendingRoute, accountChangePendingHandler).openapi(emailVerificationRequestRoute, emailVerificationRequestHandler).openapi(emailVerificationConfirmRoute, emailVerificationConfirmHandler).openapi(phoneVerificationRequestRoute, phoneVerificationRequestHandler).openapi(phoneVerificationConfirmRoute, phoneVerificationConfirmHandler).openapi(forgotPasswordRoute, forgotPasswordHandler).openapi(resetPasswordRoute, resetPasswordHandler).openapi(verifyPasswordRoute, verifyPasswordHandler).openapi(changePasswordRoute, changePasswordHandler).openapi(updateProfileRoute, updateProfileHandler).openapi(updateEmailRoute, updateEmailHandler).openapi(updatePhoneRoute, updatePhoneHandler);
|
|
2268
2889
|
return authRoutes;
|
|
2269
2890
|
};
|
|
2270
2891
|
|
|
@@ -2272,18 +2893,18 @@ var createAuthRoutes = () => {
|
|
|
2272
2893
|
var createAuthMiddleware = (config, database, getTenantId) => {
|
|
2273
2894
|
const enableTenant = config.enableTenant ?? true;
|
|
2274
2895
|
return async (c, next) => {
|
|
2275
|
-
const sessionToken =
|
|
2896
|
+
const sessionToken = getCookie3(c, "session_token") || void 0;
|
|
2276
2897
|
let tenantId = getTenantId(c);
|
|
2277
2898
|
if (enableTenant) {
|
|
2278
2899
|
if (!tenantId) {
|
|
2279
|
-
throw new
|
|
2900
|
+
throw new HTTPException16(400, {
|
|
2280
2901
|
message: "Missing tenantId. Tenant isolation is enabled."
|
|
2281
2902
|
});
|
|
2282
2903
|
}
|
|
2283
2904
|
} else {
|
|
2284
2905
|
tenantId = config.tenantId;
|
|
2285
2906
|
if (!tenantId) {
|
|
2286
|
-
throw new
|
|
2907
|
+
throw new HTTPException16(500, {
|
|
2287
2908
|
message: "tenantId must be provided in config when enableTenant is false."
|
|
2288
2909
|
});
|
|
2289
2910
|
}
|