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