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.cjs CHANGED
@@ -642,7 +642,7 @@ var AuthAdminService = class {
642
642
  }
643
643
  async updateUserPassword(userId, newPassword) {
644
644
  const hashed = await import_bcrypt.default.hash(newPassword, 10);
645
- await OrgUser.findOneAndUpdate({ id: userId }, { password: hashed });
645
+ await OrgUser.findOneAndUpdate({ id: userId }, { passwordHash: hashed });
646
646
  }
647
647
  // -------------------------------------------------------------------
648
648
  // ADMIN TOKEN (self-issued JWT)
@@ -695,7 +695,6 @@ var EmailService = class {
695
695
  return import_jsonwebtoken3.default.verify(token, process.env.EMAIL_JWT_SECRET);
696
696
  }
697
697
  async send(to, subject, html) {
698
- console.log("[EmailService] Attempting to send:", { to, subject });
699
698
  try {
700
699
  const info = await this.transporter.sendMail({
701
700
  from: process.env.EMAIL_FROM,
@@ -723,18 +722,6 @@ var EmailService = class {
723
722
  }
724
723
  }
725
724
  canSend(lastEmailSent) {
726
- console.log(
727
- process.env.EMAIL_PASSWORD,
728
- "pssword",
729
- process.env.EMAIL_USER,
730
- "user",
731
- process.env.EMAIL_SECURE,
732
- "secure",
733
- process.env.EMAIL_PORT,
734
- "porat",
735
- process.env.EMAIL_HOST,
736
- "hosat"
737
- );
738
725
  const now = Date.now();
739
726
  const windowStart = now - this.WINDOW_MINUTES * 60 * 1e3;
740
727
  const emailsInWindow = (lastEmailSent || []).map((d) => new Date(d)).filter((d) => d.getTime() >= windowStart);
@@ -748,6 +735,386 @@ var EmailService = class {
748
735
  }
749
736
  };
750
737
 
738
+ // src/templates/email.templates.ts
739
+ var colors = {
740
+ background: "#0a0a0a",
741
+ cardBackground: "#111111",
742
+ cardBorder: "#1a1a1a",
743
+ accent: "#ffffff",
744
+ accentMuted: "rgba(255, 255, 255, 0.9)",
745
+ textPrimary: "#ffffff",
746
+ textSecondary: "rgba(255, 255, 255, 0.7)",
747
+ textMuted: "rgba(255, 255, 255, 0.5)",
748
+ divider: "rgba(255, 255, 255, 0.1)",
749
+ subtle: "#161616",
750
+ highlight: "rgba(255, 255, 255, 0.05)"
751
+ };
752
+ var styles = {
753
+ wrapper: `
754
+ margin: 0;
755
+ padding: 40px 20px;
756
+ background-color: ${colors.background};
757
+ background-image:
758
+ radial-gradient(ellipse at top, rgba(255,255,255,0.03) 0%, transparent 50%),
759
+ radial-gradient(ellipse at bottom, rgba(255,255,255,0.02) 0%, transparent 50%);
760
+ min-height: 100vh;
761
+ `,
762
+ container: `
763
+ max-width: 520px;
764
+ margin: 0 auto;
765
+ font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
766
+ color: ${colors.textPrimary};
767
+ line-height: 1.7;
768
+ `,
769
+ card: `
770
+ background-color: ${colors.cardBackground};
771
+ border: 1px solid ${colors.cardBorder};
772
+ border-radius: 16px;
773
+ overflow: hidden;
774
+ box-shadow:
775
+ 0 0 0 1px rgba(255,255,255,0.05),
776
+ 0 20px 50px -20px rgba(0,0,0,0.5),
777
+ 0 30px 60px -30px rgba(0,0,0,0.3);
778
+ `,
779
+ header: `
780
+ padding: 48px 40px 32px;
781
+ text-align: center;
782
+ border-bottom: 1px solid ${colors.divider};
783
+ `,
784
+ iconWrapper: `
785
+ width: 64px;
786
+ height: 64px;
787
+ margin: 0 auto 24px;
788
+ background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);
789
+ border: 1px solid rgba(255,255,255,0.1);
790
+ border-radius: 16px;
791
+ display: flex;
792
+ align-items: center;
793
+ justify-content: center;
794
+ font-size: 28px;
795
+ `,
796
+ headerTitle: `
797
+ color: ${colors.textPrimary};
798
+ margin: 0;
799
+ font-size: 24px;
800
+ font-weight: 600;
801
+ letter-spacing: -0.5px;
802
+ `,
803
+ headerSubtitle: `
804
+ color: ${colors.textMuted};
805
+ margin: 8px 0 0;
806
+ font-size: 14px;
807
+ font-weight: 400;
808
+ `,
809
+ body: `
810
+ padding: 40px;
811
+ `,
812
+ greeting: `
813
+ margin: 0 0 24px;
814
+ color: ${colors.textPrimary};
815
+ font-size: 18px;
816
+ font-weight: 500;
817
+ `,
818
+ paragraph: `
819
+ margin: 0 0 20px;
820
+ color: ${colors.textSecondary};
821
+ font-size: 15px;
822
+ line-height: 1.7;
823
+ `,
824
+ buttonWrapper: `
825
+ text-align: center;
826
+ margin: 32px 0;
827
+ `,
828
+ button: `
829
+ display: inline-block;
830
+ background-color: ${colors.accent};
831
+ color: #000000 !important;
832
+ text-decoration: none;
833
+ padding: 14px 36px;
834
+ border-radius: 8px;
835
+ font-weight: 600;
836
+ font-size: 14px;
837
+ letter-spacing: 0.3px;
838
+ transition: all 0.2s ease;
839
+ `,
840
+ secondaryButton: `
841
+ display: inline-block;
842
+ background-color: transparent;
843
+ color: ${colors.textPrimary} !important;
844
+ text-decoration: none;
845
+ padding: 12px 28px;
846
+ border-radius: 8px;
847
+ font-weight: 500;
848
+ font-size: 14px;
849
+ border: 1px solid ${colors.divider};
850
+ `,
851
+ infoCard: `
852
+ background-color: ${colors.subtle};
853
+ border: 1px solid ${colors.divider};
854
+ border-radius: 12px;
855
+ padding: 20px 24px;
856
+ margin: 28px 0;
857
+ `,
858
+ infoCardTitle: `
859
+ margin: 0 0 12px;
860
+ color: ${colors.textPrimary};
861
+ font-size: 13px;
862
+ font-weight: 600;
863
+ text-transform: uppercase;
864
+ letter-spacing: 0.5px;
865
+ `,
866
+ infoCardText: `
867
+ margin: 0;
868
+ color: ${colors.textSecondary};
869
+ font-size: 14px;
870
+ line-height: 1.6;
871
+ `,
872
+ warningCard: `
873
+ background: linear-gradient(135deg, rgba(255,180,0,0.1) 0%, rgba(255,140,0,0.05) 100%);
874
+ border: 1px solid rgba(255,180,0,0.2);
875
+ border-radius: 12px;
876
+ padding: 20px 24px;
877
+ margin: 28px 0;
878
+ `,
879
+ warningCardTitle: `
880
+ margin: 0 0 12px;
881
+ color: #ffc107;
882
+ font-size: 13px;
883
+ font-weight: 600;
884
+ text-transform: uppercase;
885
+ letter-spacing: 0.5px;
886
+ `,
887
+ warningCardText: `
888
+ margin: 0;
889
+ color: rgba(255,255,255,0.7);
890
+ font-size: 14px;
891
+ line-height: 1.6;
892
+ `,
893
+ successCard: `
894
+ background: linear-gradient(135deg, rgba(0,255,150,0.1) 0%, rgba(0,200,100,0.05) 100%);
895
+ border: 1px solid rgba(0,255,150,0.2);
896
+ border-radius: 12px;
897
+ padding: 20px 24px;
898
+ margin: 28px 0;
899
+ `,
900
+ successCardTitle: `
901
+ margin: 0 0 12px;
902
+ color: #00ff96;
903
+ font-size: 13px;
904
+ font-weight: 600;
905
+ text-transform: uppercase;
906
+ letter-spacing: 0.5px;
907
+ `,
908
+ linkSection: `
909
+ margin: 32px 0;
910
+ padding: 20px;
911
+ background-color: ${colors.subtle};
912
+ border-radius: 8px;
913
+ border: 1px solid ${colors.divider};
914
+ `,
915
+ linkLabel: `
916
+ margin: 0 0 8px;
917
+ color: ${colors.textMuted};
918
+ font-size: 12px;
919
+ text-transform: uppercase;
920
+ letter-spacing: 0.5px;
921
+ `,
922
+ linkText: `
923
+ word-break: break-all;
924
+ color: ${colors.textSecondary};
925
+ font-size: 13px;
926
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;
927
+ margin: 0;
928
+ `,
929
+ divider: `
930
+ border: none;
931
+ border-top: 1px solid ${colors.divider};
932
+ margin: 32px 0;
933
+ `,
934
+ footer: `
935
+ padding: 24px 40px 32px;
936
+ text-align: center;
937
+ border-top: 1px solid ${colors.divider};
938
+ `,
939
+ footerText: `
940
+ margin: 0;
941
+ color: ${colors.textMuted};
942
+ font-size: 12px;
943
+ line-height: 1.8;
944
+ `,
945
+ footerLink: `
946
+ color: ${colors.textSecondary};
947
+ text-decoration: none;
948
+ `,
949
+ badge: `
950
+ display: inline-block;
951
+ background-color: rgba(255,255,255,0.1);
952
+ color: ${colors.textSecondary};
953
+ padding: 4px 12px;
954
+ border-radius: 20px;
955
+ font-size: 12px;
956
+ font-weight: 500;
957
+ letter-spacing: 0.3px;
958
+ `,
959
+ listItem: `
960
+ color: ${colors.textSecondary};
961
+ font-size: 14px;
962
+ margin: 8px 0;
963
+ padding-left: 8px;
964
+ `,
965
+ metaRow: `
966
+ display: flex;
967
+ justify-content: space-between;
968
+ padding: 12px 0;
969
+ border-bottom: 1px solid ${colors.divider};
970
+ `,
971
+ metaLabel: `
972
+ color: ${colors.textMuted};
973
+ font-size: 13px;
974
+ `,
975
+ metaValue: `
976
+ color: ${colors.textPrimary};
977
+ font-size: 13px;
978
+ font-weight: 500;
979
+ `
980
+ };
981
+ function buildVerificationEmailTemplate(data) {
982
+ const { firstName, verificationUrl, expiresIn } = data;
983
+ return `
984
+ <!DOCTYPE html>
985
+ <html lang="en">
986
+ <head>
987
+ <meta charset="UTF-8">
988
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
989
+ <meta name="color-scheme" content="dark">
990
+ <meta name="supported-color-schemes" content="dark">
991
+ <title>Verify Your Email</title>
992
+ <!--[if mso]>
993
+ <style type="text/css">
994
+ body, table, td {font-family: Arial, Helvetica, sans-serif !important;}
995
+ </style>
996
+ <![endif]-->
997
+ </head>
998
+ <body style="${styles.wrapper}">
999
+ <div style="${styles.container}">
1000
+ <div style="${styles.card}">
1001
+ <!-- Header -->
1002
+ <div style="${styles.header}">
1003
+ <div style="${styles.iconWrapper}">
1004
+ \u2709\uFE0F
1005
+ </div>
1006
+ <h1 style="${styles.headerTitle}">Verify your email</h1>
1007
+ <p style="${styles.headerSubtitle}">One quick step to get started</p>
1008
+ </div>
1009
+
1010
+ <!-- Body -->
1011
+ <div style="${styles.body}">
1012
+ <p style="${styles.greeting}">Hi ${firstName},</p>
1013
+
1014
+ <p style="${styles.paragraph}">
1015
+ Thanks for signing up. To complete your registration and unlock all features,
1016
+ please verify your email address by clicking the button below.
1017
+ </p>
1018
+
1019
+ <div style="${styles.buttonWrapper}">
1020
+ <a href="${verificationUrl}" style="${styles.button}" target="_blank">
1021
+ Verify Email Address
1022
+ </a>
1023
+ </div>
1024
+
1025
+ <div style="${styles.infoCard}">
1026
+ <p style="${styles.infoCardTitle}">\u23F1 Time Sensitive</p>
1027
+ <p style="${styles.infoCardText}">
1028
+ This verification link will expire in <strong>${expiresIn}</strong>.
1029
+ If you didn't create an account, you can safely ignore this email.
1030
+ </p>
1031
+ </div>
1032
+ </div>
1033
+
1034
+ <!-- Footer -->
1035
+ <div style="${styles.footer}">
1036
+ <p style="${styles.footerText}">
1037
+ This is an automated message \u2014 please do not reply.<br>
1038
+ \xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} All rights reserved.
1039
+ </p>
1040
+ </div>
1041
+ </div>
1042
+ </div>
1043
+ </body>
1044
+ </html>
1045
+ `;
1046
+ }
1047
+ function buildResetPasswordEmailTemplate(data) {
1048
+ const { firstName, resetUrl, expiresIn } = data;
1049
+ return `
1050
+ <!DOCTYPE html>
1051
+ <html lang="en">
1052
+ <head>
1053
+ <meta charset="UTF-8">
1054
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1055
+ <meta name="color-scheme" content="dark">
1056
+ <meta name="supported-color-schemes" content="dark">
1057
+ <title>Reset Your Password</title>
1058
+ </head>
1059
+ <body style="${styles.wrapper}">
1060
+ <div style="${styles.container}">
1061
+ <div style="${styles.card}">
1062
+ <!-- Header -->
1063
+ <div style="${styles.header}">
1064
+ <div style="${styles.iconWrapper}">
1065
+ \u{1F510}
1066
+ </div>
1067
+ <h1 style="${styles.headerTitle}">Reset your password</h1>
1068
+ <p style="${styles.headerSubtitle}">We received a reset request</p>
1069
+ </div>
1070
+
1071
+ <!-- Body -->
1072
+ <div style="${styles.body}">
1073
+ <p style="${styles.greeting}">Hi ${firstName},</p>
1074
+
1075
+ <p style="${styles.paragraph}">
1076
+ We received a request to reset the password for your account.
1077
+ Click the button below to create a new password.
1078
+ </p>
1079
+
1080
+ <div style="${styles.buttonWrapper}">
1081
+ <a href="${resetUrl}" style="${styles.button}" target="_blank">
1082
+ Reset Password
1083
+ </a>
1084
+ </div>
1085
+
1086
+ <div style="${styles.warningCard}">
1087
+ <p style="${styles.warningCardTitle}">\u26A0\uFE0F Security Notice</p>
1088
+ <p style="${styles.warningCardText}">
1089
+ \u2022 This link expires in <strong>${expiresIn}</strong><br>
1090
+ \u2022 This link can only be used once<br>
1091
+ \u2022 If you didn't request this, ignore this email
1092
+ </p>
1093
+ </div>
1094
+
1095
+ <hr style="${styles.divider}" />
1096
+
1097
+ <p style="${styles.paragraph}; font-size: 13px; color: ${colors.textMuted};">
1098
+ <strong>Didn't request this?</strong><br>
1099
+ Your password remains unchanged. If you're concerned about your account
1100
+ security, please contact our support team immediately.
1101
+ </p>
1102
+ </div>
1103
+
1104
+ <!-- Footer -->
1105
+ <div style="${styles.footer}">
1106
+ <p style="${styles.footerText}">
1107
+ This is an automated message \u2014 please do not reply.<br>
1108
+ \xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} All rights reserved.
1109
+ </p>
1110
+ </div>
1111
+ </div>
1112
+ </div>
1113
+ </body>
1114
+ </html>
1115
+ `;
1116
+ }
1117
+
751
1118
  // src/express/auth.routes.ts
752
1119
  function createAuthRouter(options = {}) {
753
1120
  const googleClientId = process.env.GOOGLE_CLIENT_ID;
@@ -854,10 +1221,20 @@ function createAuthRouter(options = {}) {
854
1221
  emailService: email,
855
1222
  user,
856
1223
  subject: "Verify your email",
857
- html: buildVerificationTemplate(
858
- email.sign({ userId: kcUser.id, email: kcUser.email }),
859
- options
860
- )
1224
+ // html: buildVerificationTemplate(
1225
+ // email.sign({ userId: kcUser.id, email: kcUser.email }),
1226
+ // options,
1227
+ // ),
1228
+ html: buildVerificationEmailTemplate({
1229
+ firstName: user.firstName,
1230
+ verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(
1231
+ {
1232
+ userId: user.id,
1233
+ email: user.email
1234
+ }
1235
+ )}`,
1236
+ expiresIn: "1 hour"
1237
+ })
861
1238
  });
862
1239
  if (emailResult.rateLimited) {
863
1240
  return res.status(429).json({
@@ -937,7 +1314,17 @@ function createAuthRouter(options = {}) {
937
1314
  emailService: email,
938
1315
  user,
939
1316
  subject: "Verify your email",
940
- html: buildVerificationTemplate(token, options)
1317
+ // html: buildVerificationTemplate(token, options),
1318
+ html: buildVerificationEmailTemplate({
1319
+ firstName: user.firstName,
1320
+ verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(
1321
+ {
1322
+ userId: user.id,
1323
+ email: user.email
1324
+ }
1325
+ )}`,
1326
+ expiresIn: "1 hour"
1327
+ })
941
1328
  });
942
1329
  if (resendResult.rateLimited) {
943
1330
  return res.status(429).json({
@@ -966,7 +1353,17 @@ function createAuthRouter(options = {}) {
966
1353
  emailService: email,
967
1354
  user,
968
1355
  subject: "Reset password",
969
- html: buildResetTemplate(resetToken, options)
1356
+ // html: buildResetTemplate(resetToken, options),
1357
+ html: buildResetPasswordEmailTemplate({
1358
+ firstName: user.firstName,
1359
+ resetUrl: `${getFrontendBaseUrl(options)}/reset-password?token=${email.sign(
1360
+ {
1361
+ userId: user.id,
1362
+ email: user.email
1363
+ }
1364
+ )}`,
1365
+ expiresIn: "1 hour"
1366
+ })
970
1367
  });
971
1368
  if (resetResult.rateLimited) {
972
1369
  return res.status(429).json({
@@ -979,9 +1376,16 @@ function createAuthRouter(options = {}) {
979
1376
  });
980
1377
  r.post("/reset-password", validateResetPassword, async (req, res) => {
981
1378
  const { token, newPassword } = req.body || {};
1379
+ if (!token || !newPassword) {
1380
+ return res.status(400).json({
1381
+ ok: false,
1382
+ error: "Token and new password are required",
1383
+ code: "MISSING_FIELDS"
1384
+ });
1385
+ }
982
1386
  try {
983
1387
  const payload = email.verify(token);
984
- const user = await OrgUser.findOne({ keycloakId: payload.userId });
1388
+ const user = await OrgUser.findOne({ id: payload.userId });
985
1389
  if (!user) {
986
1390
  return res.status(404).json({ ok: false, error: "User not found" });
987
1391
  }
@@ -1349,8 +1753,6 @@ function setAuthCookies(res, tokens, cookie) {
1349
1753
  if (cookie.domain) {
1350
1754
  base.domain = cookie.domain;
1351
1755
  }
1352
- console.log(cookie, "cookie");
1353
- console.log(base, "base");
1354
1756
  if (tokens?.access_token) {
1355
1757
  res.cookie("access_token", tokens.access_token, base);
1356
1758
  }
@@ -1374,12 +1776,6 @@ function respondWithKeycloakError(res, err, fallback, status = 400) {
1374
1776
  const description = err?.response?.data?.error_description || err?.response?.data?.errorMessage || err?.message || fallback;
1375
1777
  return res.status(status).json({ ok: false, error: description });
1376
1778
  }
1377
- function buildVerificationTemplate(token, options) {
1378
- return `<a href="${getFrontendBaseUrl(options)}/auth/verify-email?token=${token}">Verify</a>`;
1379
- }
1380
- function buildResetTemplate(token, options) {
1381
- return `<a href="${getFrontendBaseUrl(options)}/auth/reset-password?token=${token}">Reset</a>`;
1382
- }
1383
1779
  function getFrontendBaseUrl(options) {
1384
1780
  if (options.frontendBaseUrl)
1385
1781
  return options.frontendBaseUrl.replace(/\/$/, "");