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