aaspai-authx 0.1.4 → 0.1.5

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.
@@ -562,7 +562,7 @@ var AuthAdminService = class {
562
562
  }
563
563
  async updateUserPassword(userId, newPassword) {
564
564
  const hashed = await import_bcrypt.default.hash(newPassword, 10);
565
- await OrgUser.findOneAndUpdate({ id: userId }, { password: hashed });
565
+ await OrgUser.findOneAndUpdate({ id: userId }, { passwordHash: hashed });
566
566
  }
567
567
  // -------------------------------------------------------------------
568
568
  // ADMIN TOKEN (self-issued JWT)
@@ -615,7 +615,6 @@ var EmailService = class {
615
615
  return import_jsonwebtoken3.default.verify(token, process.env.EMAIL_JWT_SECRET);
616
616
  }
617
617
  async send(to, subject, html) {
618
- console.log("[EmailService] Attempting to send:", { to, subject });
619
618
  try {
620
619
  const info = await this.transporter.sendMail({
621
620
  from: process.env.EMAIL_FROM,
@@ -643,18 +642,6 @@ var EmailService = class {
643
642
  }
644
643
  }
645
644
  canSend(lastEmailSent) {
646
- console.log(
647
- process.env.EMAIL_PASSWORD,
648
- "pssword",
649
- process.env.EMAIL_USER,
650
- "user",
651
- process.env.EMAIL_SECURE,
652
- "secure",
653
- process.env.EMAIL_PORT,
654
- "porat",
655
- process.env.EMAIL_HOST,
656
- "hosat"
657
- );
658
645
  const now = Date.now();
659
646
  const windowStart = now - this.WINDOW_MINUTES * 60 * 1e3;
660
647
  const emailsInWindow = (lastEmailSent || []).map((d) => new Date(d)).filter((d) => d.getTime() >= windowStart);
@@ -668,6 +655,386 @@ var EmailService = class {
668
655
  }
669
656
  };
670
657
 
658
+ // src/templates/email.templates.ts
659
+ var colors = {
660
+ background: "#0a0a0a",
661
+ cardBackground: "#111111",
662
+ cardBorder: "#1a1a1a",
663
+ accent: "#ffffff",
664
+ accentMuted: "rgba(255, 255, 255, 0.9)",
665
+ textPrimary: "#ffffff",
666
+ textSecondary: "rgba(255, 255, 255, 0.7)",
667
+ textMuted: "rgba(255, 255, 255, 0.5)",
668
+ divider: "rgba(255, 255, 255, 0.1)",
669
+ subtle: "#161616",
670
+ highlight: "rgba(255, 255, 255, 0.05)"
671
+ };
672
+ var styles = {
673
+ wrapper: `
674
+ margin: 0;
675
+ padding: 40px 20px;
676
+ background-color: ${colors.background};
677
+ background-image:
678
+ radial-gradient(ellipse at top, rgba(255,255,255,0.03) 0%, transparent 50%),
679
+ radial-gradient(ellipse at bottom, rgba(255,255,255,0.02) 0%, transparent 50%);
680
+ min-height: 100vh;
681
+ `,
682
+ container: `
683
+ max-width: 520px;
684
+ margin: 0 auto;
685
+ font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
686
+ color: ${colors.textPrimary};
687
+ line-height: 1.7;
688
+ `,
689
+ card: `
690
+ background-color: ${colors.cardBackground};
691
+ border: 1px solid ${colors.cardBorder};
692
+ border-radius: 16px;
693
+ overflow: hidden;
694
+ box-shadow:
695
+ 0 0 0 1px rgba(255,255,255,0.05),
696
+ 0 20px 50px -20px rgba(0,0,0,0.5),
697
+ 0 30px 60px -30px rgba(0,0,0,0.3);
698
+ `,
699
+ header: `
700
+ padding: 48px 40px 32px;
701
+ text-align: center;
702
+ border-bottom: 1px solid ${colors.divider};
703
+ `,
704
+ iconWrapper: `
705
+ width: 64px;
706
+ height: 64px;
707
+ margin: 0 auto 24px;
708
+ background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);
709
+ border: 1px solid rgba(255,255,255,0.1);
710
+ border-radius: 16px;
711
+ display: flex;
712
+ align-items: center;
713
+ justify-content: center;
714
+ font-size: 28px;
715
+ `,
716
+ headerTitle: `
717
+ color: ${colors.textPrimary};
718
+ margin: 0;
719
+ font-size: 24px;
720
+ font-weight: 600;
721
+ letter-spacing: -0.5px;
722
+ `,
723
+ headerSubtitle: `
724
+ color: ${colors.textMuted};
725
+ margin: 8px 0 0;
726
+ font-size: 14px;
727
+ font-weight: 400;
728
+ `,
729
+ body: `
730
+ padding: 40px;
731
+ `,
732
+ greeting: `
733
+ margin: 0 0 24px;
734
+ color: ${colors.textPrimary};
735
+ font-size: 18px;
736
+ font-weight: 500;
737
+ `,
738
+ paragraph: `
739
+ margin: 0 0 20px;
740
+ color: ${colors.textSecondary};
741
+ font-size: 15px;
742
+ line-height: 1.7;
743
+ `,
744
+ buttonWrapper: `
745
+ text-align: center;
746
+ margin: 32px 0;
747
+ `,
748
+ button: `
749
+ display: inline-block;
750
+ background-color: ${colors.accent};
751
+ color: #000000 !important;
752
+ text-decoration: none;
753
+ padding: 14px 36px;
754
+ border-radius: 8px;
755
+ font-weight: 600;
756
+ font-size: 14px;
757
+ letter-spacing: 0.3px;
758
+ transition: all 0.2s ease;
759
+ `,
760
+ secondaryButton: `
761
+ display: inline-block;
762
+ background-color: transparent;
763
+ color: ${colors.textPrimary} !important;
764
+ text-decoration: none;
765
+ padding: 12px 28px;
766
+ border-radius: 8px;
767
+ font-weight: 500;
768
+ font-size: 14px;
769
+ border: 1px solid ${colors.divider};
770
+ `,
771
+ infoCard: `
772
+ background-color: ${colors.subtle};
773
+ border: 1px solid ${colors.divider};
774
+ border-radius: 12px;
775
+ padding: 20px 24px;
776
+ margin: 28px 0;
777
+ `,
778
+ infoCardTitle: `
779
+ margin: 0 0 12px;
780
+ color: ${colors.textPrimary};
781
+ font-size: 13px;
782
+ font-weight: 600;
783
+ text-transform: uppercase;
784
+ letter-spacing: 0.5px;
785
+ `,
786
+ infoCardText: `
787
+ margin: 0;
788
+ color: ${colors.textSecondary};
789
+ font-size: 14px;
790
+ line-height: 1.6;
791
+ `,
792
+ warningCard: `
793
+ background: linear-gradient(135deg, rgba(255,180,0,0.1) 0%, rgba(255,140,0,0.05) 100%);
794
+ border: 1px solid rgba(255,180,0,0.2);
795
+ border-radius: 12px;
796
+ padding: 20px 24px;
797
+ margin: 28px 0;
798
+ `,
799
+ warningCardTitle: `
800
+ margin: 0 0 12px;
801
+ color: #ffc107;
802
+ font-size: 13px;
803
+ font-weight: 600;
804
+ text-transform: uppercase;
805
+ letter-spacing: 0.5px;
806
+ `,
807
+ warningCardText: `
808
+ margin: 0;
809
+ color: rgba(255,255,255,0.7);
810
+ font-size: 14px;
811
+ line-height: 1.6;
812
+ `,
813
+ successCard: `
814
+ background: linear-gradient(135deg, rgba(0,255,150,0.1) 0%, rgba(0,200,100,0.05) 100%);
815
+ border: 1px solid rgba(0,255,150,0.2);
816
+ border-radius: 12px;
817
+ padding: 20px 24px;
818
+ margin: 28px 0;
819
+ `,
820
+ successCardTitle: `
821
+ margin: 0 0 12px;
822
+ color: #00ff96;
823
+ font-size: 13px;
824
+ font-weight: 600;
825
+ text-transform: uppercase;
826
+ letter-spacing: 0.5px;
827
+ `,
828
+ linkSection: `
829
+ margin: 32px 0;
830
+ padding: 20px;
831
+ background-color: ${colors.subtle};
832
+ border-radius: 8px;
833
+ border: 1px solid ${colors.divider};
834
+ `,
835
+ linkLabel: `
836
+ margin: 0 0 8px;
837
+ color: ${colors.textMuted};
838
+ font-size: 12px;
839
+ text-transform: uppercase;
840
+ letter-spacing: 0.5px;
841
+ `,
842
+ linkText: `
843
+ word-break: break-all;
844
+ color: ${colors.textSecondary};
845
+ font-size: 13px;
846
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;
847
+ margin: 0;
848
+ `,
849
+ divider: `
850
+ border: none;
851
+ border-top: 1px solid ${colors.divider};
852
+ margin: 32px 0;
853
+ `,
854
+ footer: `
855
+ padding: 24px 40px 32px;
856
+ text-align: center;
857
+ border-top: 1px solid ${colors.divider};
858
+ `,
859
+ footerText: `
860
+ margin: 0;
861
+ color: ${colors.textMuted};
862
+ font-size: 12px;
863
+ line-height: 1.8;
864
+ `,
865
+ footerLink: `
866
+ color: ${colors.textSecondary};
867
+ text-decoration: none;
868
+ `,
869
+ badge: `
870
+ display: inline-block;
871
+ background-color: rgba(255,255,255,0.1);
872
+ color: ${colors.textSecondary};
873
+ padding: 4px 12px;
874
+ border-radius: 20px;
875
+ font-size: 12px;
876
+ font-weight: 500;
877
+ letter-spacing: 0.3px;
878
+ `,
879
+ listItem: `
880
+ color: ${colors.textSecondary};
881
+ font-size: 14px;
882
+ margin: 8px 0;
883
+ padding-left: 8px;
884
+ `,
885
+ metaRow: `
886
+ display: flex;
887
+ justify-content: space-between;
888
+ padding: 12px 0;
889
+ border-bottom: 1px solid ${colors.divider};
890
+ `,
891
+ metaLabel: `
892
+ color: ${colors.textMuted};
893
+ font-size: 13px;
894
+ `,
895
+ metaValue: `
896
+ color: ${colors.textPrimary};
897
+ font-size: 13px;
898
+ font-weight: 500;
899
+ `
900
+ };
901
+ function buildVerificationEmailTemplate(data) {
902
+ const { firstName, verificationUrl, expiresIn } = data;
903
+ return `
904
+ <!DOCTYPE html>
905
+ <html lang="en">
906
+ <head>
907
+ <meta charset="UTF-8">
908
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
909
+ <meta name="color-scheme" content="dark">
910
+ <meta name="supported-color-schemes" content="dark">
911
+ <title>Verify Your Email</title>
912
+ <!--[if mso]>
913
+ <style type="text/css">
914
+ body, table, td {font-family: Arial, Helvetica, sans-serif !important;}
915
+ </style>
916
+ <![endif]-->
917
+ </head>
918
+ <body style="${styles.wrapper}">
919
+ <div style="${styles.container}">
920
+ <div style="${styles.card}">
921
+ <!-- Header -->
922
+ <div style="${styles.header}">
923
+ <div style="${styles.iconWrapper}">
924
+ \u2709\uFE0F
925
+ </div>
926
+ <h1 style="${styles.headerTitle}">Verify your email</h1>
927
+ <p style="${styles.headerSubtitle}">One quick step to get started</p>
928
+ </div>
929
+
930
+ <!-- Body -->
931
+ <div style="${styles.body}">
932
+ <p style="${styles.greeting}">Hi ${firstName},</p>
933
+
934
+ <p style="${styles.paragraph}">
935
+ Thanks for signing up. To complete your registration and unlock all features,
936
+ please verify your email address by clicking the button below.
937
+ </p>
938
+
939
+ <div style="${styles.buttonWrapper}">
940
+ <a href="${verificationUrl}" style="${styles.button}" target="_blank">
941
+ Verify Email Address
942
+ </a>
943
+ </div>
944
+
945
+ <div style="${styles.infoCard}">
946
+ <p style="${styles.infoCardTitle}">\u23F1 Time Sensitive</p>
947
+ <p style="${styles.infoCardText}">
948
+ This verification link will expire in <strong>${expiresIn}</strong>.
949
+ If you didn't create an account, you can safely ignore this email.
950
+ </p>
951
+ </div>
952
+ </div>
953
+
954
+ <!-- Footer -->
955
+ <div style="${styles.footer}">
956
+ <p style="${styles.footerText}">
957
+ This is an automated message \u2014 please do not reply.<br>
958
+ \xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} All rights reserved.
959
+ </p>
960
+ </div>
961
+ </div>
962
+ </div>
963
+ </body>
964
+ </html>
965
+ `;
966
+ }
967
+ function buildResetPasswordEmailTemplate(data) {
968
+ const { firstName, resetUrl, expiresIn } = data;
969
+ return `
970
+ <!DOCTYPE html>
971
+ <html lang="en">
972
+ <head>
973
+ <meta charset="UTF-8">
974
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
975
+ <meta name="color-scheme" content="dark">
976
+ <meta name="supported-color-schemes" content="dark">
977
+ <title>Reset Your Password</title>
978
+ </head>
979
+ <body style="${styles.wrapper}">
980
+ <div style="${styles.container}">
981
+ <div style="${styles.card}">
982
+ <!-- Header -->
983
+ <div style="${styles.header}">
984
+ <div style="${styles.iconWrapper}">
985
+ \u{1F510}
986
+ </div>
987
+ <h1 style="${styles.headerTitle}">Reset your password</h1>
988
+ <p style="${styles.headerSubtitle}">We received a reset request</p>
989
+ </div>
990
+
991
+ <!-- Body -->
992
+ <div style="${styles.body}">
993
+ <p style="${styles.greeting}">Hi ${firstName},</p>
994
+
995
+ <p style="${styles.paragraph}">
996
+ We received a request to reset the password for your account.
997
+ Click the button below to create a new password.
998
+ </p>
999
+
1000
+ <div style="${styles.buttonWrapper}">
1001
+ <a href="${resetUrl}" style="${styles.button}" target="_blank">
1002
+ Reset Password
1003
+ </a>
1004
+ </div>
1005
+
1006
+ <div style="${styles.warningCard}">
1007
+ <p style="${styles.warningCardTitle}">\u26A0\uFE0F Security Notice</p>
1008
+ <p style="${styles.warningCardText}">
1009
+ \u2022 This link expires in <strong>${expiresIn}</strong><br>
1010
+ \u2022 This link can only be used once<br>
1011
+ \u2022 If you didn't request this, ignore this email
1012
+ </p>
1013
+ </div>
1014
+
1015
+ <hr style="${styles.divider}" />
1016
+
1017
+ <p style="${styles.paragraph}; font-size: 13px; color: ${colors.textMuted};">
1018
+ <strong>Didn't request this?</strong><br>
1019
+ Your password remains unchanged. If you're concerned about your account
1020
+ security, please contact our support team immediately.
1021
+ </p>
1022
+ </div>
1023
+
1024
+ <!-- Footer -->
1025
+ <div style="${styles.footer}">
1026
+ <p style="${styles.footerText}">
1027
+ This is an automated message \u2014 please do not reply.<br>
1028
+ \xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} All rights reserved.
1029
+ </p>
1030
+ </div>
1031
+ </div>
1032
+ </div>
1033
+ </body>
1034
+ </html>
1035
+ `;
1036
+ }
1037
+
671
1038
  // src/express/auth.routes.ts
672
1039
  function createAuthRouter(options = {}) {
673
1040
  const googleClientId = process.env.GOOGLE_CLIENT_ID;
@@ -774,10 +1141,20 @@ function createAuthRouter(options = {}) {
774
1141
  emailService: email,
775
1142
  user,
776
1143
  subject: "Verify your email",
777
- html: buildVerificationTemplate(
778
- email.sign({ userId: kcUser.id, email: kcUser.email }),
779
- options
780
- )
1144
+ // html: buildVerificationTemplate(
1145
+ // email.sign({ userId: kcUser.id, email: kcUser.email }),
1146
+ // options,
1147
+ // ),
1148
+ html: buildVerificationEmailTemplate({
1149
+ firstName: user.firstName,
1150
+ verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(
1151
+ {
1152
+ userId: user.id,
1153
+ email: user.email
1154
+ }
1155
+ )}`,
1156
+ expiresIn: "1 hour"
1157
+ })
781
1158
  });
782
1159
  if (emailResult.rateLimited) {
783
1160
  return res.status(429).json({
@@ -857,7 +1234,17 @@ function createAuthRouter(options = {}) {
857
1234
  emailService: email,
858
1235
  user,
859
1236
  subject: "Verify your email",
860
- html: buildVerificationTemplate(token, options)
1237
+ // html: buildVerificationTemplate(token, options),
1238
+ html: buildVerificationEmailTemplate({
1239
+ firstName: user.firstName,
1240
+ verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(
1241
+ {
1242
+ userId: user.id,
1243
+ email: user.email
1244
+ }
1245
+ )}`,
1246
+ expiresIn: "1 hour"
1247
+ })
861
1248
  });
862
1249
  if (resendResult.rateLimited) {
863
1250
  return res.status(429).json({
@@ -886,7 +1273,17 @@ function createAuthRouter(options = {}) {
886
1273
  emailService: email,
887
1274
  user,
888
1275
  subject: "Reset password",
889
- html: buildResetTemplate(resetToken, options)
1276
+ // html: buildResetTemplate(resetToken, options),
1277
+ html: buildResetPasswordEmailTemplate({
1278
+ firstName: user.firstName,
1279
+ resetUrl: `${getFrontendBaseUrl(options)}/reset-password?token=${email.sign(
1280
+ {
1281
+ userId: user.id,
1282
+ email: user.email
1283
+ }
1284
+ )}`,
1285
+ expiresIn: "1 hour"
1286
+ })
890
1287
  });
891
1288
  if (resetResult.rateLimited) {
892
1289
  return res.status(429).json({
@@ -899,9 +1296,16 @@ function createAuthRouter(options = {}) {
899
1296
  });
900
1297
  r.post("/reset-password", validateResetPassword, async (req, res) => {
901
1298
  const { token, newPassword } = req.body || {};
1299
+ if (!token || !newPassword) {
1300
+ return res.status(400).json({
1301
+ ok: false,
1302
+ error: "Token and new password are required",
1303
+ code: "MISSING_FIELDS"
1304
+ });
1305
+ }
902
1306
  try {
903
1307
  const payload = email.verify(token);
904
- const user = await OrgUser.findOne({ keycloakId: payload.userId });
1308
+ const user = await OrgUser.findOne({ id: payload.userId });
905
1309
  if (!user) {
906
1310
  return res.status(404).json({ ok: false, error: "User not found" });
907
1311
  }
@@ -1269,8 +1673,6 @@ function setAuthCookies(res, tokens, cookie) {
1269
1673
  if (cookie.domain) {
1270
1674
  base.domain = cookie.domain;
1271
1675
  }
1272
- console.log(cookie, "cookie");
1273
- console.log(base, "base");
1274
1676
  if (tokens?.access_token) {
1275
1677
  res.cookie("access_token", tokens.access_token, base);
1276
1678
  }
@@ -1294,12 +1696,6 @@ function respondWithKeycloakError(res, err, fallback, status = 400) {
1294
1696
  const description = err?.response?.data?.error_description || err?.response?.data?.errorMessage || err?.message || fallback;
1295
1697
  return res.status(status).json({ ok: false, error: description });
1296
1698
  }
1297
- function buildVerificationTemplate(token, options) {
1298
- return `<a href="${getFrontendBaseUrl(options)}/auth/verify-email?token=${token}">Verify</a>`;
1299
- }
1300
- function buildResetTemplate(token, options) {
1301
- return `<a href="${getFrontendBaseUrl(options)}/auth/reset-password?token=${token}">Reset</a>`;
1302
- }
1303
1699
  function getFrontendBaseUrl(options) {
1304
1700
  if (options.frontendBaseUrl)
1305
1701
  return options.frontendBaseUrl.replace(/\/$/, "");