@rpcbase/auth 0.107.0 → 0.108.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/api/request-password-reset/handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAmB,MAAM,cAAc,CAAA;AAInD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;yBAuDlC,KAAK,GAAG,CAAC,eAAe,CAAC;AAAzC,wBAEC"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/api/request-password-reset/handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAmB,MAAM,cAAc,CAAA;AAInD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;yBA2ElC,KAAK,GAAG,CAAC,eAAe,CAAC;AAAzC,wBAEC"}
@@ -10,6 +10,20 @@ object({
10
10
  success: boolean(),
11
11
  error: string().optional()
12
12
  });
13
+ const maskEmailForLog = (email) => {
14
+ const [localRaw, domainRaw = ""] = email.split("@");
15
+ const local = localRaw || "";
16
+ const domain = domainRaw || "";
17
+ const localPrefix = local.slice(0, Math.min(3, local.length));
18
+ const localMask = "*".repeat(Math.max(5, local.length - localPrefix.length));
19
+ if (!domain) {
20
+ return `${localPrefix}${localMask}@****`;
21
+ }
22
+ const domainTailLength = Math.min(7, domain.length);
23
+ const domainTail = domain.slice(-domainTailLength);
24
+ const domainMask = "*".repeat(Math.max(4, domain.length - domainTailLength));
25
+ return `${localPrefix}${localMask}@${domainMask}${domainTail}`;
26
+ };
13
27
  const requestPasswordReset = async (payload, ctx) => {
14
28
  const User = await models.getGlobal("RBUser", ctx);
15
29
  const parsed = requestSchema.safeParse(payload);
@@ -39,6 +53,7 @@ const requestPasswordReset = async (payload, ctx) => {
39
53
  user.passwordResetTokenExpiresAt = void 0;
40
54
  await user.save();
41
55
  try {
56
+ console.info(`sending password reset to email ${maskEmailForLog(email)}`);
42
57
  await sendEmail({
43
58
  to: email,
44
59
  subject: `Your password reset code: ${passwordResetCode}`,
@@ -61,4 +76,4 @@ const handler = (api) => {
61
76
  export {
62
77
  handler as default
63
78
  };
64
- //# sourceMappingURL=handler-ycNayTfM.js.map
79
+ //# sourceMappingURL=handler-BNDemOGd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-BNDemOGd.js","sources":["../src/api/request-password-reset/index.ts","../src/api/request-password-reset/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const Route = \"/api/rb/auth/request-password-reset\"\n\nexport const requestSchema = z.object({\n email: z.string().email(),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import crypto from \"crypto\"\n\nimport { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { sendEmail } from \"@rpcbase/server\"\n\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as RequestPasswordReset from \"./index\"\n\n\nconst maskEmailForLog = (email: string) => {\n const [localRaw, domainRaw = \"\"] = email.split(\"@\")\n const local = localRaw || \"\"\n const domain = domainRaw || \"\"\n const localPrefix = local.slice(0, Math.min(3, local.length))\n const localMask = \"*\".repeat(Math.max(5, local.length - localPrefix.length))\n\n if (!domain) {\n return `${localPrefix}${localMask}@****`\n }\n\n const domainTailLength = Math.min(7, domain.length)\n const domainTail = domain.slice(-domainTailLength)\n const domainMask = \"*\".repeat(Math.max(4, domain.length - domainTailLength))\n\n return `${localPrefix}${localMask}@${domainMask}${domainTail}`\n}\n\n\nconst requestPasswordReset: ApiHandler<\n RequestPasswordReset.RequestPayload,\n RequestPasswordReset.ResponsePayload,\n AuthSessionUser\n> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<RequestPasswordReset.ResponsePayload> => {\n const User = await models.getGlobal(\"RBUser\", ctx)\n\n const parsed = RequestPasswordReset.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const { email } = parsed.data\n const user = await User.findOne({ email })\n\n if (!user) {\n return { success: true }\n }\n\n const passwordResetCode = crypto.randomInt(0, 1_000_000).toString().padStart(6, \"0\")\n const passwordResetCodeExpiresAt = new Date(Date.now() + 10 * 60 * 1000)\n\n user.passwordResetCode = passwordResetCode\n user.passwordResetCodeExpiresAt = passwordResetCodeExpiresAt\n user.passwordResetToken = undefined\n user.passwordResetTokenExpiresAt = undefined\n await user.save()\n\n try {\n console.info(`sending password reset to email ${maskEmailForLog(email)}`)\n await sendEmail({\n to: email,\n subject: `Your password reset code: ${passwordResetCode}`,\n html: `\n <p>Your password reset code is <strong>${passwordResetCode}</strong>. It expires in 10 minutes.</p>\n <p>If you didn't request this, you can ignore this message.</p>\n `,\n text: `Your password reset code is ${passwordResetCode}. It expires in 10 minutes. If you didn't request this, you can ignore this message.`,\n })\n } catch (err) {\n console.warn(\"failed to send password reset email\", err)\n }\n\n return { success: true }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(RequestPasswordReset.Route, requestPasswordReset)\n}\n"],"names":["Route","requestSchema","z","email","string","success","boolean","error","optional","maskEmailForLog","localRaw","domainRaw","split","local","domain","localPrefix","slice","Math","min","length","localMask","repeat","max","domainTailLength","domainTail","domainMask","requestPasswordReset","payload","ctx","User","models","getGlobal","parsed","RequestPasswordReset","safeParse","res","status","data","user","findOne","passwordResetCode","crypto","randomInt","toString","padStart","passwordResetCodeExpiresAt","Date","now","passwordResetToken","undefined","passwordResetTokenExpiresAt","save","console","info","sendEmail","to","subject","html","text","err","warn","api","post"],"mappings":";;;;AAGO,MAAMA,QAAQ;AAEd,MAAMC,gBAAgBC,OAAS;AAAA,EACpCC,OAAOD,OAAEE,EAASD,MAAAA;AACpB,CAAC;AAI6BD,OAAS;AAAA,EACrCG,SAASH,QAAEI;AAAAA,EACXC,OAAOL,OAAEE,EAASI,SAAAA;AACpB,CAAC;ACHD,MAAMC,kBAAkBA,CAACN,UAAkB;AACzC,QAAM,CAACO,UAAUC,YAAY,EAAE,IAAIR,MAAMS,MAAM,GAAG;AAClD,QAAMC,QAAQH,YAAY;AAC1B,QAAMI,SAASH,aAAa;AAC5B,QAAMI,cAAcF,MAAMG,MAAM,GAAGC,KAAKC,IAAI,GAAGL,MAAMM,MAAM,CAAC;AAC5D,QAAMC,YAAY,IAAIC,OAAOJ,KAAKK,IAAI,GAAGT,MAAMM,SAASJ,YAAYI,MAAM,CAAC;AAE3E,MAAI,CAACL,QAAQ;AACX,WAAO,GAAGC,WAAW,GAAGK,SAAS;AAAA,EACnC;AAEA,QAAMG,mBAAmBN,KAAKC,IAAI,GAAGJ,OAAOK,MAAM;AAClD,QAAMK,aAAaV,OAAOE,MAAM,CAACO,gBAAgB;AACjD,QAAME,aAAa,IAAIJ,OAAOJ,KAAKK,IAAI,GAAGR,OAAOK,SAASI,gBAAgB,CAAC;AAE3E,SAAO,GAAGR,WAAW,GAAGK,SAAS,IAAIK,UAAU,GAAGD,UAAU;AAC9D;AAGA,MAAME,uBAIF,OACFC,SACAC,QACkD;AAClD,QAAMC,OAAO,MAAMC,OAAOC,UAAU,UAAUH,GAAG;AAEjD,QAAMI,SAASC,cAAmCC,UAAUP,OAAO;AAEnE,MAAI,CAACK,OAAO3B,SAAS;AACnBuB,QAAIO,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAE/B,SAAS;AAAA,MAAOE,OAAO;AAAA,IAAA;AAAA,EAClC;AAEA,QAAM;AAAA,IAAEJ;AAAAA,EAAAA,IAAU6B,OAAOK;AACzB,QAAMC,OAAO,MAAMT,KAAKU,QAAQ;AAAA,IAAEpC;AAAAA,EAAAA,CAAO;AAEzC,MAAI,CAACmC,MAAM;AACT,WAAO;AAAA,MAAEjC,SAAS;AAAA,IAAA;AAAA,EACpB;AAEA,QAAMmC,oBAAoBC,OAAOC,UAAU,GAAG,GAAS,EAAEC,WAAWC,SAAS,GAAG,GAAG;AACnF,QAAMC,6BAA6B,IAAIC,KAAKA,KAAKC,QAAQ,KAAK,KAAK,GAAI;AAEvET,OAAKE,oBAAoBA;AACzBF,OAAKO,6BAA6BA;AAClCP,OAAKU,qBAAqBC;AAC1BX,OAAKY,8BAA8BD;AACnC,QAAMX,KAAKa,KAAAA;AAEX,MAAI;AACFC,YAAQC,KAAK,mCAAmC5C,gBAAgBN,KAAK,CAAC,EAAE;AACxE,UAAMmD,UAAU;AAAA,MACdC,IAAIpD;AAAAA,MACJqD,SAAS,6BAA6BhB,iBAAiB;AAAA,MACvDiB,MAAM;AAAA,iDACqCjB,iBAAiB;AAAA;AAAA;AAAA,MAG5DkB,MAAM,+BAA+BlB,iBAAiB;AAAA,IAAA,CACvD;AAAA,EACH,SAASmB,KAAK;AACZP,YAAQQ,KAAK,uCAAuCD,GAAG;AAAA,EACzD;AAEA,SAAO;AAAA,IAAEtD,SAAS;AAAA,EAAA;AACpB;AAEA,MAAA,UAAe,CAACwD,QAA8B;AAC5CA,MAAIC,KAAK7B,OAA4BP,oBAAoB;AAC3D;"}
package/dist/routes.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const routes = Object.entries({
2
- .../* @__PURE__ */ Object.assign({ "./api/me/handler.ts": () => import("./handler-xEpHnzkZ.js"), "./api/request-password-reset/handler.ts": () => import("./handler-ycNayTfM.js"), "./api/resend-otp/handler.ts": () => import("./handler-Bt53h0sk.js"), "./api/set-new-password/handler.ts": () => import("./handler-C4cw739Z.js"), "./api/sign-in/handler.ts": () => import("./handler-DfEsSB4T.js"), "./api/sign-out/handler.ts": () => import("./handler-CyP6R8FM.js"), "./api/sign-up/handler.ts": () => import("./handler-D6zJn86A.js"), "./api/verify-otp/handler.ts": () => import("./handler-Ck7oLQ_R.js"), "./api/verify-password-reset-otp/handler.ts": () => import("./handler-D8HfTbUs.js") })
2
+ .../* @__PURE__ */ Object.assign({ "./api/me/handler.ts": () => import("./handler-xEpHnzkZ.js"), "./api/request-password-reset/handler.ts": () => import("./handler-BNDemOGd.js"), "./api/resend-otp/handler.ts": () => import("./handler-Bt53h0sk.js"), "./api/set-new-password/handler.ts": () => import("./handler-C4cw739Z.js"), "./api/sign-in/handler.ts": () => import("./handler-DfEsSB4T.js"), "./api/sign-out/handler.ts": () => import("./handler-CyP6R8FM.js"), "./api/sign-up/handler.ts": () => import("./handler-D6zJn86A.js"), "./api/verify-otp/handler.ts": () => import("./handler-Ck7oLQ_R.js"), "./api/verify-password-reset-otp/handler.ts": () => import("./handler-D8HfTbUs.js") })
3
3
  }).reduce((acc, [path, mod]) => {
4
4
  acc[path.replace("./api/", "@rpcbase/auth/api/")] = mod;
5
5
  return acc;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/auth",
3
- "version": "0.107.0",
3
+ "version": "0.108.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -1 +0,0 @@
1
- {"version":3,"file":"handler-ycNayTfM.js","sources":["../src/api/request-password-reset/index.ts","../src/api/request-password-reset/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const Route = \"/api/rb/auth/request-password-reset\"\n\nexport const requestSchema = z.object({\n email: z.string().email(),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import crypto from \"crypto\"\n\nimport { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { sendEmail } from \"@rpcbase/server\"\n\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as RequestPasswordReset from \"./index\"\n\n\nconst requestPasswordReset: ApiHandler<\n RequestPasswordReset.RequestPayload,\n RequestPasswordReset.ResponsePayload,\n AuthSessionUser\n> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<RequestPasswordReset.ResponsePayload> => {\n const User = await models.getGlobal(\"RBUser\", ctx)\n\n const parsed = RequestPasswordReset.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const { email } = parsed.data\n const user = await User.findOne({ email })\n\n if (!user) {\n return { success: true }\n }\n\n const passwordResetCode = crypto.randomInt(0, 1_000_000).toString().padStart(6, \"0\")\n const passwordResetCodeExpiresAt = new Date(Date.now() + 10 * 60 * 1000)\n\n user.passwordResetCode = passwordResetCode\n user.passwordResetCodeExpiresAt = passwordResetCodeExpiresAt\n user.passwordResetToken = undefined\n user.passwordResetTokenExpiresAt = undefined\n await user.save()\n\n try {\n await sendEmail({\n to: email,\n subject: `Your password reset code: ${passwordResetCode}`,\n html: `\n <p>Your password reset code is <strong>${passwordResetCode}</strong>. It expires in 10 minutes.</p>\n <p>If you didn't request this, you can ignore this message.</p>\n `,\n text: `Your password reset code is ${passwordResetCode}. It expires in 10 minutes. If you didn't request this, you can ignore this message.`,\n })\n } catch (err) {\n console.warn(\"failed to send password reset email\", err)\n }\n\n return { success: true }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(RequestPasswordReset.Route, requestPasswordReset)\n}\n"],"names":["Route","requestSchema","z","email","string","success","boolean","error","optional","requestPasswordReset","payload","ctx","User","models","getGlobal","parsed","RequestPasswordReset","safeParse","res","status","data","user","findOne","passwordResetCode","crypto","randomInt","toString","padStart","passwordResetCodeExpiresAt","Date","now","passwordResetToken","undefined","passwordResetTokenExpiresAt","save","sendEmail","to","subject","html","text","err","console","warn","api","post"],"mappings":";;;;AAGO,MAAMA,QAAQ;AAEd,MAAMC,gBAAgBC,OAAS;AAAA,EACpCC,OAAOD,OAAEE,EAASD,MAAAA;AACpB,CAAC;AAI6BD,OAAS;AAAA,EACrCG,SAASH,QAAEI;AAAAA,EACXC,OAAOL,OAAEE,EAASI,SAAAA;AACpB,CAAC;ACHD,MAAMC,uBAIF,OACFC,SACAC,QACkD;AAClD,QAAMC,OAAO,MAAMC,OAAOC,UAAU,UAAUH,GAAG;AAEjD,QAAMI,SAASC,cAAmCC,UAAUP,OAAO;AAEnE,MAAI,CAACK,OAAOV,SAAS;AACnBM,QAAIO,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEd,SAAS;AAAA,MAAOE,OAAO;AAAA,IAAA;AAAA,EAClC;AAEA,QAAM;AAAA,IAAEJ;AAAAA,EAAAA,IAAUY,OAAOK;AACzB,QAAMC,OAAO,MAAMT,KAAKU,QAAQ;AAAA,IAAEnB;AAAAA,EAAAA,CAAO;AAEzC,MAAI,CAACkB,MAAM;AACT,WAAO;AAAA,MAAEhB,SAAS;AAAA,IAAA;AAAA,EACpB;AAEA,QAAMkB,oBAAoBC,OAAOC,UAAU,GAAG,GAAS,EAAEC,WAAWC,SAAS,GAAG,GAAG;AACnF,QAAMC,6BAA6B,IAAIC,KAAKA,KAAKC,QAAQ,KAAK,KAAK,GAAI;AAEvET,OAAKE,oBAAoBA;AACzBF,OAAKO,6BAA6BA;AAClCP,OAAKU,qBAAqBC;AAC1BX,OAAKY,8BAA8BD;AACnC,QAAMX,KAAKa,KAAAA;AAEX,MAAI;AACF,UAAMC,UAAU;AAAA,MACdC,IAAIjC;AAAAA,MACJkC,SAAS,6BAA6Bd,iBAAiB;AAAA,MACvDe,MAAM;AAAA,iDACqCf,iBAAiB;AAAA;AAAA;AAAA,MAG5DgB,MAAM,+BAA+BhB,iBAAiB;AAAA,IAAA,CACvD;AAAA,EACH,SAASiB,KAAK;AACZC,YAAQC,KAAK,uCAAuCF,GAAG;AAAA,EACzD;AAEA,SAAO;AAAA,IAAEnC,SAAS;AAAA,EAAA;AACpB;AAEA,MAAA,UAAe,CAACsC,QAA8B;AAC5CA,MAAIC,KAAK5B,OAA4BP,oBAAoB;AAC3D;"}