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.
@@ -524,7 +524,7 @@ var AuthAdminService = class {
524
524
  }
525
525
  async updateUserPassword(userId, newPassword) {
526
526
  const hashed = await bcrypt.hash(newPassword, 10);
527
- await OrgUser.findOneAndUpdate({ id: userId }, { password: hashed });
527
+ await OrgUser.findOneAndUpdate({ id: userId }, { passwordHash: hashed });
528
528
  }
529
529
  // -------------------------------------------------------------------
530
530
  // ADMIN TOKEN (self-issued JWT)
@@ -577,7 +577,6 @@ var EmailService = class {
577
577
  return jwt3.verify(token, process.env.EMAIL_JWT_SECRET);
578
578
  }
579
579
  async send(to, subject, html) {
580
- console.log("[EmailService] Attempting to send:", { to, subject });
581
580
  try {
582
581
  const info = await this.transporter.sendMail({
583
582
  from: process.env.EMAIL_FROM,
@@ -605,18 +604,6 @@ var EmailService = class {
605
604
  }
606
605
  }
607
606
  canSend(lastEmailSent) {
608
- console.log(
609
- process.env.EMAIL_PASSWORD,
610
- "pssword",
611
- process.env.EMAIL_USER,
612
- "user",
613
- process.env.EMAIL_SECURE,
614
- "secure",
615
- process.env.EMAIL_PORT,
616
- "porat",
617
- process.env.EMAIL_HOST,
618
- "hosat"
619
- );
620
607
  const now = Date.now();
621
608
  const windowStart = now - this.WINDOW_MINUTES * 60 * 1e3;
622
609
  const emailsInWindow = (lastEmailSent || []).map((d) => new Date(d)).filter((d) => d.getTime() >= windowStart);
@@ -630,6 +617,386 @@ var EmailService = class {
630
617
  }
631
618
  };
632
619
 
620
+ // src/templates/email.templates.ts
621
+ var colors = {
622
+ background: "#0a0a0a",
623
+ cardBackground: "#111111",
624
+ cardBorder: "#1a1a1a",
625
+ accent: "#ffffff",
626
+ accentMuted: "rgba(255, 255, 255, 0.9)",
627
+ textPrimary: "#ffffff",
628
+ textSecondary: "rgba(255, 255, 255, 0.7)",
629
+ textMuted: "rgba(255, 255, 255, 0.5)",
630
+ divider: "rgba(255, 255, 255, 0.1)",
631
+ subtle: "#161616",
632
+ highlight: "rgba(255, 255, 255, 0.05)"
633
+ };
634
+ var styles = {
635
+ wrapper: `
636
+ margin: 0;
637
+ padding: 40px 20px;
638
+ background-color: ${colors.background};
639
+ background-image:
640
+ radial-gradient(ellipse at top, rgba(255,255,255,0.03) 0%, transparent 50%),
641
+ radial-gradient(ellipse at bottom, rgba(255,255,255,0.02) 0%, transparent 50%);
642
+ min-height: 100vh;
643
+ `,
644
+ container: `
645
+ max-width: 520px;
646
+ margin: 0 auto;
647
+ font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
648
+ color: ${colors.textPrimary};
649
+ line-height: 1.7;
650
+ `,
651
+ card: `
652
+ background-color: ${colors.cardBackground};
653
+ border: 1px solid ${colors.cardBorder};
654
+ border-radius: 16px;
655
+ overflow: hidden;
656
+ box-shadow:
657
+ 0 0 0 1px rgba(255,255,255,0.05),
658
+ 0 20px 50px -20px rgba(0,0,0,0.5),
659
+ 0 30px 60px -30px rgba(0,0,0,0.3);
660
+ `,
661
+ header: `
662
+ padding: 48px 40px 32px;
663
+ text-align: center;
664
+ border-bottom: 1px solid ${colors.divider};
665
+ `,
666
+ iconWrapper: `
667
+ width: 64px;
668
+ height: 64px;
669
+ margin: 0 auto 24px;
670
+ background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);
671
+ border: 1px solid rgba(255,255,255,0.1);
672
+ border-radius: 16px;
673
+ display: flex;
674
+ align-items: center;
675
+ justify-content: center;
676
+ font-size: 28px;
677
+ `,
678
+ headerTitle: `
679
+ color: ${colors.textPrimary};
680
+ margin: 0;
681
+ font-size: 24px;
682
+ font-weight: 600;
683
+ letter-spacing: -0.5px;
684
+ `,
685
+ headerSubtitle: `
686
+ color: ${colors.textMuted};
687
+ margin: 8px 0 0;
688
+ font-size: 14px;
689
+ font-weight: 400;
690
+ `,
691
+ body: `
692
+ padding: 40px;
693
+ `,
694
+ greeting: `
695
+ margin: 0 0 24px;
696
+ color: ${colors.textPrimary};
697
+ font-size: 18px;
698
+ font-weight: 500;
699
+ `,
700
+ paragraph: `
701
+ margin: 0 0 20px;
702
+ color: ${colors.textSecondary};
703
+ font-size: 15px;
704
+ line-height: 1.7;
705
+ `,
706
+ buttonWrapper: `
707
+ text-align: center;
708
+ margin: 32px 0;
709
+ `,
710
+ button: `
711
+ display: inline-block;
712
+ background-color: ${colors.accent};
713
+ color: #000000 !important;
714
+ text-decoration: none;
715
+ padding: 14px 36px;
716
+ border-radius: 8px;
717
+ font-weight: 600;
718
+ font-size: 14px;
719
+ letter-spacing: 0.3px;
720
+ transition: all 0.2s ease;
721
+ `,
722
+ secondaryButton: `
723
+ display: inline-block;
724
+ background-color: transparent;
725
+ color: ${colors.textPrimary} !important;
726
+ text-decoration: none;
727
+ padding: 12px 28px;
728
+ border-radius: 8px;
729
+ font-weight: 500;
730
+ font-size: 14px;
731
+ border: 1px solid ${colors.divider};
732
+ `,
733
+ infoCard: `
734
+ background-color: ${colors.subtle};
735
+ border: 1px solid ${colors.divider};
736
+ border-radius: 12px;
737
+ padding: 20px 24px;
738
+ margin: 28px 0;
739
+ `,
740
+ infoCardTitle: `
741
+ margin: 0 0 12px;
742
+ color: ${colors.textPrimary};
743
+ font-size: 13px;
744
+ font-weight: 600;
745
+ text-transform: uppercase;
746
+ letter-spacing: 0.5px;
747
+ `,
748
+ infoCardText: `
749
+ margin: 0;
750
+ color: ${colors.textSecondary};
751
+ font-size: 14px;
752
+ line-height: 1.6;
753
+ `,
754
+ warningCard: `
755
+ background: linear-gradient(135deg, rgba(255,180,0,0.1) 0%, rgba(255,140,0,0.05) 100%);
756
+ border: 1px solid rgba(255,180,0,0.2);
757
+ border-radius: 12px;
758
+ padding: 20px 24px;
759
+ margin: 28px 0;
760
+ `,
761
+ warningCardTitle: `
762
+ margin: 0 0 12px;
763
+ color: #ffc107;
764
+ font-size: 13px;
765
+ font-weight: 600;
766
+ text-transform: uppercase;
767
+ letter-spacing: 0.5px;
768
+ `,
769
+ warningCardText: `
770
+ margin: 0;
771
+ color: rgba(255,255,255,0.7);
772
+ font-size: 14px;
773
+ line-height: 1.6;
774
+ `,
775
+ successCard: `
776
+ background: linear-gradient(135deg, rgba(0,255,150,0.1) 0%, rgba(0,200,100,0.05) 100%);
777
+ border: 1px solid rgba(0,255,150,0.2);
778
+ border-radius: 12px;
779
+ padding: 20px 24px;
780
+ margin: 28px 0;
781
+ `,
782
+ successCardTitle: `
783
+ margin: 0 0 12px;
784
+ color: #00ff96;
785
+ font-size: 13px;
786
+ font-weight: 600;
787
+ text-transform: uppercase;
788
+ letter-spacing: 0.5px;
789
+ `,
790
+ linkSection: `
791
+ margin: 32px 0;
792
+ padding: 20px;
793
+ background-color: ${colors.subtle};
794
+ border-radius: 8px;
795
+ border: 1px solid ${colors.divider};
796
+ `,
797
+ linkLabel: `
798
+ margin: 0 0 8px;
799
+ color: ${colors.textMuted};
800
+ font-size: 12px;
801
+ text-transform: uppercase;
802
+ letter-spacing: 0.5px;
803
+ `,
804
+ linkText: `
805
+ word-break: break-all;
806
+ color: ${colors.textSecondary};
807
+ font-size: 13px;
808
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;
809
+ margin: 0;
810
+ `,
811
+ divider: `
812
+ border: none;
813
+ border-top: 1px solid ${colors.divider};
814
+ margin: 32px 0;
815
+ `,
816
+ footer: `
817
+ padding: 24px 40px 32px;
818
+ text-align: center;
819
+ border-top: 1px solid ${colors.divider};
820
+ `,
821
+ footerText: `
822
+ margin: 0;
823
+ color: ${colors.textMuted};
824
+ font-size: 12px;
825
+ line-height: 1.8;
826
+ `,
827
+ footerLink: `
828
+ color: ${colors.textSecondary};
829
+ text-decoration: none;
830
+ `,
831
+ badge: `
832
+ display: inline-block;
833
+ background-color: rgba(255,255,255,0.1);
834
+ color: ${colors.textSecondary};
835
+ padding: 4px 12px;
836
+ border-radius: 20px;
837
+ font-size: 12px;
838
+ font-weight: 500;
839
+ letter-spacing: 0.3px;
840
+ `,
841
+ listItem: `
842
+ color: ${colors.textSecondary};
843
+ font-size: 14px;
844
+ margin: 8px 0;
845
+ padding-left: 8px;
846
+ `,
847
+ metaRow: `
848
+ display: flex;
849
+ justify-content: space-between;
850
+ padding: 12px 0;
851
+ border-bottom: 1px solid ${colors.divider};
852
+ `,
853
+ metaLabel: `
854
+ color: ${colors.textMuted};
855
+ font-size: 13px;
856
+ `,
857
+ metaValue: `
858
+ color: ${colors.textPrimary};
859
+ font-size: 13px;
860
+ font-weight: 500;
861
+ `
862
+ };
863
+ function buildVerificationEmailTemplate(data) {
864
+ const { firstName, verificationUrl, expiresIn } = data;
865
+ return `
866
+ <!DOCTYPE html>
867
+ <html lang="en">
868
+ <head>
869
+ <meta charset="UTF-8">
870
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
871
+ <meta name="color-scheme" content="dark">
872
+ <meta name="supported-color-schemes" content="dark">
873
+ <title>Verify Your Email</title>
874
+ <!--[if mso]>
875
+ <style type="text/css">
876
+ body, table, td {font-family: Arial, Helvetica, sans-serif !important;}
877
+ </style>
878
+ <![endif]-->
879
+ </head>
880
+ <body style="${styles.wrapper}">
881
+ <div style="${styles.container}">
882
+ <div style="${styles.card}">
883
+ <!-- Header -->
884
+ <div style="${styles.header}">
885
+ <div style="${styles.iconWrapper}">
886
+ \u2709\uFE0F
887
+ </div>
888
+ <h1 style="${styles.headerTitle}">Verify your email</h1>
889
+ <p style="${styles.headerSubtitle}">One quick step to get started</p>
890
+ </div>
891
+
892
+ <!-- Body -->
893
+ <div style="${styles.body}">
894
+ <p style="${styles.greeting}">Hi ${firstName},</p>
895
+
896
+ <p style="${styles.paragraph}">
897
+ Thanks for signing up. To complete your registration and unlock all features,
898
+ please verify your email address by clicking the button below.
899
+ </p>
900
+
901
+ <div style="${styles.buttonWrapper}">
902
+ <a href="${verificationUrl}" style="${styles.button}" target="_blank">
903
+ Verify Email Address
904
+ </a>
905
+ </div>
906
+
907
+ <div style="${styles.infoCard}">
908
+ <p style="${styles.infoCardTitle}">\u23F1 Time Sensitive</p>
909
+ <p style="${styles.infoCardText}">
910
+ This verification link will expire in <strong>${expiresIn}</strong>.
911
+ If you didn't create an account, you can safely ignore this email.
912
+ </p>
913
+ </div>
914
+ </div>
915
+
916
+ <!-- Footer -->
917
+ <div style="${styles.footer}">
918
+ <p style="${styles.footerText}">
919
+ This is an automated message \u2014 please do not reply.<br>
920
+ \xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} All rights reserved.
921
+ </p>
922
+ </div>
923
+ </div>
924
+ </div>
925
+ </body>
926
+ </html>
927
+ `;
928
+ }
929
+ function buildResetPasswordEmailTemplate(data) {
930
+ const { firstName, resetUrl, expiresIn } = data;
931
+ return `
932
+ <!DOCTYPE html>
933
+ <html lang="en">
934
+ <head>
935
+ <meta charset="UTF-8">
936
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
937
+ <meta name="color-scheme" content="dark">
938
+ <meta name="supported-color-schemes" content="dark">
939
+ <title>Reset Your Password</title>
940
+ </head>
941
+ <body style="${styles.wrapper}">
942
+ <div style="${styles.container}">
943
+ <div style="${styles.card}">
944
+ <!-- Header -->
945
+ <div style="${styles.header}">
946
+ <div style="${styles.iconWrapper}">
947
+ \u{1F510}
948
+ </div>
949
+ <h1 style="${styles.headerTitle}">Reset your password</h1>
950
+ <p style="${styles.headerSubtitle}">We received a reset request</p>
951
+ </div>
952
+
953
+ <!-- Body -->
954
+ <div style="${styles.body}">
955
+ <p style="${styles.greeting}">Hi ${firstName},</p>
956
+
957
+ <p style="${styles.paragraph}">
958
+ We received a request to reset the password for your account.
959
+ Click the button below to create a new password.
960
+ </p>
961
+
962
+ <div style="${styles.buttonWrapper}">
963
+ <a href="${resetUrl}" style="${styles.button}" target="_blank">
964
+ Reset Password
965
+ </a>
966
+ </div>
967
+
968
+ <div style="${styles.warningCard}">
969
+ <p style="${styles.warningCardTitle}">\u26A0\uFE0F Security Notice</p>
970
+ <p style="${styles.warningCardText}">
971
+ \u2022 This link expires in <strong>${expiresIn}</strong><br>
972
+ \u2022 This link can only be used once<br>
973
+ \u2022 If you didn't request this, ignore this email
974
+ </p>
975
+ </div>
976
+
977
+ <hr style="${styles.divider}" />
978
+
979
+ <p style="${styles.paragraph}; font-size: 13px; color: ${colors.textMuted};">
980
+ <strong>Didn't request this?</strong><br>
981
+ Your password remains unchanged. If you're concerned about your account
982
+ security, please contact our support team immediately.
983
+ </p>
984
+ </div>
985
+
986
+ <!-- Footer -->
987
+ <div style="${styles.footer}">
988
+ <p style="${styles.footerText}">
989
+ This is an automated message \u2014 please do not reply.<br>
990
+ \xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} All rights reserved.
991
+ </p>
992
+ </div>
993
+ </div>
994
+ </div>
995
+ </body>
996
+ </html>
997
+ `;
998
+ }
999
+
633
1000
  // src/express/auth.routes.ts
634
1001
  function createAuthRouter(options = {}) {
635
1002
  const googleClientId = process.env.GOOGLE_CLIENT_ID;
@@ -736,10 +1103,20 @@ function createAuthRouter(options = {}) {
736
1103
  emailService: email,
737
1104
  user,
738
1105
  subject: "Verify your email",
739
- html: buildVerificationTemplate(
740
- email.sign({ userId: kcUser.id, email: kcUser.email }),
741
- options
742
- )
1106
+ // html: buildVerificationTemplate(
1107
+ // email.sign({ userId: kcUser.id, email: kcUser.email }),
1108
+ // options,
1109
+ // ),
1110
+ html: buildVerificationEmailTemplate({
1111
+ firstName: user.firstName,
1112
+ verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(
1113
+ {
1114
+ userId: user.id,
1115
+ email: user.email
1116
+ }
1117
+ )}`,
1118
+ expiresIn: "1 hour"
1119
+ })
743
1120
  });
744
1121
  if (emailResult.rateLimited) {
745
1122
  return res.status(429).json({
@@ -819,7 +1196,17 @@ function createAuthRouter(options = {}) {
819
1196
  emailService: email,
820
1197
  user,
821
1198
  subject: "Verify your email",
822
- html: buildVerificationTemplate(token, options)
1199
+ // html: buildVerificationTemplate(token, options),
1200
+ html: buildVerificationEmailTemplate({
1201
+ firstName: user.firstName,
1202
+ verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(
1203
+ {
1204
+ userId: user.id,
1205
+ email: user.email
1206
+ }
1207
+ )}`,
1208
+ expiresIn: "1 hour"
1209
+ })
823
1210
  });
824
1211
  if (resendResult.rateLimited) {
825
1212
  return res.status(429).json({
@@ -848,7 +1235,17 @@ function createAuthRouter(options = {}) {
848
1235
  emailService: email,
849
1236
  user,
850
1237
  subject: "Reset password",
851
- html: buildResetTemplate(resetToken, options)
1238
+ // html: buildResetTemplate(resetToken, options),
1239
+ html: buildResetPasswordEmailTemplate({
1240
+ firstName: user.firstName,
1241
+ resetUrl: `${getFrontendBaseUrl(options)}/reset-password?token=${email.sign(
1242
+ {
1243
+ userId: user.id,
1244
+ email: user.email
1245
+ }
1246
+ )}`,
1247
+ expiresIn: "1 hour"
1248
+ })
852
1249
  });
853
1250
  if (resetResult.rateLimited) {
854
1251
  return res.status(429).json({
@@ -861,9 +1258,16 @@ function createAuthRouter(options = {}) {
861
1258
  });
862
1259
  r.post("/reset-password", validateResetPassword, async (req, res) => {
863
1260
  const { token, newPassword } = req.body || {};
1261
+ if (!token || !newPassword) {
1262
+ return res.status(400).json({
1263
+ ok: false,
1264
+ error: "Token and new password are required",
1265
+ code: "MISSING_FIELDS"
1266
+ });
1267
+ }
864
1268
  try {
865
1269
  const payload = email.verify(token);
866
- const user = await OrgUser.findOne({ keycloakId: payload.userId });
1270
+ const user = await OrgUser.findOne({ id: payload.userId });
867
1271
  if (!user) {
868
1272
  return res.status(404).json({ ok: false, error: "User not found" });
869
1273
  }
@@ -1231,8 +1635,6 @@ function setAuthCookies(res, tokens, cookie) {
1231
1635
  if (cookie.domain) {
1232
1636
  base.domain = cookie.domain;
1233
1637
  }
1234
- console.log(cookie, "cookie");
1235
- console.log(base, "base");
1236
1638
  if (tokens?.access_token) {
1237
1639
  res.cookie("access_token", tokens.access_token, base);
1238
1640
  }
@@ -1256,12 +1658,6 @@ function respondWithKeycloakError(res, err, fallback, status = 400) {
1256
1658
  const description = err?.response?.data?.error_description || err?.response?.data?.errorMessage || err?.message || fallback;
1257
1659
  return res.status(status).json({ ok: false, error: description });
1258
1660
  }
1259
- function buildVerificationTemplate(token, options) {
1260
- return `<a href="${getFrontendBaseUrl(options)}/auth/verify-email?token=${token}">Verify</a>`;
1261
- }
1262
- function buildResetTemplate(token, options) {
1263
- return `<a href="${getFrontendBaseUrl(options)}/auth/reset-password?token=${token}">Reset</a>`;
1264
- }
1265
1661
  function getFrontendBaseUrl(options) {
1266
1662
  if (options.frontendBaseUrl)
1267
1663
  return options.frontendBaseUrl.replace(/\/$/, "");