customer-registration 0.0.112 → 0.0.113

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.
Files changed (34) hide show
  1. package/.medusa/server/src/api/auth/customer/emailpass/reset-password/route.js +1 -26
  2. package/.medusa/server/src/api/auth/customer/emailpass/route.js +19 -97
  3. package/.medusa/server/src/api/auth/customer/phonepass/register/route.js +50 -0
  4. package/.medusa/server/src/api/auth/customer/phonepass/route.js +105 -0
  5. package/.medusa/server/src/api/middlewares/{block-pending-customer.js → guard-account-deletion.js} +4 -4
  6. package/.medusa/server/src/api/middlewares/ip-rate-limit.js +48 -0
  7. package/.medusa/server/src/api/middlewares/validate-customer-registration.js +60 -0
  8. package/.medusa/server/src/api/middlewares.js +17 -4
  9. package/.medusa/server/src/api/store/customers/account-deletion/cancel-request/route.js +16 -6
  10. package/.medusa/server/src/api/store/customers/account-deletion/validators.js +11 -3
  11. package/.medusa/server/src/api/store/customers/me/contact/route.js +95 -0
  12. package/.medusa/server/src/api/store/customers/me/contact/verify/route.js +83 -0
  13. package/.medusa/server/src/api/store/customers/me/route.js +53 -0
  14. package/.medusa/server/src/api/store/customers/otp/send/route.js +1 -6
  15. package/.medusa/server/src/api/store/customers/otp/verify/route.js +95 -3
  16. package/.medusa/server/src/api/store/customers/route.js +89 -0
  17. package/.medusa/server/src/config.js +32 -23
  18. package/.medusa/server/src/modules/otp-verification/service.js +72 -1
  19. package/.medusa/server/src/providers/phonepass/index.js +9 -0
  20. package/.medusa/server/src/providers/phonepass/service.js +133 -0
  21. package/.medusa/server/src/subscribers/password-reset.js +1 -42
  22. package/.medusa/server/src/workflows/change-password.js +40 -64
  23. package/.medusa/server/src/workflows/send-contact-change-otp-workflow.js +41 -0
  24. package/.medusa/server/src/workflows/steps/determine-contact-method-step.js +8 -2
  25. package/.medusa/server/src/workflows/steps/generate-contact-change-otp-step.js +24 -0
  26. package/.medusa/server/src/workflows/steps/index.js +6 -2
  27. package/.medusa/server/src/workflows/steps/send-notification-step.js +1 -11
  28. package/.medusa/server/src/workflows/steps/sync-phonepass-entity-id-step.js +63 -0
  29. package/.medusa/server/src/workflows/steps/update-password-step.js +21 -29
  30. package/.medusa/server/src/workflows/update-contact-workflow.js +100 -0
  31. package/.medusa/server/src/workflows/verify-phone.js +11 -4
  32. package/README.md +363 -223
  33. package/package.json +3 -1
  34. package/.medusa/server/src/subscribers/customer-updated.js +0 -100
@@ -1,17 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const http_1 = require("@medusajs/framework/http");
4
- const block_pending_customer_1 = require("./middlewares/block-pending-customer");
4
+ const guard_account_deletion_1 = require("./middlewares/guard-account-deletion");
5
+ const validate_customer_registration_1 = require("./middlewares/validate-customer-registration");
6
+ const ip_rate_limit_1 = require("./middlewares/ip-rate-limit");
5
7
  exports.default = (0, http_1.defineMiddlewares)({
6
8
  routes: [
7
9
  {
8
10
  matcher: "/auth/customer/*",
9
- middlewares: [block_pending_customer_1.blockPendingCustomer],
11
+ middlewares: [guard_account_deletion_1.guardAccountDeletion],
10
12
  },
11
13
  {
12
14
  matcher: "/store/*",
13
- middlewares: [block_pending_customer_1.blockPendingCustomer],
15
+ middlewares: [guard_account_deletion_1.guardAccountDeletion],
16
+ },
17
+ {
18
+ matcher: "/store/customers",
19
+ method: ["POST"],
20
+ middlewares: [validate_customer_registration_1.validateCustomerRegistration],
21
+ },
22
+ {
23
+ // Unauthenticated endpoint — rate-limit per IP to prevent OTP spam
24
+ matcher: "/store/customers/account-deletion/cancel-request",
25
+ method: ["POST"],
26
+ middlewares: [(0, ip_rate_limit_1.ipRateLimit)({ max: 5, windowMs: 60_000 })],
14
27
  },
15
28
  ],
16
29
  });
17
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYXBpL21pZGRsZXdhcmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsbURBQTREO0FBQzVELGlGQUEyRTtBQUUzRSxrQkFBZSxJQUFBLHdCQUFpQixFQUFDO0lBQy9CLE1BQU0sRUFBRTtRQUNOO1lBQ0UsT0FBTyxFQUFFLGtCQUFrQjtZQUMzQixXQUFXLEVBQUUsQ0FBQyw2Q0FBb0IsQ0FBQztTQUNwQztRQUNEO1lBQ0UsT0FBTyxFQUFFLFVBQVU7WUFDbkIsV0FBVyxFQUFFLENBQUMsNkNBQW9CLENBQUM7U0FDcEM7S0FDRjtDQUNGLENBQUMsQ0FBQSJ9
30
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYXBpL21pZGRsZXdhcmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsbURBQTREO0FBQzVELGlGQUEyRTtBQUMzRSxpR0FBMkY7QUFDM0YsK0RBQXlEO0FBRXpELGtCQUFlLElBQUEsd0JBQWlCLEVBQUM7SUFDL0IsTUFBTSxFQUFFO1FBQ047WUFDRSxPQUFPLEVBQUUsa0JBQWtCO1lBQzNCLFdBQVcsRUFBRSxDQUFDLDZDQUFvQixDQUFDO1NBQ3BDO1FBQ0Q7WUFDRSxPQUFPLEVBQUUsVUFBVTtZQUNuQixXQUFXLEVBQUUsQ0FBQyw2Q0FBb0IsQ0FBQztTQUNwQztRQUNEO1lBQ0UsT0FBTyxFQUFFLGtCQUFrQjtZQUMzQixNQUFNLEVBQUUsQ0FBQyxNQUFNLENBQUM7WUFDaEIsV0FBVyxFQUFFLENBQUMsNkRBQTRCLENBQUM7U0FDNUM7UUFDRDtZQUNFLG1FQUFtRTtZQUNuRSxPQUFPLEVBQUUsa0RBQWtEO1lBQzNELE1BQU0sRUFBRSxDQUFDLE1BQU0sQ0FBQztZQUNoQixXQUFXLEVBQUUsQ0FBQyxJQUFBLDJCQUFXLEVBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1NBQ3pEO0tBQ0Y7Q0FDRixDQUFDLENBQUEifQ==
@@ -14,16 +14,26 @@ const POST = async (req, res) => {
14
14
  if (!parsed.success) {
15
15
  throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, parsed.error.errors.map((e) => e.message).join("; ") || "Invalid input");
16
16
  }
17
- const email = parsed.data.email.toLowerCase().trim();
17
+ const { email, phone } = parsed.data;
18
18
  const knex = req.scope.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
19
19
  if (!knex) {
20
20
  throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Database connection not available");
21
21
  }
22
- const result = await knex.raw(`SELECT id FROM customer WHERE email = ? LIMIT 1`, [email]);
23
- const row = result.rows?.[0] ?? result[0]?.[0];
24
- const customerId = row?.id;
22
+ let customerId;
23
+ if (email) {
24
+ const result = await knex.raw(`SELECT id FROM customer WHERE email = ? LIMIT 1`, [email.toLowerCase().trim()]);
25
+ const row = result.rows?.[0] ?? result[0]?.[0];
26
+ customerId = row?.id;
27
+ }
28
+ else if (phone) {
29
+ const result = await knex.raw(`SELECT id FROM customer WHERE phone = ? LIMIT 1`, [phone.trim()]);
30
+ const row = result.rows?.[0] ?? result[0]?.[0];
31
+ customerId = row?.id;
32
+ }
25
33
  if (!customerId) {
26
- throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, "Customer not found with this email");
34
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, email
35
+ ? "Customer not found with this email"
36
+ : "Customer not found with this phone number");
27
37
  }
28
38
  const accountDeletionService = req.scope.resolve(account_deletion_request_1.ACCOUNT_DELETION_REQUEST_MODULE);
29
39
  const active = await accountDeletionService.getActiveByCustomerId(customerId);
@@ -42,4 +52,4 @@ const POST = async (req, res) => {
42
52
  });
43
53
  };
44
54
  exports.POST = POST;
45
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9hY2NvdW50LWRlbGV0aW9uL2NhbmNlbC1yZXF1ZXN0L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUNBLHFEQUdrQztBQUNsQyw4RkFBaUc7QUFFakcsc0dBQTRGO0FBQzVGLG1HQUF3RTtBQUN4RSw4Q0FBbUQ7QUFFNUMsTUFBTSxJQUFJLEdBQUcsS0FBSyxFQUFFLEdBQWtCLEVBQUUsR0FBbUIsRUFBRSxFQUFFO0lBQ3BFLE1BQU0sTUFBTSxHQUFHLGdDQUFtQixDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFBO0lBQzVELElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDcEIsTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLFlBQVksRUFDOUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLGVBQWUsQ0FDeEUsQ0FBQTtJQUNILENBQUM7SUFFRCxNQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQTtJQUVwRCxNQUFNLElBQUksR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxhQUFhLENBQUMsQ0FBQTtJQUN2RSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDVixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLEVBQ2xDLG1DQUFtQyxDQUNwQyxDQUFBO0lBQ0gsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FDM0IsaURBQWlELEVBQ2pELENBQUMsS0FBSyxDQUFDLENBQ1IsQ0FBQTtJQUNELE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUM5QyxNQUFNLFVBQVUsR0FBRyxHQUFHLEVBQUUsRUFBRSxDQUFBO0lBRTFCLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNoQixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUMzQixvQ0FBb0MsQ0FDckMsQ0FBQTtJQUNILENBQUM7SUFFRCxNQUFNLHNCQUFzQixHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUM5QywwREFBK0IsQ0FDaEMsQ0FBQTtJQUVELE1BQU0sTUFBTSxHQUFHLE1BQU0sc0JBQXNCLENBQUMscUJBQXFCLENBQUMsVUFBVSxDQUFDLENBQUE7SUFDN0UsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ1osTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLFNBQVMsRUFDM0Isb0RBQW9ELENBQ3JELENBQUE7SUFDSCxDQUFDO0lBRUQsTUFBTSxFQUFFLE1BQU0sRUFBRSxjQUFjLEVBQUUsR0FBRyxNQUFNLElBQUEsMkJBQWUsRUFBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDO1FBQ3RFLEtBQUssRUFBRTtZQUNMLFdBQVcsRUFBRSxVQUFVO1lBQ3ZCLElBQUksRUFBRSw2QkFBVSxDQUFDLHVCQUF1QjtTQUN6QztLQUNGLENBQUMsQ0FBQTtJQUVGLE9BQU8sR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDMUIsS0FBSyxFQUFFLGNBQWMsQ0FBQyxLQUFLO1FBQzNCLFVBQVUsRUFBRSxjQUFjLENBQUMsVUFBVTtLQUN0QyxDQUFDLENBQUE7QUFDSixDQUFDLENBQUE7QUF4RFksUUFBQSxJQUFJLFFBd0RoQiJ9
55
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9hY2NvdW50LWRlbGV0aW9uL2NhbmNlbC1yZXF1ZXN0L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUNBLHFEQUdrQztBQUNsQyw4RkFBaUc7QUFFakcsc0dBQTRGO0FBQzVGLG1HQUF3RTtBQUN4RSw4Q0FBbUQ7QUFFNUMsTUFBTSxJQUFJLEdBQUcsS0FBSyxFQUFFLEdBQWtCLEVBQUUsR0FBbUIsRUFBRSxFQUFFO0lBQ3BFLE1BQU0sTUFBTSxHQUFHLGdDQUFtQixDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFBO0lBQzVELElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDcEIsTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLFlBQVksRUFDOUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLGVBQWUsQ0FDeEUsQ0FBQTtJQUNILENBQUM7SUFFRCxNQUFNLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUE7SUFFcEMsTUFBTSxJQUFJLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsaUNBQXlCLENBQUMsYUFBYSxDQUFDLENBQUE7SUFDdkUsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ1YsTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLGdCQUFnQixFQUNsQyxtQ0FBbUMsQ0FDcEMsQ0FBQTtJQUNILENBQUM7SUFFRCxJQUFJLFVBQThCLENBQUE7SUFFbEMsSUFBSSxLQUFLLEVBQUUsQ0FBQztRQUNWLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLEdBQUcsQ0FDM0IsaURBQWlELEVBQ2pELENBQUMsS0FBSyxDQUFDLFdBQVcsRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDLENBQzdCLENBQUE7UUFDRCxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUE7UUFDOUMsVUFBVSxHQUFHLEdBQUcsRUFBRSxFQUFFLENBQUE7SUFDdEIsQ0FBQztTQUFNLElBQUksS0FBSyxFQUFFLENBQUM7UUFDakIsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsR0FBRyxDQUMzQixpREFBaUQsRUFDakQsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FDZixDQUFBO1FBQ0QsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFBO1FBQzlDLFVBQVUsR0FBRyxHQUFHLEVBQUUsRUFBRSxDQUFBO0lBQ3RCLENBQUM7SUFFRCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDaEIsTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLFNBQVMsRUFDM0IsS0FBSztZQUNILENBQUMsQ0FBQyxvQ0FBb0M7WUFDdEMsQ0FBQyxDQUFDLDJDQUEyQyxDQUNoRCxDQUFBO0lBQ0gsQ0FBQztJQUVELE1BQU0sc0JBQXNCLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQzlDLDBEQUErQixDQUNoQyxDQUFBO0lBRUQsTUFBTSxNQUFNLEdBQUcsTUFBTSxzQkFBc0IsQ0FBQyxxQkFBcUIsQ0FBQyxVQUFVLENBQUMsQ0FBQTtJQUM3RSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDWixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUMzQixvREFBb0QsQ0FDckQsQ0FBQTtJQUNILENBQUM7SUFFRCxNQUFNLEVBQUUsTUFBTSxFQUFFLGNBQWMsRUFBRSxHQUFHLE1BQU0sSUFBQSwyQkFBZSxFQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUM7UUFDdEUsS0FBSyxFQUFFO1lBQ0wsV0FBVyxFQUFFLFVBQVU7WUFDdkIsSUFBSSxFQUFFLDZCQUFVLENBQUMsdUJBQXVCO1NBQ3pDO0tBQ0YsQ0FBQyxDQUFBO0lBRUYsT0FBTyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQztRQUMxQixLQUFLLEVBQUUsY0FBYyxDQUFDLEtBQUs7UUFDM0IsVUFBVSxFQUFFLGNBQWMsQ0FBQyxVQUFVO0tBQ3RDLENBQUMsQ0FBQTtBQUNKLENBQUMsQ0FBQTtBQXJFWSxRQUFBLElBQUksUUFxRWhCIn0=
@@ -9,11 +9,19 @@ exports.ConfirmDeletionSchema = zod_1.z.object({
9
9
  token: zod_1.z.string().min(1, "Token is required"),
10
10
  code: zod_1.z.string().min(1, "OTP code is required"),
11
11
  });
12
- exports.CancelRequestSchema = zod_1.z.object({
13
- email: zod_1.z.string().email("Valid email is required"),
12
+ exports.CancelRequestSchema = zod_1.z
13
+ .object({
14
+ email: zod_1.z.string().email("Valid email address").optional(),
15
+ phone: zod_1.z
16
+ .string()
17
+ .regex(/^\+?[0-9]{8,15}$/, "Invalid phone number format")
18
+ .optional(),
19
+ })
20
+ .refine((data) => data.email || data.phone, {
21
+ message: "Either email or phone is required",
14
22
  });
15
23
  exports.ConfirmCancelSchema = zod_1.z.object({
16
24
  token: zod_1.z.string().min(1, "Token is required"),
17
25
  code: zod_1.z.string().min(1, "OTP code is required"),
18
26
  });
19
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcGkvc3RvcmUvY3VzdG9tZXJzL2FjY291bnQtZGVsZXRpb24vdmFsaWRhdG9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBdUI7QUFFVixRQUFBLHFCQUFxQixHQUFHLE9BQUMsQ0FBQyxNQUFNLENBQUM7SUFDNUMsTUFBTSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxFQUFFO0NBQ25ELENBQUMsQ0FBQTtBQUVXLFFBQUEscUJBQXFCLEdBQUcsT0FBQyxDQUFDLE1BQU0sQ0FBQztJQUM1QyxLQUFLLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsbUJBQW1CLENBQUM7SUFDN0MsSUFBSSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLHNCQUFzQixDQUFDO0NBQ2hELENBQUMsQ0FBQTtBQUVXLFFBQUEsbUJBQW1CLEdBQUcsT0FBQyxDQUFDLE1BQU0sQ0FBQztJQUMxQyxLQUFLLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQztDQUNuRCxDQUFDLENBQUE7QUFFVyxRQUFBLG1CQUFtQixHQUFHLE9BQUMsQ0FBQyxNQUFNLENBQUM7SUFDMUMsS0FBSyxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLG1CQUFtQixDQUFDO0lBQzdDLElBQUksRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxzQkFBc0IsQ0FBQztDQUNoRCxDQUFDLENBQUEifQ==
27
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcGkvc3RvcmUvY3VzdG9tZXJzL2FjY291bnQtZGVsZXRpb24vdmFsaWRhdG9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2QkFBdUI7QUFFVixRQUFBLHFCQUFxQixHQUFHLE9BQUMsQ0FBQyxNQUFNLENBQUM7SUFDNUMsTUFBTSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUMsUUFBUSxFQUFFO0NBQ25ELENBQUMsQ0FBQTtBQUVXLFFBQUEscUJBQXFCLEdBQUcsT0FBQyxDQUFDLE1BQU0sQ0FBQztJQUM1QyxLQUFLLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsbUJBQW1CLENBQUM7SUFDN0MsSUFBSSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLHNCQUFzQixDQUFDO0NBQ2hELENBQUMsQ0FBQTtBQUVXLFFBQUEsbUJBQW1CLEdBQUcsT0FBQztLQUNqQyxNQUFNLENBQUM7SUFDTixLQUFLLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDLFFBQVEsRUFBRTtJQUN6RCxLQUFLLEVBQUUsT0FBQztTQUNMLE1BQU0sRUFBRTtTQUNSLEtBQUssQ0FBQyxrQkFBa0IsRUFBRSw2QkFBNkIsQ0FBQztTQUN4RCxRQUFRLEVBQUU7Q0FDZCxDQUFDO0tBQ0QsTUFBTSxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUU7SUFDMUMsT0FBTyxFQUFFLG1DQUFtQztDQUM3QyxDQUFDLENBQUE7QUFFUyxRQUFBLG1CQUFtQixHQUFHLE9BQUMsQ0FBQyxNQUFNLENBQUM7SUFDMUMsS0FBSyxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLG1CQUFtQixDQUFDO0lBQzdDLElBQUksRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxzQkFBc0IsQ0FBQztDQUNoRCxDQUFDLENBQUEifQ==
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.POST = void 0;
7
+ const utils_1 = require("@medusajs/framework/utils");
8
+ const otp_verification_1 = require("../../../../../modules/otp-verification/models/otp-verification");
9
+ const config_1 = require("../../../../../config");
10
+ const send_contact_change_otp_workflow_1 = __importDefault(require("../../../../../workflows/send-contact-change-otp-workflow"));
11
+ const EMAIL_REGEX = /^\S+@\S+\.\S+$/;
12
+ const PHONE_REGEX = /^\+?[0-9]{8,15}$/;
13
+ /**
14
+ * POST /store/customers/me/contact
15
+ *
16
+ * Authenticated endpoint for requesting a phone or email change.
17
+ * Validates the new value, checks for duplicates, generates an OTP with the
18
+ * new_value embedded in the signed JWT (no metadata staging), sends the OTP
19
+ * to the new contact, and returns the token + expiry for use at /verify.
20
+ *
21
+ * Request body (exactly one field required):
22
+ * { phone: string } | { email: string }
23
+ */
24
+ const POST = async (req, res) => {
25
+ const customerId = req.auth_context.actor_id;
26
+ const configModule = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
27
+ const options = (0, config_1.resolveCustomerRegistrationOptions)(configModule);
28
+ const { phone: rawPhone, email: rawEmail } = (req.body ?? {});
29
+ const phone = rawPhone?.trim() || undefined;
30
+ const email = rawEmail?.trim() || undefined;
31
+ if (!phone && !email) {
32
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Either phone or email is required");
33
+ }
34
+ if (phone && email) {
35
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Only one of phone or email can be changed at a time");
36
+ }
37
+ const knex = req.scope.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
38
+ const customerService = req.scope.resolve(utils_1.Modules.CUSTOMER);
39
+ const customer = await customerService.retrieveCustomer(customerId);
40
+ if (phone) {
41
+ if (!PHONE_REGEX.test(phone)) {
42
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Invalid phone number format. Must be 8–15 digits, optionally prefixed with +");
43
+ }
44
+ if (phone === customer.phone) {
45
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "New phone number is the same as the current one");
46
+ }
47
+ // Reject if login.identifier doesn't support phone
48
+ if (options.login.identifier !== "phone" &&
49
+ options.login.identifier !== "both") {
50
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, "Phone-based login is not enabled for this store");
51
+ }
52
+ // Duplicate check
53
+ const existing = await knex.raw(`SELECT id FROM customer WHERE phone = ? AND has_account = true LIMIT 1`, [phone]);
54
+ if (existing.rows?.[0] ?? existing[0]?.[0]) {
55
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.DUPLICATE_ERROR, "A customer with this phone number already exists");
56
+ }
57
+ const { result } = await (0, send_contact_change_otp_workflow_1.default)(req.scope).run({
58
+ input: {
59
+ customer_id: customerId,
60
+ new_value: phone,
61
+ contact_type: "phone",
62
+ otp_type: otp_verification_1.OtpPurpose.PHONE_VERIFICATION,
63
+ },
64
+ });
65
+ return res.status(200).json({ token: result.token, expires_at: result.expires_at });
66
+ }
67
+ // email branch
68
+ if (!EMAIL_REGEX.test(email)) {
69
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Invalid email format");
70
+ }
71
+ if (email === customer.email) {
72
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "New email is the same as the current one");
73
+ }
74
+ // Reject if login.identifier doesn't support email
75
+ if (options.login.identifier !== "email" &&
76
+ options.login.identifier !== "both") {
77
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, "Email-based login is not enabled for this store");
78
+ }
79
+ // Duplicate check
80
+ const existingEmail = await knex.raw(`SELECT id FROM customer WHERE email = ? AND has_account = true LIMIT 1`, [email]);
81
+ if (existingEmail.rows?.[0] ?? existingEmail[0]?.[0]) {
82
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.DUPLICATE_ERROR, "A customer with this email already exists");
83
+ }
84
+ const { result } = await (0, send_contact_change_otp_workflow_1.default)(req.scope).run({
85
+ input: {
86
+ customer_id: customerId,
87
+ new_value: email,
88
+ contact_type: "email",
89
+ otp_type: otp_verification_1.OtpPurpose.EMAIL_VERIFICATION,
90
+ },
91
+ });
92
+ return res.status(200).json({ token: result.token, expires_at: result.expires_at });
93
+ };
94
+ exports.POST = POST;
95
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9tZS9jb250YWN0L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUNBLHFEQUlrQztBQUVsQyxzR0FBNEY7QUFDNUYsa0RBRzhCO0FBQzlCLGlJQUFvRztBQUVwRyxNQUFNLFdBQVcsR0FBRyxnQkFBZ0IsQ0FBQTtBQUNwQyxNQUFNLFdBQVcsR0FBRyxrQkFBa0IsQ0FBQTtBQUV0Qzs7Ozs7Ozs7OztHQVVHO0FBQ0ksTUFBTSxJQUFJLEdBQUcsS0FBSyxFQUN2QixHQUErQixFQUMvQixHQUFtQixFQUNuQixFQUFFO0lBQ0YsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFDLFlBQVksQ0FBQyxRQUFRLENBQUE7SUFFNUMsTUFBTSxZQUFZLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsaUNBQXlCLENBQUMsYUFBYSxDQUFDLENBQUE7SUFDL0UsTUFBTSxPQUFPLEdBQUcsSUFBQSwyQ0FBa0MsRUFBQyxZQUFpQyxDQUFDLENBQUE7SUFFckYsTUFBTSxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksSUFBSSxFQUFFLENBRzNELENBQUE7SUFFRCxNQUFNLEtBQUssR0FBRyxRQUFRLEVBQUUsSUFBSSxFQUFFLElBQUksU0FBUyxDQUFBO0lBQzNDLE1BQU0sS0FBSyxHQUFHLFFBQVEsRUFBRSxJQUFJLEVBQUUsSUFBSSxTQUFTLENBQUE7SUFFM0MsSUFBSSxDQUFDLEtBQUssSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ3JCLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLG1DQUFtQyxDQUNwQyxDQUFBO0lBQ0gsQ0FBQztJQUVELElBQUksS0FBSyxJ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
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.POST = void 0;
7
+ const utils_1 = require("@medusajs/framework/utils");
8
+ const generate_jwt_token_1 = require("@medusajs/medusa/api/auth/utils/generate-jwt-token");
9
+ const otp_verification_1 = require("../../../../../../modules/otp-verification");
10
+ const config_1 = require("../../../../../../config");
11
+ const update_contact_workflow_1 = __importDefault(require("../../../../../../workflows/update-contact-workflow"));
12
+ /**
13
+ * POST /store/customers/me/contact/verify
14
+ *
15
+ * Authenticated endpoint for confirming a phone or email change.
16
+ *
17
+ * 1. Decodes the JWT from /store/customers/me/contact — extracts new_value
18
+ * and contact_type without any database lookup.
19
+ * 2. Validates the OTP code against the stored hashed record.
20
+ * 3. Runs updateContactWorkflow which atomically:
21
+ * - Writes new_value to customer.phone / customer.email
22
+ * - Sets phone_verified / email_verified = true
23
+ * - Updates provider_identity.entity_id (phonepass / emailpass) when the
24
+ * changed field is the active login identifier.
25
+ * 4. Returns the updated customer + a fresh JWT so the caller is immediately
26
+ * re-authenticated with the new identity.
27
+ *
28
+ * Request body: { token: string; code: string }
29
+ */
30
+ const POST = async (req, res) => {
31
+ const customerId = req.auth_context.actor_id;
32
+ const { token, code } = (req.body ?? {});
33
+ if (!token) {
34
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "token is required");
35
+ }
36
+ if (!code) {
37
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "code is required");
38
+ }
39
+ const config = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
40
+ const { http } = config.projectConfig;
41
+ const jwtSecret = http?.jwtSecret || "supersecret";
42
+ const otpService = req.scope.resolve(otp_verification_1.OTP_VERIFICATION_MODULE);
43
+ // Decode the contact-change JWT — new_value lives here, not in metadata
44
+ const decoded = otpService.decodeContactChangeToken(token, jwtSecret);
45
+ // Ensure the token belongs to the authenticated customer
46
+ if (decoded.customer_id !== customerId) {
47
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, "Token does not match the authenticated customer");
48
+ }
49
+ // Validate the OTP record (expiry, attempts, already-used)
50
+ // and verify the submitted code against the stored hash.
51
+ // These are the same private-method-backed operations used by verifyOtp().
52
+ await otpService.verifyOtp({ token, code }, jwtSecret);
53
+ const options = (0, config_1.resolveCustomerRegistrationOptions)(config);
54
+ const { result } = await (0, update_contact_workflow_1.default)(req.scope).run({
55
+ input: {
56
+ customer_id: customerId,
57
+ new_value: decoded.new_value,
58
+ contact_type: decoded.contact_type,
59
+ login_identifier: options.login.identifier,
60
+ },
61
+ });
62
+ // Generate a fresh login token so the caller stays authenticated
63
+ let loginToken;
64
+ try {
65
+ const knex = req.scope.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
66
+ const authRow = await knex.raw(`SELECT id FROM auth_identity WHERE app_metadata->>'customer_id' = ? LIMIT 1`, [customerId]);
67
+ const authIdentityId = (authRow.rows?.[0] ?? authRow[0]?.[0])?.id;
68
+ if (authIdentityId) {
69
+ const authService = req.scope.resolve(utils_1.Modules.AUTH);
70
+ const authIdentity = await authService.retrieveAuthIdentity(authIdentityId);
71
+ loginToken = await (0, generate_jwt_token_1.generateJwtTokenForAuthIdentity)({ authIdentity, actorType: "customer" }, { secret: jwtSecret, expiresIn: http.jwtExpiresIn || "7d" });
72
+ }
73
+ }
74
+ catch {
75
+ // Token generation is best-effort; the contact update already succeeded
76
+ }
77
+ return res.status(200).json({
78
+ customer: result.customer,
79
+ token: loginToken ?? null,
80
+ });
81
+ };
82
+ exports.POST = POST;
83
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9tZS9jb250YWN0L3ZlcmlmeS9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFDQSxxREFJa0M7QUFDbEMsMkZBQW9HO0FBQ3BHLGlGQUFvRjtBQUVwRixxREFHaUM7QUFDakMsa0hBQXVGO0FBRXZGOzs7Ozs7Ozs7Ozs7Ozs7OztHQWlCRztBQUNJLE1BQU0sSUFBSSxHQUFHLEtBQUssRUFDdkIsR0FBK0IsRUFDL0IsR0FBbUIsRUFDbkIsRUFBRTtJQUNGLE1BQU0sVUFBVSxHQUFHLEdBQUcsQ0FBQyxZQUFZLENBQUMsUUFBUSxDQUFBO0lBRTVDLE1BQU0sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBc0MsQ0FBQTtJQUU3RSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDWCxNQUFNLElBQUksbUJBQVcsQ0FBQyxtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQUUsbUJBQW1CLENBQUMsQ0FBQTtJQUM1RSxDQUFDO0lBQ0QsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ1YsTUFBTSxJQUFJLG1CQUFXLENBQUMsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUFFLGtCQUFrQixDQUFDLENBQUE7SUFDM0UsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLGFBQWEsQ0FBQyxDQUFBO0lBQ3pFLE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxNQUFNLENBQUMsYUFBYSxDQUFBO0lBQ3JDLE1BQU0sU0FBUyxHQUFJLElBQUksRUFBRSxTQUFnQyxJQUFJLGFBQWEsQ0FBQTtJQUUxRSxNQUFNLFVBQVUsR0FBRyxHQUFHLENBQUMsS0FBSyxD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
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PATCH = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ /**
6
+ * Override of Medusa's built-in PATCH /store/customers/me.
7
+ *
8
+ * Phone and email changes require OTP verification and must go through the
9
+ * dedicated endpoint:
10
+ * POST /store/customers/me/contact — request the change + receive OTP token
11
+ * POST /store/customers/me/contact/verify — submit OTP code to apply the change
12
+ *
13
+ * All other profile fields (first_name, last_name, etc.) are applied directly
14
+ * via the customer service, matching Medusa's default behaviour.
15
+ */
16
+ const PATCH = async (req, res) => {
17
+ const customerId = req.auth_context.actor_id;
18
+ const body = (req.body ?? {});
19
+ if (body.phone !== undefined) {
20
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, "Phone changes require OTP verification. Use POST /store/customers/me/contact to request a change.");
21
+ }
22
+ if (body.email !== undefined) {
23
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, "Email changes require OTP verification. Use POST /store/customers/me/contact to request a change.");
24
+ }
25
+ const customerService = req.scope.resolve(utils_1.Modules.CUSTOMER);
26
+ if (Object.keys(body).length > 0) {
27
+ await customerService.updateCustomers(customerId, body);
28
+ }
29
+ const updated = await refetchCustomer(customerId, req.scope);
30
+ return res.status(200).json({ customer: updated });
31
+ };
32
+ exports.PATCH = PATCH;
33
+ async function refetchCustomer(customerId, scope) {
34
+ const remoteQuery = scope.resolve(utils_1.ContainerRegistrationKeys.REMOTE_QUERY);
35
+ const queryObject = (0, utils_1.remoteQueryObjectFromString)({
36
+ entryPoint: "customer",
37
+ variables: { filters: { id: customerId } },
38
+ fields: [
39
+ "id",
40
+ "email",
41
+ "first_name",
42
+ "last_name",
43
+ "phone",
44
+ "has_account",
45
+ "metadata",
46
+ "created_at",
47
+ "updated_at",
48
+ ],
49
+ });
50
+ const customers = await remoteQuery(queryObject);
51
+ return customers[0];
52
+ }
53
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9tZS9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFFQSxxREFLa0M7QUFHbEM7Ozs7Ozs7Ozs7R0FVRztBQUNJLE1BQU0sS0FBSyxHQUFHLEtBQUssRUFDeEIsR0FBK0IsRUFDL0IsR0FBbUIsRUFDbkIsRUFBRTtJQUNGLE1BQU0sVUFBVSxHQUFHLEdBQUcsQ0FBQyxZQUFZLENBQUMsUUFBUSxDQUFBO0lBQzVDLE1BQU0sSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksSUFBSSxFQUFFLENBQTRCLENBQUE7SUFFeEQsSUFBSSxJQUFJLENBQUMsS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQzdCLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQzdCLG1HQUFtRyxDQUNwRyxDQUFBO0lBQ0gsQ0FBQztJQUVELElBQUksSUFBSSxDQUFDLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUM3QixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsV0FBVyxFQUM3QixtR0FBbUcsQ0FDcEcsQ0FBQTtJQUNILENBQUM7SUFFRCxNQUFNLGVBQWUsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBeUIsZUFBTyxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBRW5GLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDakMsTUFBTSxlQUFlLENBQUMsZUFBZSxDQUNuQyxVQUFVLEVBQ1YsSUFBNkQsQ0FDOUQsQ0FBQTtJQUNILENBQUM7SUFFRCxNQUFNLE9BQU8sR0FBRyxNQUFNLGVBQWUsQ0FBQyxVQUFVLEVBQUUsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFBO0lBQzVELE9BQU8sR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQTtBQUNwRCxDQUFDLENBQUE7QUFoQ1ksUUFBQSxLQUFLLFNBZ0NqQjtBQUVELEtBQUssVUFBVSxlQUFlLENBQUMsVUFBa0IsRUFBRSxLQUFzQjtJQUN2RSxNQUFNLFdBQVcsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLFlBQVksQ0FBQyxDQUFBO0lBQ3pFLE1BQU0sV0FBVyxHQUFHLElBQUEsbUNBQTJCLEVBQUM7UUFDOUMsVUFBVSxFQUFFLFVBQVU7UUFDdEIsU0FBUyxFQUFFLEVBQUUsT0FBTyxFQUFFLEVBQUUsRUFBRSxFQUFFLFVBQVUsRUFBRSxFQUFFO1FBQzFDLE1BQU0sRUFBRTtZQUNOLElBQUk7WUFDSixPQUFPO1lBQ1AsWUFBWTtZQUNaLFdBQVc7WUFDWCxPQUFPO1lBQ1AsYUFBYTtZQUNiLFVBQVU7WUFDVixZQUFZO1lBQ1osWUFBWTtTQUNiO0tBQ0YsQ0FBQyxDQUFBO0lBQ0YsTUFBTSxTQUFTLEdBQUcsTUFBTSxXQUFXLENBQUMsV0FBVyxDQUFDLENBQUE7SUFDaEQsT0FBTyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUE7QUFDckIsQ0FBQyJ9
@@ -9,15 +9,12 @@ const otp_verification_1 = require("../../../../../modules/otp-verification/mode
9
9
  const send_otp_workflow_1 = __importDefault(require("../../../../../workflows/send-otp-workflow"));
10
10
  const POST = async (req, res) => {
11
11
  const { customer_id, type } = req.body;
12
- console.log("=== [otp/send] Route Called ===");
13
- console.log("Request body:", { customer_id, type });
14
12
  if (!customer_id) {
15
13
  throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "customer_id is required");
16
14
  }
17
15
  if (!type || !Object.values(otp_verification_1.OtpPurpose).includes(type)) {
18
16
  throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Type must be one of: ${Object.values(otp_verification_1.OtpPurpose).join(", ")}`);
19
17
  }
20
- console.log("=== [otp/send] Starting Workflow ===");
21
18
  // Execute workflow
22
19
  const { result } = await (0, send_otp_workflow_1.default)(req.scope).run({
23
20
  input: {
@@ -25,12 +22,10 @@ const POST = async (req, res) => {
25
22
  type: type,
26
23
  },
27
24
  });
28
- console.log("=== [otp/send] Workflow Completed ===");
29
- console.log("Result:", { token: result.token, expires_at: result.expires_at });
30
25
  return res.status(200).json({
31
26
  token: result.token,
32
27
  expires_at: result.expires_at,
33
28
  });
34
29
  };
35
30
  exports.POST = POST;
36
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9vdHAvc2VuZC9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFDQSxxREFBdUQ7QUFDdkQsc0dBQTRGO0FBQzVGLG1HQUF3RTtBQUVqRSxNQUFNLElBQUksR0FBRyxLQUFLLEVBQUUsR0FBa0IsRUFBRSxHQUFtQixFQUFFLEVBQUU7SUFDcEUsTUFBTSxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsR0FBRyxHQUFHLENBQUMsSUFHakMsQ0FBQTtJQUVELE9BQU8sQ0FBQyxHQUFHLENBQUMsaUNBQWlDLENBQUMsQ0FBQTtJQUM5QyxPQUFPLENBQUMsR0FBRyxDQUFDLGVBQWUsRUFBRSxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFBO0lBRW5ELElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUNqQixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5Qix5QkFBeUIsQ0FDMUIsQ0FBQTtJQUNILENBQUM7SUFFRCxJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyw2QkFBVSxDQUFDLENBQUMsUUFBUSxDQUFDLElBQWtCLENBQUMsRUFBRSxDQUFDO1FBQ3JFLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLHdCQUF3QixNQUFNLENBQUMsTUFBTSxDQUFDLDZCQUFVLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FDL0QsQ0FBQTtJQUNILENBQUM7SUFFRCxPQUFPLENBQUMsR0FBRyxDQUFDLHNDQUFzQyxDQUFDLENBQUE7SUFFbkQsbUJBQW1CO0lBQ25CLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLElBQUEsMkJBQWUsRUFBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDO1FBQ3RELEtBQUssRUFBRTtZQUNMLFdBQVc7WUFDWCxJQUFJLEVBQUUsSUFBa0I7U0FDekI7S0FDRixDQUFDLENBQUE7SUFFRixPQUFPLENBQUMsR0FBRyxDQUFDLHVDQUF1QyxDQUFDLENBQUE7SUFDcEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsRUFBRSxLQUFLLEVBQUUsTUFBTSxDQUFDLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUE7SUFFOUUsT0FBTyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQztRQUMxQixLQUFLLEVBQUUsTUFBTSxDQUFDLEtBQUs7UUFDbkIsVUFBVSxFQUFFLE1BQU0sQ0FBQyxVQUFVO0tBQzlCLENBQUMsQ0FBQTtBQUNKLENBQUMsQ0FBQTtBQXhDWSxRQUFBLElBQUksUUF3Q2hCIn0=
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9vdHAvc2VuZC9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFDQSxxREFBdUQ7QUFDdkQsc0dBQTRGO0FBQzVGLG1HQUF3RTtBQUVqRSxNQUFNLElBQUksR0FBRyxLQUFLLEVBQUUsR0FBa0IsRUFBRSxHQUFtQixFQUFFLEVBQUU7SUFDcEUsTUFBTSxFQUFFLFdBQVcsRUFBRSxJQUFJLEVBQUUsR0FBRyxHQUFHLENBQUMsSUFHakMsQ0FBQTtJQUVELElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUNqQixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5Qix5QkFBeUIsQ0FDMUIsQ0FBQTtJQUNILENBQUM7SUFFRCxJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyw2QkFBVSxDQUFDLENBQUMsUUFBUSxDQUFDLElBQWtCLENBQUMsRUFBRSxDQUFDO1FBQ3JFLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLHdCQUF3QixNQUFNLENBQUMsTUFBTSxDQUFDLDZCQUFVLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FDL0QsQ0FBQTtJQUNILENBQUM7SUFHRCxtQkFBbUI7SUFDbkIsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU0sSUFBQSwyQkFBZSxFQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUM7UUFDdEQsS0FBSyxFQUFFO1lBQ0wsV0FBVztZQUNYLElBQUksRUFBRSxJQUFrQjtTQUN6QjtLQUNGLENBQUMsQ0FBQTtJQUVGLE9BQU8sR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDMUIsS0FBSyxFQUFFLE1BQU0sQ0FBQyxLQUFLO1FBQ25CLFVBQVUsRUFBRSxNQUFNLENBQUMsVUFBVTtLQUM5QixDQUFDLENBQUE7QUFDSixDQUFDLENBQUE7QUFqQ1ksUUFBQSxJQUFJLFFBaUNoQiJ9
@@ -5,10 +5,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.POST = void 0;
7
7
  const utils_1 = require("@medusajs/framework/utils");
8
+ const generate_jwt_token_1 = require("@medusajs/medusa/api/auth/utils/generate-jwt-token");
8
9
  const otp_verification_1 = require("../../../../../modules/otp-verification");
9
10
  const otp_verification_2 = require("../../../../../modules/otp-verification/models/otp-verification");
10
11
  const verify_email_1 = __importDefault(require("../../../../../workflows/verify-email"));
11
12
  const verify_phone_1 = __importDefault(require("../../../../../workflows/verify-phone"));
13
+ const config_1 = require("../../../../../config");
12
14
  const POST = async (req, res) => {
13
15
  const { token, code } = req.body;
14
16
  if (!token) {
@@ -21,12 +23,14 @@ const POST = async (req, res) => {
21
23
  const otpService = req.scope.resolve(otp_verification_1.OTP_VERIFICATION_MODULE);
22
24
  // Get JWT secret from config
23
25
  const config = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
24
- const jwtSecret = config?.projectConfig?.http?.jwtSecret || "supersecret";
26
+ const { http } = config.projectConfig;
27
+ const jwtSecret = http?.jwtSecret || "supersecret";
25
28
  // Verify OTP
26
29
  const verifyResult = await otpService.verifyOtp({
27
30
  token,
28
31
  code,
29
32
  }, jwtSecret);
33
+ const loginOptions = (0, config_1.resolveCustomerRegistrationOptions)(config);
30
34
  // Execute workflow based on type
31
35
  let result;
32
36
  if (verifyResult.type === otp_verification_2.OtpPurpose.EMAIL_VERIFICATION) {
@@ -45,6 +49,7 @@ const POST = async (req, res) => {
45
49
  const { result: workflowResult } = await (0, verify_phone_1.default)(req.scope).run({
46
50
  input: {
47
51
  customer_id: verifyResult.customer_id,
52
+ login_identifier: loginOptions.login.identifier,
48
53
  },
49
54
  });
50
55
  result = {
@@ -56,7 +61,94 @@ const POST = async (req, res) => {
56
61
  else {
57
62
  throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Invalid verification type");
58
63
  }
59
- return res.status(200).json(result);
64
+ // When login.identifier is "both", automatically create the complementary
65
+ // auth identity (phonepass <-> emailpass) so the customer can log in with
66
+ // either provider without a separate registration step.
67
+ await ensureComplementaryAuthIdentity(result.customer?.id ?? "", result.customer ?? {}, verifyResult.type, loginOptions.login.identifier, req.scope).catch(() => {
68
+ // Non-fatal — customer can still log in with their original provider.
69
+ // The next successful OTP verification will retry.
70
+ });
71
+ let loginToken;
72
+ try {
73
+ const knex = req.scope.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
74
+ const authRow = await knex.raw(`SELECT id FROM auth_identity WHERE app_metadata->>'customer_id' = ? LIMIT 1`, [result.customer?.id ?? ""]);
75
+ const authIdentityId = (authRow.rows?.[0] ?? authRow[0]?.[0])?.id;
76
+ if (authIdentityId && jwtSecret) {
77
+ const authService = req.scope.resolve(utils_1.Modules.AUTH);
78
+ const authIdentity = await authService.retrieveAuthIdentity(authIdentityId);
79
+ loginToken = await (0, generate_jwt_token_1.generateJwtTokenForAuthIdentity)({ authIdentity, actorType: "customer" }, { secret: jwtSecret, expiresIn: http.jwtExpiresIn || "7d" });
80
+ }
81
+ }
82
+ catch {
83
+ // token generation is best-effort; verification result is still returned
84
+ }
85
+ return res.status(200).json({
86
+ ...result,
87
+ token: loginToken ?? null,
88
+ needs_login: !loginToken,
89
+ });
60
90
  };
61
91
  exports.POST = POST;
62
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9vdHAvdmVyaWZ5L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUNBLHFEQUdrQztBQUNsQyw4RUFBaUY7QUFFakYsc0dBQTRGO0FBQzVGLHlGQUF1RTtBQUN2RSx5RkFBdUU7QUFFaEUsTUFBTSxJQUFJLEdBQUcsS0FBSyxFQUFFLEdBQWtCLEVBQUUsR0FBbUIsRUFBRSxFQUFFO0lBQ3BFLE1BQU0sRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLEdBQUcsR0FBRyxDQUFDLElBRzNCLENBQUE7SUFFRCxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDWCxNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5QixtQkFBbUIsQ0FDcEIsQ0FBQTtJQUNILENBQUM7SUFFRCxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDVixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5QixzQkFBc0IsQ0FDdkIsQ0FBQTtJQUNILENBQUM7SUFFRCw4QkFBOEI7SUFDOUIsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQ2xDLDBDQUF1QixDQUNFLENBQUE7SUFFM0IsNkJBQTZCO0lBQzdCLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLGFBQWEsQ0FBQyxDQUFBO0lBQ3pFLE1BQU0sU0FBUyxHQUFJLE1BQU0sRUFBRSxhQUFxQixFQUFFLElBQUksRUFBRSxTQUFTLElBQUksYUFBYSxDQUFBO0lBRWxGLGFBQWE7SUFDYixNQUFNLFlBQVksR0FBRyxNQUFNLFVBQVUsQ0FBQyxTQUFTLENBQzdDO1FBQ0UsS0FBSztRQUNMLElBQUk7S0FDTCxFQUNELFNBQVMsQ0FDVixDQUFBO0lBRUQsaUNBQWlDO0lBQ2pDLElBQUksTUFBVyxDQUFBO0lBRWYsSUFBSSxZQUFZLENBQUMsSUFBSSxLQUFLLDZCQUFVLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUN4RCxNQUFNLEVBQUUsTUFBTSxFQUFFLGNBQWMsRUFBRSxHQUFHLE1BQU0sSUFBQSxzQkFBbUIsRUFBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDO1lBQzFFLEtBQUssRUFBRTtnQkFDTCxXQUFXLEVBQUUsWUFBWSxDQUFDLFdBQVc7YUFDdEM7U0FDRixDQUFDLENBQUE7UUFDRixNQUFNLEdBQUc7WUFDUCxRQUFRLEVBQUUsSUFBSTtZQUNkLFFBQVEsRUFBRSxjQUFjLENBQUMsUUFBUTtZQUNqQyxjQUFjLEVBQUUsY0FBYyxDQUFDLGNBQWM7U0FDOUMsQ0FBQTtJQUNILENBQUM7U0FBTSxJQUFJLFlBQVksQ0FBQyxJQUFJLEtBQUssNkJBQVUsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBQy9ELE1BQU0sRUFBRSxNQUFNLEVBQUUsY0FBYyxFQUFFLEdBQUcsTUFBTSxJQUFBLHNCQUFtQixFQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUM7WUFDMUUsS0FBSyxFQUFFO2dCQUNMLFdBQVcsRUFBRSxZQUFZLENBQUMsV0FBVzthQUN0QztTQUNGLENBQUMsQ0FBQTtRQUNGLE1BQU0sR0FBRztZQUNQLFFBQVEsRUFBRSxJQUFJO1lBQ2QsUUFBUSxFQUFFLGNBQWMsQ0FBQyxRQUFRO1lBQ2pDLGNBQWMsRUFBRSxjQUFjLENBQUMsY0FBYztTQUM5QyxDQUFBO0lBQ0gsQ0FBQztTQUFNLENBQUM7UUFDTixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5QiwyQkFBMkIsQ0FDNUIsQ0FBQTtJQUNILENBQUM7SUFFRCxPQUFPLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFBO0FBQ3JDLENBQUMsQ0FBQTtBQXZFWSxRQUFBLElBQUksUUF1RWhCIn0=
92
+ /**
93
+ * When `login.identifier` is "both", a customer who registered with one
94
+ * provider (emailpass or phonepass) needs the complementary provider identity
95
+ * created so both login methods work.
96
+ *
97
+ * Strategy: attach a new provider_identity to the EXISTING auth identity
98
+ * (rather than creating a second auth identity) and copy the scrypt password
99
+ * hash. Both emailpass and phonepass use the same scrypt-kdf format, so the
100
+ * hash is portable between them as long as this plugin controls both providers.
101
+ */
102
+ async function ensureComplementaryAuthIdentity(customerId, customer, otpType, loginIdentifier, scope) {
103
+ if (loginIdentifier !== "both")
104
+ return;
105
+ if (!customerId)
106
+ return;
107
+ const knex = scope.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
108
+ const authService = scope.resolve(utils_1.Modules.AUTH);
109
+ // Find the customer's primary auth identity ID (used as the anchor for the
110
+ // new provider identity).
111
+ const authRow = await knex.raw(`SELECT id FROM auth_identity WHERE app_metadata->>'customer_id' = ? LIMIT 1`, [customerId]);
112
+ const authIdentityId = (authRow.rows?.[0] ?? authRow[0]?.[0])?.id;
113
+ if (!authIdentityId)
114
+ return;
115
+ if (otpType === otp_verification_2.OtpPurpose.PHONE_VERIFICATION && customer.phone) {
116
+ // Check if phonepass provider identity already exists for this customer.
117
+ const existing = await knex.raw(`SELECT pi.id FROM provider_identity pi
118
+ WHERE pi.auth_identity_id = ? AND pi.provider = 'phonepass' LIMIT 1`, [authIdentityId]);
119
+ if (existing.rows?.[0] ?? existing[0]?.[0])
120
+ return;
121
+ // Copy password hash from emailpass identity (same scrypt format).
122
+ const hashRow = await knex.raw(`SELECT pi.provider_metadata FROM provider_identity pi
123
+ WHERE pi.auth_identity_id = ? AND pi.provider = 'emailpass' LIMIT 1`, [authIdentityId]);
124
+ const hash = (hashRow.rows?.[0] ?? hashRow[0]?.[0])?.provider_metadata?.password;
125
+ if (!hash)
126
+ return;
127
+ await authService.createProviderIdentities({
128
+ auth_identity_id: authIdentityId,
129
+ provider: "phonepass",
130
+ entity_id: customer.phone,
131
+ provider_metadata: { password: hash },
132
+ });
133
+ }
134
+ if (otpType === otp_verification_2.OtpPurpose.EMAIL_VERIFICATION && customer.email) {
135
+ // Check if emailpass provider identity already exists.
136
+ const existing = await knex.raw(`SELECT pi.id FROM provider_identity pi
137
+ WHERE pi.auth_identity_id = ? AND pi.provider = 'emailpass' LIMIT 1`, [authIdentityId]);
138
+ if (existing.rows?.[0] ?? existing[0]?.[0])
139
+ return;
140
+ // Copy password hash from phonepass identity.
141
+ const hashRow = await knex.raw(`SELECT pi.provider_metadata FROM provider_identity pi
142
+ WHERE pi.auth_identity_id = ? AND pi.provider = 'phonepass' LIMIT 1`, [authIdentityId]);
143
+ const hash = (hashRow.rows?.[0] ?? hashRow[0]?.[0])?.provider_metadata?.password;
144
+ if (!hash)
145
+ return;
146
+ await authService.createProviderIdentities({
147
+ auth_identity_id: authIdentityId,
148
+ provider: "emailpass",
149
+ entity_id: customer.email.toLowerCase(),
150
+ provider_metadata: { password: hash },
151
+ });
152
+ }
153
+ }
154
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2N1c3RvbWVycy9vdHAvdmVyaWZ5L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUVBLHFEQUlrQztBQUNsQywyRkFBb0c7QUFDcEcsOEVBQWlGO0FBRWpGLHNHQUE0RjtBQUM1Rix5RkFBdUU7QUFDdkUseUZBQXVFO0FBQ3ZFLGtEQUc4QjtBQUV2QixNQUFNLElBQUksR0FBRyxLQUFLLEVBQUUsR0FBa0IsRUFBRSxHQUFtQixFQUFFLEVBQUU7SUFDcEUsTUFBTSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsR0FBRyxHQUFHLENBQUMsSUFHM0IsQ0FBQTtJQUVELElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNYLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLG1CQUFtQixDQUNwQixDQUFBO0lBQ0gsQ0FBQztJQUVELElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNWLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLHNCQUFzQixDQUN2QixDQUFBO0lBQ0gsQ0FBQztJQUVELDhCQUE4QjtJQUM5QixNQUFNLFVBQVUsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FDbEMsMENBQXVCLENBQ0UsQ0FBQTtJQUUzQiw2QkFBNkI7SUFDN0IsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsaUNBQXlCLENBQUMsYUFBYSxDQUFDLENBQUE7SUFDekUsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUE7SUFDckMsTUFBTSxTQUFTLEdBQUksSUFBSSxFQUFFLFNBQWdDLElBQUksYUFBYSxDQUFBO0lBRTFFLGFBQWE7SUFDYixNQUFNLFlB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