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
package/README.md CHANGED
@@ -1,19 +1,20 @@
1
1
  # Medusa Plugin: Customer Registration & OTP Verification
2
2
 
3
- A comprehensive Medusa v2 plugin that provides OTP-based verification for email and phone functionality, with support for Medusa's built-in password reset flow.
3
+ A comprehensive Medusa v2 plugin that provides OTP-based verification for email and phone functionality, with support for flexible registration identifiers, phone-only authentication, contact change flows, and account deletion.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Unified OTP API**: Single endpoints for sending and verifying OTPs
8
- - **Token-based Verification**: Secure JWT token system for OTP verification
9
- - **Multiple Verification Types**: Email verification and phone verification
10
- - **Workflow-based Processing**: Automatic handling of verification flags
11
- - **Password Reset Support**: Notification subscriber for Medusa's built-in password reset flow
12
- - **Flexible Configuration**: Per-purpose channel configuration (email/SMS)
13
- - **Automatic Contact Detection**: Automatically selects email/phone from customer based on channel
14
- - **Throttling & Rate Limiting**: Built-in protection against abuse
15
- - **Database Migrations**: Automatic schema updates for verification columns
16
- - **Account Deletion Request Flow**: Two-step OTP flow to request and confirm account deletion; optional cancel flow with OTP; admin view-only list of requests
7
+ - **Unified OTP API**: Single endpoints for sending and verifying OTPs
8
+ - **Token-based Verification**: Secure JWT token system for OTP verification
9
+ - **Multiple Verification Types**: Email verification and phone verification
10
+ - **Workflow-based Processing**: Automatic handling of verification flags
11
+ - **Password Reset Support**: Notification subscriber for Medusa's built-in password reset flow
12
+ - **Flexible Configuration**: Per-purpose channel configuration (email/SMS)
13
+ - **Throttling & Rate Limiting**: Built-in protection against OTP spam
14
+ - **Database Migrations**: Automatic schema updates for verification columns
15
+ - **Account Deletion Request Flow**: Two-step OTP flow to request and confirm account deletion with optional cancel flow
16
+ - **Phone-only Authentication**: `phonepass` auth provider for registering and logging in with phone + password (no email required)
17
+ - **Authenticated Contact Change**: Dedicated OTP-verified routes for updating phone or email — new value is embedded in the signed JWT, no metadata staging required
17
18
 
18
19
  ## Quick Start
19
20
 
@@ -31,16 +32,21 @@ export default defineConfig({
31
32
  {
32
33
  resolve: "customer-registration",
33
34
  options: {
34
- // Storefront URL for constructing reset password links
35
- storefrontUrl: "http://localhost:8000", // Optional: base URL for reset links
36
-
37
- // Password reset configuration
35
+ storefrontUrl: "http://localhost:8000",
36
+
37
+ registration: {
38
+ identifier: "phone", // "email" | "phone" | "both"
39
+ require_verification: true,
40
+ },
41
+ login: {
42
+ identifier: "phone", // defaults to registration.identifier
43
+ },
44
+
38
45
  password_reset: {
39
- template: "src/templates/emails/password-reset.html", // Required: path to HTML template
40
- subject: "Reset Your Password", // Optional: email subject
46
+ template: "src/templates/emails/password-reset.html",
47
+ subject: "Reset Your Password",
41
48
  },
42
49
 
43
- // Account deletion OTP emails (optional; required if using account deletion flow)
44
50
  account_deletion_request: {
45
51
  template: "src/templates/emails/account-deletion-request.html",
46
52
  subject: "Confirm your account deletion request",
@@ -50,8 +56,7 @@ export default defineConfig({
50
56
  template: "src/templates/emails/account-deletion-cancel.html",
51
57
  subject: "Confirm cancellation of account deletion",
52
58
  },
53
-
54
- // OTP channel configuration
59
+
55
60
  email_verification: {
56
61
  channel: "email",
57
62
  subject: "Verify your email",
@@ -65,33 +70,17 @@ export default defineConfig({
65
70
  })
66
71
  ```
67
72
 
68
- **Required Options:**
69
- - `password_reset.template` - Path to HTML template file for password reset emails
70
-
71
73
  3. **Run migrations**:
72
74
  ```bash
73
75
  npx medusa db:migrate
74
76
  ```
75
77
 
76
- 4. **Use the API**:
77
- ```bash
78
- # Send OTP
79
- POST /store/customers/otp/send
80
- {
81
- "customer_id": "cus_...",
82
- "type": "email_verification"
83
- }
84
-
85
- # Verify OTP
86
- POST /store/customers/otp/verify
87
- {
88
- "token": "...",
89
- "code": "123456"
90
- }
91
- ```
78
+ 4. **Use the API** — see [API Endpoints](#api-endpoints) below.
92
79
 
93
80
  📖 **For complete documentation, see [USAGE.md](./USAGE.md)**
94
81
 
82
+ ---
83
+
95
84
  ## Installation
96
85
 
97
86
  ### Local Development
@@ -108,18 +97,7 @@ cd ../../test-medusa
108
97
  npx medusa plugin:add customer-registration
109
98
  ```
110
99
 
111
- 3. Register the plugin in `medusa-config.ts`:
112
- ```typescript
113
- module.exports = defineConfig({
114
- // ... other config
115
- plugins: [
116
- {
117
- resolve: "customer-registration",
118
- options: {},
119
- },
120
- ],
121
- })
122
- ```
100
+ 3. Register the plugin in `medusa-config.ts` (see Quick Start above).
123
101
 
124
102
  4. Start development mode (in plugin directory):
125
103
  ```bash
@@ -133,269 +111,431 @@ cd ../../test-medusa
133
111
  yarn dev
134
112
  ```
135
113
 
136
- ## Usage
137
-
138
- ### Registration lifecycle hook
114
+ ---
139
115
 
140
- The plugin no longer overrides `POST /store/customers`. Instead, it listens to the `customer.created` event and automatically issues an email OTP (when `email.autoSendOnRegistration` is enabled). Because the default Medusa route still handles persistence and response formatting, there are no behavioral differences for registration requests aside from the verification guard.
116
+ ## Configuration
141
117
 
142
- ### API Endpoints
143
-
144
- The plugin provides unified OTP endpoints:
145
-
146
- | Endpoint | Method | Description |
147
- | --- | --- | --- |
148
- | `/store/customers/otp/send` | POST | Send OTP for email/phone verification |
149
- | `/store/customers/otp/verify` | POST | Verify OTP code and execute appropriate workflow |
118
+ ### `registration.identifier`
150
119
 
151
- **Request Types**:
152
- - `email_verification` - Verify customer email
153
- - `phone_verification` - Verify customer phone
154
- - Note: Password reset uses Medusa's built-in flow (see Password Reset section below)
120
+ Controls which field is required at sign-up:
155
121
 
156
- See [USAGE.md](./USAGE.md) for detailed API documentation and examples.
122
+ | Value | Behaviour |
123
+ |---|---|
124
+ | `"email"` (default) | Email required; standard Medusa registration |
125
+ | `"phone"` | Phone required; no email needed; uses `phonepass` provider |
126
+ | `"both"` | Both email and phone required |
157
127
 
158
- ### Example Flow
128
+ ### `login.identifier`
159
129
 
160
- 1. **Register Customer** - Use standard Medusa customer registration endpoint
161
- 2. **Send OTP** - Request OTP using unified endpoint with type
162
- 3. **Verify OTP** - Verify code using token from send response
163
- 4. **Login** - Customer can login after email verification
130
+ Controls which auth providers are accepted at login. Defaults to `registration.identifier`.
164
131
 
165
- See [USAGE.md](./USAGE.md) for complete examples and integration guide.
132
+ | Value | Accepted login methods |
133
+ |---|---|
134
+ | `"email"` | `POST /auth/customer/emailpass` only |
135
+ | `"phone"` | `POST /auth/customer/phonepass` only |
136
+ | `"both"` | Both emailpass and phonepass accepted |
166
137
 
167
- ### Configuration
168
-
169
- The plugin uses purpose-based configuration:
138
+ ### Full options reference
170
139
 
171
140
  ```typescript
172
141
  {
173
142
  resolve: "customer-registration",
174
143
  options: {
175
- otpLength: 6,
176
- otpCharset: "numeric",
177
- otpExpiryMinutes: 15,
178
- maxAttempts: 5,
179
- email_verification: {
180
- channel: "email",
181
- template: "otp-email-verify",
182
- subject: "Verify your email",
183
- resendThrottleSeconds: 90,
144
+ storefrontUrl?: string, // Base URL for password reset links
145
+
146
+ registration?: {
147
+ identifier?: "email" | "phone" | "both", // default: "email"
148
+ require_verification?: boolean, // default: true
149
+ },
150
+ login?: {
151
+ identifier?: "email" | "phone" | "both", // default: registration.identifier
152
+ },
153
+
154
+ // OTP settings (apply to all types)
155
+ otpLength?: number, // default: 6
156
+ otpCharset?: "numeric" | "alphanumeric", // default: "numeric"
157
+ otpExpiryMinutes?: number, // default: 15
158
+ maxAttempts?: number, // default: 5
159
+
160
+ email_verification?: {
161
+ channel: string, // e.g. "email"
162
+ template?: string,
163
+ subject?: string,
164
+ resendThrottleSeconds?: number,
165
+ autoSendOnRegistration?: boolean,
184
166
  },
185
- phone_verification: {
186
- channel: "sms",
187
- template: "otp-phone-verify",
188
- resendThrottleSeconds: 60,
167
+ phone_verification?: {
168
+ channel: string, // e.g. "sms"
169
+ template?: string,
170
+ resendThrottleSeconds?: number,
171
+ },
172
+
173
+ password_reset?: {
174
+ template: string, // Required when using password reset
175
+ subject?: string,
176
+ },
177
+ account_deletion_request?: {
178
+ template: string,
179
+ subject?: string,
180
+ scheduled_days?: number, // default: 7
181
+ },
182
+ account_deletion_cancel?: {
183
+ template: string,
184
+ subject?: string,
189
185
  },
190
186
  },
191
187
  }
192
188
  ```
193
189
 
194
- See [USAGE.md](./USAGE.md) for complete configuration reference.
190
+ ---
195
191
 
196
- ### Database Migrations
192
+ ## API Endpoints
197
193
 
198
- The plugin includes two migrations:
194
+ ### Registration & Initial Verification
199
195
 
200
- 1. **Migration20250120000000AddCustomerVerificationColumns**
201
- - Adds `email_verified` and `phone_verified` columns to customer table
202
- - Creates indexes for performance
196
+ | Endpoint | Method | Auth | Description |
197
+ |---|---|---|---|
198
+ | `/store/customers` | POST | Bearer (pre-customer) | Create customer account |
199
+ | `/store/customers/otp/send` | POST | — | Send OTP for initial email/phone verification |
200
+ | `/store/customers/otp/verify` | POST | — | Verify OTP code; returns login token on success |
203
201
 
204
- 2. **Migration20250118001000CreateOtpVerificationTable**
205
- - Creates `otp_verification` table for storing OTP records
202
+ #### Send OTP
206
203
 
207
- 3. **Migration20250221000000CreateAccountDeletionRequestTable**
208
- - Creates `account_deletion_request` table (customer_id, reason, deletion_scheduled_at, status, cancelled_at)
204
+ ```bash
205
+ POST /store/customers/otp/send
206
+ {
207
+ "customer_id": "cus_...",
208
+ "type": "phone_verification" # or "email_verification"
209
+ }
210
+ # Response: { "token": "...", "expires_at": "..." }
211
+ ```
209
212
 
210
- 4. **Migration20250221000000AddAccountDeletionOtpPurposes**
211
- - Extends `otp_verification.purpose` to include `account_deletion_request` and `account_deletion_cancel`
213
+ #### Verify OTP
212
214
 
213
- Run migrations after installation:
214
215
  ```bash
215
- npx medusa db:migrate
216
+ POST /store/customers/otp/verify
217
+ {
218
+ "token": "<token from send>",
219
+ "code": "482917"
220
+ }
221
+ # Response: { "verified": true, "customer": {...}, "token": "<login jwt>", "needs_login": false }
216
222
  ```
217
223
 
218
- ## Requirements
224
+ ---
219
225
 
220
- - Medusa v2.11.2 or higher
221
- - Node.js >= 20
222
- - Notification module configured with at least one provider (email/SMS)
223
- - Database migrations applied (`npx medusa db:migrate`)
226
+ ### Login
224
227
 
225
- ## Documentation
228
+ | Endpoint | Method | Description |
229
+ |---|---|---|
230
+ | `/auth/customer/emailpass` | POST | Login with email + password |
231
+ | `/auth/customer/phonepass` | POST | Login with phone + password |
232
+ | `/auth/customer/emailpass/register` | POST | Create emailpass auth identity |
233
+ | `/auth/customer/phonepass/register` | POST | Create phonepass auth identity |
226
234
 
227
- - **[USAGE.md](./USAGE.md)** - Complete usage guide with examples
228
- - **[README.md](./README.md)** - This file (overview and quick start)
235
+ ---
229
236
 
230
- ## Modules
237
+ ### Contact Change (Phone or Email Update)
231
238
 
232
- The plugin includes three modules:
239
+ > These are the dedicated authenticated routes for changing a customer's phone or email.
240
+ > `PATCH /store/customers/me` **rejects** `phone` and `email` fields — all contact changes must go through these endpoints.
233
241
 
234
- 1. **`otp-verification`** - OTP generation, verification, and management
235
- 2. **`customer-registration`** - Customer registration logic and overrides
236
- 3. **`account-deletion-request`** - Account deletion request lifecycle (pending, confirmed, cancelled)
242
+ | Endpoint | Method | Auth | Description |
243
+ |---|---|---|---|
244
+ | `/store/customers/me/contact` | POST | Customer JWT | Request contact change — validates new value, sends OTP, returns token |
245
+ | `/store/customers/me/contact/verify` | POST | Customer JWT | Verify OTP and apply the change |
237
246
 
238
- ## Workflows
247
+ #### How it works
239
248
 
240
- The plugin uses workflows for different verification types:
249
+ The new phone or email is embedded directly in the signed OTP JWT. No metadata is written to the customer record during the pending state. On successful verification:
241
250
 
242
- - `verify-email` - Sets email_verified flag
243
- - `verify-phone` - Sets phone_verified flag
251
+ 1. `customer.phone` / `customer.email` is updated
252
+ 2. `phone_verified` / `email_verified` is set to `true`
253
+ 3. `provider_identity.entity_id` is updated for `phonepass` / `emailpass` **only when the changed field is the active `login.identifier`** — so login continues to work with the new value
244
254
 
245
- ## Password Reset
255
+ #### Step 1 — Request the change
246
256
 
247
- This plugin provides password reset functionality using Medusa's AUTH module:
257
+ ```bash
258
+ curl -X POST http://localhost:9000/store/customers/me/contact \
259
+ -H "Content-Type: application/json" \
260
+ -H "Authorization: Bearer $CUSTOMER_JWT" \
261
+ -d '{ "phone": "+15559998888" }'
248
262
 
249
- 1. **Request Reset**: POST to `/auth/customer/emailpass/reset-password` with email (or `identifier`)
250
- - Custom route that generates JWT reset token using Medusa's `generateJwtTokenForAuthIdentity`
251
- - Sends password reset email directly via notification service
252
- - Returns 201 status (matches Medusa's built-in route behavior)
253
- 2. **Email Sent**: Password reset email is sent with reset token and URL
254
- 3. **Complete Reset**: POST to `/auth/customer/emailpass/update` with token and new password
255
- - Uses AUTH module's `authenticate()` method with `update-password` action
256
- - AUTH module validates token and updates password
263
+ # Response
264
+ { "token": "<otp_token>", "expires_at": "2026-03-12T10:15:00.000Z" }
265
+ ```
257
266
 
258
- **Key Features:**
259
- - Uses Medusa's AUTH module for token generation and password updates
260
- - Custom routes that match Medusa's built-in route behavior
261
- - Email sending handled directly in the route (subscriber available for `auth.password_reset` event)
262
- - Consistent with Medusa's authentication patterns
267
+ Only one field at a time is accepted. Sending both `phone` and `email` returns a `400`.
263
268
 
264
- **Note:** These custom routes override Medusa's built-in routes at the same paths. The subscriber listens to `auth.password_reset` event (emitted by Medusa's built-in route) but since we're using custom routes, emails are sent directly from the route.
269
+ #### Step 2 Verify the OTP
265
270
 
266
- **Configuration Required:**
267
- - Set `password_reset.template` in plugin options (path to HTML template file)
268
- - Configure email template with ID `password-reset` in your notification provider (optional, subscriber includes HTML)
269
- - Ensure AUTH module is configured with `emailpass` provider
271
+ ```bash
272
+ curl -X POST http://localhost:9000/store/customers/me/contact/verify \
273
+ -H "Content-Type: application/json" \
274
+ -H "Authorization: Bearer $CUSTOMER_JWT" \
275
+ -d '{
276
+ "token": "<otp_token from step 1>",
277
+ "code": "482917"
278
+ }'
270
279
 
271
- **Helper Functions:**
280
+ # Response
281
+ {
282
+ "customer": { "id": "cus_...", "phone": "+15559998888", ... },
283
+ "token": "<fresh_login_jwt>"
284
+ }
285
+ ```
272
286
 
273
- The plugin provides helper functions for password reset operations:
287
+ The response includes a **fresh login JWT** so the caller is immediately re-authenticated after the change.
274
288
 
275
- ```typescript
276
- import {
277
- requestPasswordReset,
278
- completePasswordReset,
279
- } from "customer-registration/helpers"
289
+ #### Security notes
280
290
 
281
- // Request password reset
282
- await requestPasswordReset(
283
- { email: "customer@example.com" },
284
- { baseUrl: "https://store.example.com", publishableApiKey: "pk_..." }
285
- )
291
+ - The OTP token is signed with the server's `jwtSecret` — the new value cannot be tampered with
292
+ - The verify route checks that `token.customer_id === auth_context.actor_id`, preventing one customer from applying another's OTP
293
+ - `provider_identity.entity_id` sync has a compensation function: if a later workflow step fails the `entity_id` is rolled back to the original value
294
+ - If the current phone/email is `null` (first-time contact assignment), the flow works identically — the field is set from null to the new value and marked verified
286
295
 
287
- // Complete password reset
288
- await completePasswordReset(
289
- {
290
- email: "customer@example.com",
291
- password: "NewPassword123!",
292
- token: "reset_token_from_email",
293
- },
294
- { baseUrl: "https://store.example.com", publishableApiKey: "pk_..." }
295
- )
296
+ #### What `PATCH /store/customers/me` does now
297
+
298
+ Non-contact fields (e.g. `first_name`, `last_name`) continue to work as before. Passing `phone` or `email` in the body returns:
299
+
300
+ ```json
301
+ {
302
+ "type": "not_allowed",
303
+ "message": "Phone changes require OTP verification. Use POST /store/customers/me/contact to request a change."
304
+ }
296
305
  ```
297
306
 
298
- See [USAGE.md](./USAGE.md) for complete helper function documentation.
307
+ ---
308
+
309
+ ### Profile Update
299
310
 
300
- ## Account Deletion Request Flow
311
+ | Endpoint | Method | Auth | Description |
312
+ |---|---|---|---|
313
+ | `/store/customers/me` | PATCH | Customer JWT | Update non-contact profile fields (first_name, last_name, etc.) |
301
314
 
302
- Customers can request account deletion via a two-step OTP flow, and optionally cancel an existing request. **No database record is created when the user requests deletion;** the record is created only when they confirm with token and code.
315
+ ---
303
316
 
304
- **Store API (customer auth required for request only):**
317
+ ### Password Reset
318
+
319
+ | Endpoint | Method | Description |
320
+ |---|---|---|
321
+ | `/auth/customer/emailpass/reset-password` | POST | Request password reset email |
322
+ | `/auth/customer/emailpass/update` | POST | Complete password reset with token |
305
323
 
306
- 1. **Request deletion** – `POST /store/customers/account-deletion/request`
307
- Body: `{ "reason"?: string }`
308
- Does not create a DB row; only sends OTP email and returns `{ token, expires_at }`. Rejects if the customer already has an active (confirmed) deletion request (they must cancel it first).
324
+ ```bash
325
+ # Request reset
326
+ POST /auth/customer/emailpass/reset-password
327
+ { "identifier": "customer@example.com" }
309
328
 
310
- 2. **Confirm deletion** – `POST /store/customers/account-deletion/confirm`
311
- Body: `{ "token": string, "code": string }`
312
- Verifies OTP and **creates** the account_deletion_request row with status **confirmed** and `deletion_scheduled_at` from config (`account_deletion_request.scheduled_days`, default 7 days from confirmation).
329
+ # Complete reset
330
+ POST /auth/customer/emailpass/update
331
+ { "token": "<reset_token>", "password": "NewPassword123!" }
332
+ ```
313
333
 
314
- 3. **Request cancel** – `POST /store/customers/account-deletion/cancel-request`
315
- No auth required. Body: `{ "email": string }` (required). Looks up customer by email, verifies they have an active deletion request, sends OTP email, returns `{ token, expires_at }`.
334
+ ---
316
335
 
317
- 4. **Confirm cancel** – `POST /store/customers/account-deletion/cancel-confirm`
318
- Body: `{ "token": string, "code": string }`
319
- Verifies OTP and cancels the request (user can request again).
336
+ ### Account Deletion
320
337
 
321
- **cURL examples (store base URL and customer token are required where noted):**
338
+ | Endpoint | Method | Auth | Description |
339
+ |---|---|---|---|
340
+ | `/store/customers/account-deletion/request` | POST | Customer JWT | Request deletion — sends OTP, returns token |
341
+ | `/store/customers/account-deletion/confirm` | POST | — | Confirm deletion with OTP code |
342
+ | `/store/customers/account-deletion/cancel-request` | POST | — | Request cancellation (lookup by email/phone) |
343
+ | `/store/customers/account-deletion/cancel-confirm` | POST | — | Confirm cancellation with OTP code |
344
+ | `/admin/account-deletion-requests` | GET | Admin JWT | List deletion requests |
322
345
 
323
346
  ```bash
324
- # 1. Request deletion (customer auth required)
325
- curl -X POST "http://localhost:9000/store/customers/account-deletion/request" \
326
- -H "Content-Type: application/json" \
327
- -H "Authorization: Bearer YOUR_CUSTOMER_JWT" \
347
+ # 1. Request deletion
348
+ curl -X POST http://localhost:9000/store/customers/account-deletion/request \
349
+ -H "Authorization: Bearer $CUSTOMER_JWT" \
328
350
  -d '{"reason": "No longer need account"}'
351
+ # Response: { "token": "...", "expires_at": "..." }
329
352
 
330
- # 2. Confirm deletion (use token and code from step 1 email)
331
- curl -X POST "http://localhost:9000/store/customers/account-deletion/confirm" \
332
- -H "Content-Type: application/json" \
333
- -d '{"token": "TOKEN_FROM_EMAIL", "code": "123456"}'
353
+ # 2. Confirm deletion
354
+ curl -X POST http://localhost:9000/store/customers/account-deletion/confirm \
355
+ -d '{"token": "...", "code": "123456"}'
334
356
 
335
- # 3. Request cancel (no auth; body: email of customer with active deletion request)
336
- curl -X POST "http://localhost:9000/store/customers/account-deletion/cancel-request" \
337
- -H "Content-Type: application/json" \
357
+ # 3. Request cancel (no auth; email lookup)
358
+ curl -X POST http://localhost:9000/store/customers/account-deletion/cancel-request \
338
359
  -d '{"email": "customer@example.com"}'
339
360
 
340
- # 4. Confirm cancel (use token and code from step 3 email)
341
- curl -X POST "http://localhost:9000/store/customers/account-deletion/cancel-confirm" \
342
- -H "Content-Type: application/json" \
343
- -d '{"token": "TOKEN_FROM_EMAIL", "code": "123456"}'
361
+ # 4. Confirm cancel
362
+ curl -X POST http://localhost:9000/store/customers/account-deletion/cancel-confirm \
363
+ -d '{"token": "...", "code": "123456"}'
344
364
  ```
345
365
 
346
- **Admin API (view only):**
366
+ ---
347
367
 
348
- - **List requests** `GET /admin/account-deletion-requests?limit=20&offset=0&status=pending`
349
- Returns `{ requests, count, offset, limit }`.
368
+ ## Phone-only Registration & Login (`phonepass`)
350
369
 
351
- **Plugin options (required for account deletion emails):**
370
+ When `registration.identifier = "phone"`, customers register and log in with phone + password only. No email address is required.
371
+
372
+ ### 1. Register the provider in `medusa-config.ts`
352
373
 
353
374
  ```typescript
354
- {
355
- resolve: "customer-registration",
356
- options: {
357
- account_deletion_request: {
358
- template: "src/templates/emails/account-deletion-request.html",
359
- subject: "Confirm your account deletion request",
360
- scheduled_days: 7, // Optional: days from confirmation until deletion is scheduled (default: 7)
375
+ import { Modules } from "@medusajs/framework/utils"
376
+
377
+ export default defineConfig({
378
+ modules: [
379
+ {
380
+ resolve: "@medusajs/medusa/auth",
381
+ options: {
382
+ providers: [
383
+ {
384
+ resolve: "customer-registration/providers/phonepass",
385
+ id: "phonepass",
386
+ },
387
+ ],
388
+ },
361
389
  },
362
- account_deletion_cancel: {
363
- template: "src/templates/emails/account-deletion-cancel.html",
364
- subject: "Confirm cancellation of account deletion",
390
+ ],
391
+ plugins: [
392
+ {
393
+ resolve: "customer-registration",
394
+ options: {
395
+ registration: {
396
+ identifier: "phone",
397
+ require_verification: true,
398
+ },
399
+ login: {
400
+ identifier: "phone",
401
+ },
402
+ phone_verification: {
403
+ channel: "sms",
404
+ },
405
+ },
365
406
  },
366
- },
367
- }
407
+ ],
408
+ })
368
409
  ```
369
410
 
370
- All account-deletion email templates must be set in plugin options; there are no built-in fallbacks. `deletion_scheduled_at` is not sent by the client; it is computed on confirmation as now + `scheduled_days`.
411
+ ### 2. Registration flow
371
412
 
372
- **Example email templates:** The plugin includes two example HTML templates you can copy into your project or reference (paths relative to your project root):
413
+ ```bash
414
+ # Step 1 — create phonepass auth identity
415
+ POST /auth/customer/phonepass/register
416
+ { "phone": "+15551234567", "password": "SecretPass1!" }
417
+ # Response: { "token": "<pre-customer jwt>" }
373
418
 
374
- | Template | Path | Purpose |
375
- |----------|------|---------|
376
- | Request deletion OTP | `src/templates/emails/account-deletion-request.html` | Sent when the customer requests account deletion |
377
- | Cancel deletion OTP | `src/templates/emails/account-deletion-cancel.html` | Sent when the customer requests to cancel a deletion |
419
+ # Step 2 create customer record
420
+ POST /store/customers
421
+ Authorization: Bearer <token>
422
+ { "phone": "+15551234567", "first_name": "Jane" }
378
423
 
379
- Both templates support the same placeholders: `{{otp}}`, `{{customer_name}}`, and `{{customer_email}}`. Copy these files from the plugin (e.g. from `node_modules/customer-registration/src/templates/emails/`) into your app’s `src/templates/emails/` and use the paths above in plugin options, or point to your own custom templates.
424
+ # Step 3 send phone OTP
425
+ POST /store/customers/otp/send
426
+ { "customer_id": "cus_...", "type": "phone_verification" }
427
+ # Response: { "token": "<otp_token>", "expires_at": "..." }
428
+
429
+ # Step 4 — verify phone OTP (returns login token)
430
+ POST /store/customers/otp/verify
431
+ { "token": "<otp_token>", "code": "482917" }
432
+ # Response: { "verified": true, "phone_verified": true, "token": "<login jwt>" }
433
+ ```
380
434
 
381
- **Scheduled job:** The plugin runs a job `process-account-deletions` (hourly by default) that finds requests with status **confirmed** and `deletion_scheduled_at` in the past, deletes the customer and their auth identity (where `app_metadata.customer_id` matches), then sets the request status to **completed**.
435
+ ### 3. Login flow
382
436
 
383
- ## Development
437
+ ```bash
438
+ POST /auth/customer/phonepass
439
+ { "phone": "+15551234567", "password": "SecretPass1!" }
440
+ # Response: { "token": "<jwt>" }
441
+ ```
384
442
 
385
- ### Build
443
+ ### 4. Update phone (after registration)
386
444
 
387
445
  ```bash
388
- npm run build
446
+ # Request change
447
+ POST /store/customers/me/contact
448
+ Authorization: Bearer <login jwt>
449
+ { "phone": "+15559998888" }
450
+ # Response: { "token": "<otp_token>", "expires_at": "..." }
451
+
452
+ # Verify and apply
453
+ POST /store/customers/me/contact/verify
454
+ Authorization: Bearer <login jwt>
455
+ { "token": "<otp_token>", "code": "739201" }
456
+ # Response: { "customer": { "phone": "+15559998888", ... }, "token": "<fresh jwt>" }
457
+ ```
458
+
459
+ ---
460
+
461
+ ## Modules
462
+
463
+ The plugin includes three modules:
464
+
465
+ 1. **`otp-verification`** — OTP generation, verification, and management
466
+ 2. **`customer-registration`** — Customer registration logic and route overrides
467
+ 3. **`account-deletion-request`** — Account deletion request lifecycle (pending → confirmed → completed/cancelled)
468
+
469
+ ## Workflows
470
+
471
+ | Workflow | Description |
472
+ |---|---|
473
+ | `verify-email` | Sets `email_verified = true` on the customer record |
474
+ | `verify-phone` | Sets `phone_verified = true` on the customer record |
475
+ | `update-contact` | Updates `customer.phone` / `customer.email`, sets verified flag, syncs `provider_identity.entity_id` when applicable |
476
+ | `send-otp` | Resolves channel config, generates OTP, sends notification |
477
+ | `send-contact-change-otp` | Generates OTP with `new_value` embedded in JWT, sends to the new contact address |
478
+ | `change-password` | Updates customer password via the auth module |
479
+
480
+ ## Database Migrations
481
+
482
+ | Migration | Description |
483
+ |---|---|
484
+ | `Migration20250120000000AddCustomerVerificationColumns` | Adds `email_verified` and `phone_verified` columns to the customer table |
485
+ | `Migration20250118001000CreateOtpVerificationTable` | Creates `otp_verification` table |
486
+ | `Migration20250221000000CreateAccountDeletionRequestTable` | Creates `account_deletion_request` table |
487
+ | `Migration20250221000000AddAccountDeletionOtpPurposes` | Extends `otp_verification.purpose` enum for account deletion flows |
488
+
489
+ ```bash
490
+ npx medusa db:migrate
389
491
  ```
390
492
 
391
- ### Watch for Changes
493
+ ## Password Reset (Helper Functions)
494
+
495
+ ```typescript
496
+ import {
497
+ requestPasswordReset,
498
+ completePasswordReset,
499
+ } from "customer-registration/helpers"
500
+
501
+ await requestPasswordReset(
502
+ { email: "customer@example.com" },
503
+ { baseUrl: "https://store.example.com", publishableApiKey: "pk_..." }
504
+ )
505
+
506
+ await completePasswordReset(
507
+ {
508
+ email: "customer@example.com",
509
+ password: "NewPassword123!",
510
+ token: "reset_token_from_email",
511
+ },
512
+ { baseUrl: "https://store.example.com", publishableApiKey: "pk_..." }
513
+ )
514
+ ```
515
+
516
+ ## Requirements
517
+
518
+ - Medusa v2.11.2 or higher
519
+ - Node.js >= 20
520
+ - Notification module configured with at least one provider (email/SMS)
521
+ - Database migrations applied (`npx medusa db:migrate`)
522
+ - `phonepass` provider registered in auth module (required when `registration.identifier` is `"phone"` or `"both"`)
523
+
524
+ ## Documentation
525
+
526
+ - **[USAGE.md](./USAGE.md)** — Complete usage guide with examples
527
+ - **[README.md](./README.md)** — This file (overview and quick start)
528
+
529
+ ## Development
392
530
 
393
531
  ```bash
532
+ # Build
533
+ npm run build
534
+
535
+ # Watch mode
394
536
  npx medusa plugin:develop
395
537
  ```
396
538
 
397
- This command watches for changes and automatically rebuilds and publishes the plugin to the local registry.
398
-
399
539
  ## License
400
540
 
401
541
  MIT