customer-registration 0.0.111 → 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.
- package/.medusa/server/src/admin/index.js +3249 -2
- package/.medusa/server/src/admin/index.mjs +3232 -2
- package/.medusa/server/src/api/admin/account-deletion-requests/route.js +30 -0
- package/.medusa/server/src/api/admin/account-deletion-requests/validators.js +12 -0
- package/.medusa/server/src/api/auth/customer/emailpass/reset-password/route.js +1 -26
- package/.medusa/server/src/api/auth/customer/emailpass/route.js +24 -97
- package/.medusa/server/src/api/auth/customer/phonepass/register/route.js +50 -0
- package/.medusa/server/src/api/auth/customer/phonepass/route.js +105 -0
- package/.medusa/server/src/api/middlewares/guard-account-deletion.js +29 -0
- package/.medusa/server/src/api/middlewares/ip-rate-limit.js +48 -0
- package/.medusa/server/src/api/middlewares/validate-customer-registration.js +60 -0
- package/.medusa/server/src/api/middlewares.js +30 -0
- package/.medusa/server/src/api/store/customers/account-deletion/cancel-confirm/route.js +36 -0
- package/.medusa/server/src/api/store/customers/account-deletion/cancel-request/route.js +55 -0
- package/.medusa/server/src/api/store/customers/account-deletion/confirm/route.js +43 -0
- package/.medusa/server/src/api/store/customers/account-deletion/request/route.js +40 -0
- package/.medusa/server/src/api/store/customers/account-deletion/validators.js +27 -0
- package/.medusa/server/src/api/store/customers/me/contact/route.js +95 -0
- package/.medusa/server/src/api/store/customers/me/contact/verify/route.js +83 -0
- package/.medusa/server/src/api/store/customers/me/route.js +53 -0
- package/.medusa/server/src/api/store/customers/otp/send/route.js +1 -6
- package/.medusa/server/src/api/store/customers/otp/verify/route.js +95 -3
- package/.medusa/server/src/api/store/customers/route.js +89 -0
- package/.medusa/server/src/config.js +44 -13
- package/.medusa/server/src/jobs/process-account-deletions.js +69 -0
- package/.medusa/server/src/modules/account-deletion-request/index.js +17 -0
- package/.medusa/server/src/modules/account-deletion-request/migrations/Migration20250221000000CreateAccountDeletionRequestTable.js +42 -0
- package/.medusa/server/src/modules/account-deletion-request/migrations/Migration20250221100000AddCompletedStatusToAccountDeletionRequest.js +30 -0
- package/.medusa/server/src/modules/account-deletion-request/models/account-deletion-request.js +26 -0
- package/.medusa/server/src/modules/account-deletion-request/service.js +90 -0
- package/.medusa/server/src/modules/otp-verification/migrations/Migration20250221000000AddAccountDeletionOtpPurposes.js +35 -0
- package/.medusa/server/src/modules/otp-verification/models/otp-verification.js +9 -2
- package/.medusa/server/src/modules/otp-verification/service.js +79 -1
- package/.medusa/server/src/providers/phonepass/index.js +9 -0
- package/.medusa/server/src/providers/phonepass/service.js +133 -0
- package/.medusa/server/src/subscribers/password-reset.js +1 -42
- package/.medusa/server/src/workflows/change-password.js +40 -64
- package/.medusa/server/src/workflows/send-contact-change-otp-workflow.js +41 -0
- package/.medusa/server/src/workflows/steps/determine-contact-method-step.js +8 -2
- package/.medusa/server/src/workflows/steps/generate-contact-change-otp-step.js +24 -0
- package/.medusa/server/src/workflows/steps/index.js +6 -2
- package/.medusa/server/src/workflows/steps/resolve-channel-config-step.js +10 -1
- package/.medusa/server/src/workflows/steps/send-notification-step.js +1 -11
- package/.medusa/server/src/workflows/steps/sync-phonepass-entity-id-step.js +63 -0
- package/.medusa/server/src/workflows/steps/update-password-step.js +21 -29
- package/.medusa/server/src/workflows/update-contact-workflow.js +100 -0
- package/.medusa/server/src/workflows/verify-phone.js +11 -4
- package/README.md +389 -147
- package/package.json +12 -3
- package/.medusa/server/src/subscribers/customer-updated.js +0 -100
package/README.md
CHANGED
|
@@ -1,18 +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
|
|
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
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
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
|
|
16
18
|
|
|
17
19
|
## Quick Start
|
|
18
20
|
|
|
@@ -30,16 +32,31 @@ export default defineConfig({
|
|
|
30
32
|
{
|
|
31
33
|
resolve: "customer-registration",
|
|
32
34
|
options: {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
+
|
|
37
45
|
password_reset: {
|
|
38
|
-
template: "src/templates/emails/password-reset.html",
|
|
39
|
-
subject: "Reset Your Password",
|
|
46
|
+
template: "src/templates/emails/password-reset.html",
|
|
47
|
+
subject: "Reset Your Password",
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
account_deletion_request: {
|
|
51
|
+
template: "src/templates/emails/account-deletion-request.html",
|
|
52
|
+
subject: "Confirm your account deletion request",
|
|
53
|
+
scheduled_days: 7,
|
|
40
54
|
},
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
account_deletion_cancel: {
|
|
56
|
+
template: "src/templates/emails/account-deletion-cancel.html",
|
|
57
|
+
subject: "Confirm cancellation of account deletion",
|
|
58
|
+
},
|
|
59
|
+
|
|
43
60
|
email_verification: {
|
|
44
61
|
channel: "email",
|
|
45
62
|
subject: "Verify your email",
|
|
@@ -53,33 +70,17 @@ export default defineConfig({
|
|
|
53
70
|
})
|
|
54
71
|
```
|
|
55
72
|
|
|
56
|
-
**Required Options:**
|
|
57
|
-
- `password_reset.template` - Path to HTML template file for password reset emails
|
|
58
|
-
|
|
59
73
|
3. **Run migrations**:
|
|
60
74
|
```bash
|
|
61
75
|
npx medusa db:migrate
|
|
62
76
|
```
|
|
63
77
|
|
|
64
|
-
4. **Use the API
|
|
65
|
-
```bash
|
|
66
|
-
# Send OTP
|
|
67
|
-
POST /store/customers/otp/send
|
|
68
|
-
{
|
|
69
|
-
"customer_id": "cus_...",
|
|
70
|
-
"type": "email_verification"
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
# Verify OTP
|
|
74
|
-
POST /store/customers/otp/verify
|
|
75
|
-
{
|
|
76
|
-
"token": "...",
|
|
77
|
-
"code": "123456"
|
|
78
|
-
}
|
|
79
|
-
```
|
|
78
|
+
4. **Use the API** — see [API Endpoints](#api-endpoints) below.
|
|
80
79
|
|
|
81
80
|
📖 **For complete documentation, see [USAGE.md](./USAGE.md)**
|
|
82
81
|
|
|
82
|
+
---
|
|
83
|
+
|
|
83
84
|
## Installation
|
|
84
85
|
|
|
85
86
|
### Local Development
|
|
@@ -96,18 +97,7 @@ cd ../../test-medusa
|
|
|
96
97
|
npx medusa plugin:add customer-registration
|
|
97
98
|
```
|
|
98
99
|
|
|
99
|
-
3. Register the plugin in `medusa-config.ts
|
|
100
|
-
```typescript
|
|
101
|
-
module.exports = defineConfig({
|
|
102
|
-
// ... other config
|
|
103
|
-
plugins: [
|
|
104
|
-
{
|
|
105
|
-
resolve: "customer-registration",
|
|
106
|
-
options: {},
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
})
|
|
110
|
-
```
|
|
100
|
+
3. Register the plugin in `medusa-config.ts` (see Quick Start above).
|
|
111
101
|
|
|
112
102
|
4. Start development mode (in plugin directory):
|
|
113
103
|
```bash
|
|
@@ -121,137 +111,386 @@ cd ../../test-medusa
|
|
|
121
111
|
yarn dev
|
|
122
112
|
```
|
|
123
113
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
### Registration lifecycle hook
|
|
127
|
-
|
|
128
|
-
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.
|
|
114
|
+
---
|
|
129
115
|
|
|
130
|
-
|
|
116
|
+
## Configuration
|
|
131
117
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
| Endpoint | Method | Description |
|
|
135
|
-
| --- | --- | --- |
|
|
136
|
-
| `/store/customers/otp/send` | POST | Send OTP for email/phone verification |
|
|
137
|
-
| `/store/customers/otp/verify` | POST | Verify OTP code and execute appropriate workflow |
|
|
118
|
+
### `registration.identifier`
|
|
138
119
|
|
|
139
|
-
|
|
140
|
-
- `email_verification` - Verify customer email
|
|
141
|
-
- `phone_verification` - Verify customer phone
|
|
142
|
-
- Note: Password reset uses Medusa's built-in flow (see Password Reset section below)
|
|
120
|
+
Controls which field is required at sign-up:
|
|
143
121
|
|
|
144
|
-
|
|
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 |
|
|
145
127
|
|
|
146
|
-
###
|
|
128
|
+
### `login.identifier`
|
|
147
129
|
|
|
148
|
-
|
|
149
|
-
2. **Send OTP** - Request OTP using unified endpoint with type
|
|
150
|
-
3. **Verify OTP** - Verify code using token from send response
|
|
151
|
-
4. **Login** - Customer can login after email verification
|
|
130
|
+
Controls which auth providers are accepted at login. Defaults to `registration.identifier`.
|
|
152
131
|
|
|
153
|
-
|
|
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 |
|
|
154
137
|
|
|
155
|
-
###
|
|
156
|
-
|
|
157
|
-
The plugin uses purpose-based configuration:
|
|
138
|
+
### Full options reference
|
|
158
139
|
|
|
159
140
|
```typescript
|
|
160
141
|
{
|
|
161
142
|
resolve: "customer-registration",
|
|
162
143
|
options: {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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,
|
|
172
166
|
},
|
|
173
|
-
phone_verification
|
|
174
|
-
channel: "sms"
|
|
175
|
-
template
|
|
176
|
-
resendThrottleSeconds
|
|
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,
|
|
177
185
|
},
|
|
178
186
|
},
|
|
179
187
|
}
|
|
180
188
|
```
|
|
181
189
|
|
|
182
|
-
|
|
190
|
+
---
|
|
183
191
|
|
|
184
|
-
|
|
192
|
+
## API Endpoints
|
|
185
193
|
|
|
186
|
-
|
|
194
|
+
### Registration & Initial Verification
|
|
187
195
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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 |
|
|
191
201
|
|
|
192
|
-
|
|
193
|
-
- Creates `otp_verification` table for storing OTP records
|
|
202
|
+
#### Send OTP
|
|
194
203
|
|
|
195
|
-
Run migrations after installation:
|
|
196
204
|
```bash
|
|
197
|
-
|
|
205
|
+
POST /store/customers/otp/send
|
|
206
|
+
{
|
|
207
|
+
"customer_id": "cus_...",
|
|
208
|
+
"type": "phone_verification" # or "email_verification"
|
|
209
|
+
}
|
|
210
|
+
# Response: { "token": "...", "expires_at": "..." }
|
|
198
211
|
```
|
|
199
212
|
|
|
200
|
-
|
|
213
|
+
#### Verify OTP
|
|
201
214
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
215
|
+
```bash
|
|
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 }
|
|
222
|
+
```
|
|
206
223
|
|
|
207
|
-
|
|
224
|
+
---
|
|
208
225
|
|
|
209
|
-
|
|
210
|
-
- **[README.md](./README.md)** - This file (overview and quick start)
|
|
226
|
+
### Login
|
|
211
227
|
|
|
212
|
-
|
|
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 |
|
|
213
234
|
|
|
214
|
-
|
|
235
|
+
---
|
|
215
236
|
|
|
216
|
-
|
|
217
|
-
2. **`customer-registration`** - Customer registration logic and overrides
|
|
237
|
+
### Contact Change (Phone or Email Update)
|
|
218
238
|
|
|
219
|
-
|
|
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.
|
|
220
241
|
|
|
221
|
-
|
|
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 |
|
|
222
246
|
|
|
223
|
-
|
|
224
|
-
- `verify-phone` - Sets phone_verified flag
|
|
247
|
+
#### How it works
|
|
225
248
|
|
|
226
|
-
|
|
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:
|
|
227
250
|
|
|
228
|
-
|
|
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
|
|
229
254
|
|
|
230
|
-
|
|
231
|
-
- Custom route that generates JWT reset token using Medusa's `generateJwtTokenForAuthIdentity`
|
|
232
|
-
- Sends password reset email directly via notification service
|
|
233
|
-
- Returns 201 status (matches Medusa's built-in route behavior)
|
|
234
|
-
2. **Email Sent**: Password reset email is sent with reset token and URL
|
|
235
|
-
3. **Complete Reset**: POST to `/auth/customer/emailpass/update` with token and new password
|
|
236
|
-
- Uses AUTH module's `authenticate()` method with `update-password` action
|
|
237
|
-
- AUTH module validates token and updates password
|
|
255
|
+
#### Step 1 — Request the change
|
|
238
256
|
|
|
239
|
-
|
|
240
|
-
-
|
|
241
|
-
-
|
|
242
|
-
-
|
|
243
|
-
-
|
|
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" }'
|
|
244
262
|
|
|
245
|
-
|
|
263
|
+
# Response
|
|
264
|
+
{ "token": "<otp_token>", "expires_at": "2026-03-12T10:15:00.000Z" }
|
|
265
|
+
```
|
|
246
266
|
|
|
247
|
-
|
|
248
|
-
- Set `password_reset.template` in plugin options (path to HTML template file)
|
|
249
|
-
- Configure email template with ID `password-reset` in your notification provider (optional, subscriber includes HTML)
|
|
250
|
-
- Ensure AUTH module is configured with `emailpass` provider
|
|
267
|
+
Only one field at a time is accepted. Sending both `phone` and `email` returns a `400`.
|
|
251
268
|
|
|
252
|
-
|
|
269
|
+
#### Step 2 — Verify the OTP
|
|
253
270
|
|
|
254
|
-
|
|
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
|
+
}'
|
|
279
|
+
|
|
280
|
+
# Response
|
|
281
|
+
{
|
|
282
|
+
"customer": { "id": "cus_...", "phone": "+15559998888", ... },
|
|
283
|
+
"token": "<fresh_login_jwt>"
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
The response includes a **fresh login JWT** so the caller is immediately re-authenticated after the change.
|
|
288
|
+
|
|
289
|
+
#### Security notes
|
|
290
|
+
|
|
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
|
|
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
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
### Profile Update
|
|
310
|
+
|
|
311
|
+
| Endpoint | Method | Auth | Description |
|
|
312
|
+
|---|---|---|---|
|
|
313
|
+
| `/store/customers/me` | PATCH | Customer JWT | Update non-contact profile fields (first_name, last_name, etc.) |
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
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 |
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
# Request reset
|
|
326
|
+
POST /auth/customer/emailpass/reset-password
|
|
327
|
+
{ "identifier": "customer@example.com" }
|
|
328
|
+
|
|
329
|
+
# Complete reset
|
|
330
|
+
POST /auth/customer/emailpass/update
|
|
331
|
+
{ "token": "<reset_token>", "password": "NewPassword123!" }
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
### Account Deletion
|
|
337
|
+
|
|
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 |
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
# 1. Request deletion
|
|
348
|
+
curl -X POST http://localhost:9000/store/customers/account-deletion/request \
|
|
349
|
+
-H "Authorization: Bearer $CUSTOMER_JWT" \
|
|
350
|
+
-d '{"reason": "No longer need account"}'
|
|
351
|
+
# Response: { "token": "...", "expires_at": "..." }
|
|
352
|
+
|
|
353
|
+
# 2. Confirm deletion
|
|
354
|
+
curl -X POST http://localhost:9000/store/customers/account-deletion/confirm \
|
|
355
|
+
-d '{"token": "...", "code": "123456"}'
|
|
356
|
+
|
|
357
|
+
# 3. Request cancel (no auth; email lookup)
|
|
358
|
+
curl -X POST http://localhost:9000/store/customers/account-deletion/cancel-request \
|
|
359
|
+
-d '{"email": "customer@example.com"}'
|
|
360
|
+
|
|
361
|
+
# 4. Confirm cancel
|
|
362
|
+
curl -X POST http://localhost:9000/store/customers/account-deletion/cancel-confirm \
|
|
363
|
+
-d '{"token": "...", "code": "123456"}'
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Phone-only Registration & Login (`phonepass`)
|
|
369
|
+
|
|
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`
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
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
|
+
},
|
|
389
|
+
},
|
|
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
|
+
},
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
})
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### 2. Registration flow
|
|
412
|
+
|
|
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>" }
|
|
418
|
+
|
|
419
|
+
# Step 2 — create customer record
|
|
420
|
+
POST /store/customers
|
|
421
|
+
Authorization: Bearer <token>
|
|
422
|
+
{ "phone": "+15551234567", "first_name": "Jane" }
|
|
423
|
+
|
|
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
|
+
```
|
|
434
|
+
|
|
435
|
+
### 3. Login flow
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
POST /auth/customer/phonepass
|
|
439
|
+
{ "phone": "+15551234567", "password": "SecretPass1!" }
|
|
440
|
+
# Response: { "token": "<jwt>" }
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### 4. Update phone (after registration)
|
|
444
|
+
|
|
445
|
+
```bash
|
|
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
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## Password Reset (Helper Functions)
|
|
255
494
|
|
|
256
495
|
```typescript
|
|
257
496
|
import {
|
|
@@ -259,13 +498,11 @@ import {
|
|
|
259
498
|
completePasswordReset,
|
|
260
499
|
} from "customer-registration/helpers"
|
|
261
500
|
|
|
262
|
-
// Request password reset
|
|
263
501
|
await requestPasswordReset(
|
|
264
502
|
{ email: "customer@example.com" },
|
|
265
503
|
{ baseUrl: "https://store.example.com", publishableApiKey: "pk_..." }
|
|
266
504
|
)
|
|
267
505
|
|
|
268
|
-
// Complete password reset
|
|
269
506
|
await completePasswordReset(
|
|
270
507
|
{
|
|
271
508
|
email: "customer@example.com",
|
|
@@ -276,24 +513,29 @@ await completePasswordReset(
|
|
|
276
513
|
)
|
|
277
514
|
```
|
|
278
515
|
|
|
279
|
-
|
|
516
|
+
## Requirements
|
|
280
517
|
|
|
281
|
-
|
|
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"`)
|
|
282
523
|
|
|
283
|
-
|
|
524
|
+
## Documentation
|
|
284
525
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
```
|
|
526
|
+
- **[USAGE.md](./USAGE.md)** — Complete usage guide with examples
|
|
527
|
+
- **[README.md](./README.md)** — This file (overview and quick start)
|
|
288
528
|
|
|
289
|
-
|
|
529
|
+
## Development
|
|
290
530
|
|
|
291
531
|
```bash
|
|
532
|
+
# Build
|
|
533
|
+
npm run build
|
|
534
|
+
|
|
535
|
+
# Watch mode
|
|
292
536
|
npx medusa plugin:develop
|
|
293
537
|
```
|
|
294
538
|
|
|
295
|
-
This command watches for changes and automatically rebuilds and publishes the plugin to the local registry.
|
|
296
|
-
|
|
297
539
|
## License
|
|
298
540
|
|
|
299
541
|
MIT
|