customer-registration 0.0.114 → 0.0.115
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/api/auth/customer/emailpass/route.js +9 -127
- package/.medusa/server/src/api/auth/customer/phonepass/route.js +30 -85
- package/.medusa/server/src/api/auth/customer/shared/__tests__/build-unified-login-auth-data.test.js +54 -0
- package/.medusa/server/src/api/auth/customer/shared/__tests__/parse-login-body.test.js +79 -0
- package/.medusa/server/src/api/auth/customer/shared/build-unified-login-auth-data.js +47 -0
- package/.medusa/server/src/api/auth/customer/shared/complete-customer-login.js +79 -0
- package/.medusa/server/src/api/auth/customer/shared/customer-login-post.js +140 -0
- package/.medusa/server/src/api/auth/customer/shared/enforce-registration-verification.js +38 -0
- package/.medusa/server/src/api/auth/customer/shared/parse-login-body.js +42 -0
- package/README.md +16 -11
- package/package.json +1 -1
- package/.medusa/server/src/api/auth/customer/phonepass/register/route.js +0 -50
|
@@ -1,132 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.POST =
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const config = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
|
|
11
|
-
const loginOptions = (0, config_1.resolveCustomerRegistrationOptions)(config);
|
|
12
|
-
if (loginOptions.login.identifier === "phone") {
|
|
13
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, "Email login is not enabled. Please log in with your phone number.");
|
|
14
|
-
}
|
|
15
|
-
const service = req.scope.resolve(utils_1.Modules.AUTH);
|
|
16
|
-
const authData = {
|
|
17
|
-
url: req.url,
|
|
18
|
-
headers: req.headers,
|
|
19
|
-
query: req.query,
|
|
20
|
-
body: req.body,
|
|
21
|
-
protocol: req.protocol,
|
|
22
|
-
};
|
|
23
|
-
const { success, error, authIdentity, location } = await service.authenticate("emailpass", authData);
|
|
24
|
-
if (location) {
|
|
25
|
-
return res.status(200).json({ location });
|
|
26
|
-
}
|
|
27
|
-
if (success && authIdentity) {
|
|
28
|
-
const email = authIdentity.provider_identities?.[0]?.entity_id ?? "";
|
|
29
|
-
// Ensure authIdentity has customer_id in app_metadata
|
|
30
|
-
// The generateJwtTokenForAuthIdentity function requires app_metadata.customer_id
|
|
31
|
-
let customerId = authIdentity.app_metadata?.customer_id;
|
|
32
|
-
if (!customerId) {
|
|
33
|
-
// Try to get customer_id from provider_identity entity_id (email)
|
|
34
|
-
// and look up the customer
|
|
35
|
-
const customerModule = req.scope.resolve(utils_1.Modules.CUSTOMER);
|
|
36
|
-
const customers = await customerModule.listCustomers({
|
|
37
|
-
email: email.toLowerCase(),
|
|
38
|
-
});
|
|
39
|
-
if (customers && customers.length > 0) {
|
|
40
|
-
customerId = customers[0].id;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
const resolvedCustomerId = typeof customerId === "string" && customerId.trim().length > 0
|
|
44
|
-
? customerId
|
|
45
|
-
: undefined;
|
|
46
|
-
if (!resolvedCustomerId) {
|
|
47
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Unable to determine customer ID for authentication");
|
|
48
|
-
}
|
|
49
|
-
await enforceEmailAndPhoneVerification({
|
|
50
|
-
customerId: resolvedCustomerId,
|
|
51
|
-
email,
|
|
52
|
-
req,
|
|
53
|
-
});
|
|
54
|
-
const accountDeletionService = req.scope.resolve(account_deletion_request_1.ACCOUNT_DELETION_REQUEST_MODULE);
|
|
55
|
-
if (await accountDeletionService.hasPendingRequest(resolvedCustomerId)) {
|
|
56
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, "Customer has an active account deletion request");
|
|
57
|
-
}
|
|
58
|
-
// Ensure app_metadata has customer_id
|
|
59
|
-
const authIdentityWithCustomerId = {
|
|
60
|
-
...authIdentity,
|
|
61
|
-
app_metadata: {
|
|
62
|
-
...authIdentity.app_metadata,
|
|
63
|
-
customer_id: resolvedCustomerId,
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
const { http } = config.projectConfig;
|
|
67
|
-
if (!http.jwtSecret) {
|
|
68
|
-
console.error("[emailpass-auth] JWT secret is not configured", {
|
|
69
|
-
email,
|
|
70
|
-
customerId: resolvedCustomerId,
|
|
71
|
-
hasJwtSecret: !!http.jwtSecret,
|
|
72
|
-
});
|
|
73
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "JWT secret is not configured");
|
|
74
|
-
}
|
|
75
|
-
let token;
|
|
76
|
-
try {
|
|
77
|
-
token = await (0, generate_jwt_token_1.generateJwtTokenForAuthIdentity)({
|
|
78
|
-
authIdentity: authIdentityWithCustomerId,
|
|
79
|
-
actorType: "customer",
|
|
80
|
-
}, {
|
|
81
|
-
secret: http.jwtSecret,
|
|
82
|
-
expiresIn: http.jwtExpiresIn || "7d",
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
catch (jwtError) {
|
|
86
|
-
console.error("[emailpass-auth] JWT generation threw an exception", {
|
|
87
|
-
email,
|
|
88
|
-
customerId: resolvedCustomerId,
|
|
89
|
-
error: jwtError instanceof Error ? jwtError.message : String(jwtError),
|
|
90
|
-
stack: jwtError instanceof Error ? jwtError.stack : undefined,
|
|
91
|
-
jwtSecretExists: !!http.jwtSecret,
|
|
92
|
-
});
|
|
93
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `JWT generation failed: ${jwtError instanceof Error ? jwtError.message : "Unknown error"}`);
|
|
94
|
-
}
|
|
95
|
-
// Validate token was generated successfully
|
|
96
|
-
if (!token || typeof token !== "string") {
|
|
97
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to generate authentication token for customer ${resolvedCustomerId}. Token type: ${typeof token}, Token value: ${token}`);
|
|
98
|
-
}
|
|
99
|
-
return res.status(200).json({ token });
|
|
100
|
-
}
|
|
101
|
-
console.error("[emailpass-auth] Authentication failed", {
|
|
102
|
-
success,
|
|
103
|
-
error,
|
|
104
|
-
hasAuthIdentity: !!authIdentity,
|
|
105
|
-
});
|
|
106
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, error || "Authentication failed");
|
|
107
|
-
};
|
|
108
|
-
exports.GET = GET;
|
|
3
|
+
exports.POST = void 0;
|
|
4
|
+
const customer_login_post_1 = require("../shared/customer-login-post");
|
|
5
|
+
/**
|
|
6
|
+
* Customer login (unified): delegates to `handleCustomerLogin`.
|
|
7
|
+
* - POST + JSON body: `{ email, password }` **or** `{ phone, password }` (exactly one of email/phone), gated by plugin `login.identifier`.
|
|
8
|
+
* - Optional query params can fill missing fields on POST (see `buildUnifiedLoginAuthData`); prefer JSON body for credentials.
|
|
9
|
+
*/
|
|
109
10
|
const POST = async (req, res) => {
|
|
110
|
-
await (0,
|
|
11
|
+
await (0, customer_login_post_1.handleCustomerLogin)(req, res);
|
|
111
12
|
};
|
|
112
13
|
exports.POST = POST;
|
|
113
|
-
|
|
114
|
-
// Read identifier + require_verification from plugin config
|
|
115
|
-
const configModule = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
|
|
116
|
-
const options = (0, config_1.resolveCustomerRegistrationOptions)(configModule);
|
|
117
|
-
const { identifier, require_verification } = options.registration;
|
|
118
|
-
// If verification is disabled entirely, skip all checks
|
|
119
|
-
if (!require_verification) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const otpService = req.scope.resolve(otp_verification_1.OTP_VERIFICATION_MODULE);
|
|
123
|
-
const verificationStatus = await otpService.getCustomerVerificationByCustomerId(req.scope, customerId);
|
|
124
|
-
if ((identifier === "email" || identifier === "both") && !verificationStatus.email_verified) {
|
|
125
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, "Email not verified.");
|
|
126
|
-
}
|
|
127
|
-
if ((identifier === "phone" || identifier === "both") && !verificationStatus.phone_verified) {
|
|
128
|
-
console.error("[emailpass-auth] Phone not verified", { email });
|
|
129
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, "Phone not verified.");
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2F1dGgvY3VzdG9tZXIvZW1haWxwYXNzL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQU1BLHFEQUlrQztBQUNsQywyRkFBb0c7QUFDcEcsMkZBQThGO0FBRTlGLDJFQUE4RTtBQUk5RSwrQ0FHMkI7QUFFcEIsTUFBTSxHQUFHLEdBQUcsS0FBSyxFQUFFLEdBQWtCLEVBQUUsR0FBbUIsRUFBRSxFQUFFO0lBQ25FLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUM5QixpQ0FBeUIsQ0FBQyxhQUFhLENBQ3hDLENBQUE7SUFFRCxNQUFNLFlBQVksR0FBRyxJQUFBLDJDQUFrQyxFQUFDLE1BQTJCLENBQUMsQ0FBQTtJQUNwRixJQUFJLFlBQVksQ0FBQyxLQUFLLENBQUMsVUFBVSxLQUFLLE9BQU8sRUFBRSxDQUFDO1FBQzlDLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLG1FQUFtRSxDQUNwRSxDQUFBO0lBQ0gsQ0FBQztJQUVELE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGVBQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUUvQyxNQUFNLFFBQVEsR0FBRztRQUNmLEdBQUcsRUFBRSxHQUFHLENBQUMsR0FBRztRQUNaLE9BQU8sRUFBRSxHQUFHLENBQUMsT0FBTztRQUNwQixLQUFLLEVBQUUsR0FBRyxDQUFDLEtBQUs7UUFDaEIsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJO1FBQ2QsUUFBUSxFQUFFLEdBQUcsQ0FBQyxRQUFRO0tBQ0EsQ0FBQTtJQUV4QixNQUFNLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxZQUFZLEVBQUUsUUFBUSxFQUFFLEdBQzlDLE1BQU0sT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLEVB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
|
|
14
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2F1dGgvY3VzdG9tZXIvZW1haWxwYXNzL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHVFQUFtRTtBQUVuRTs7OztHQUlHO0FBRUksTUFBTSxJQUFJLEdBQUcsS0FBSyxFQUFFLEdBQWtCLEVBQUUsR0FBbUIsRUFBRSxFQUFFO0lBQ3BFLE1BQU0sSUFBQSx5Q0FBbUIsRUFBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUE7QUFDckMsQ0FBQyxDQUFBO0FBRlksUUFBQSxJQUFJLFFBRWhCIn0=
|
|
@@ -3,15 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.POST = void 0;
|
|
4
4
|
const utils_1 = require("@medusajs/framework/utils");
|
|
5
5
|
const generate_jwt_token_1 = require("@medusajs/medusa/api/auth/utils/generate-jwt-token");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Register a new customer with phone + password.
|
|
8
|
+
*
|
|
9
|
+
* On success the caller receives a short-lived JWT token. Use it as the
|
|
10
|
+
* Bearer token when calling `POST /store/customers` to create the customer
|
|
11
|
+
* record and link it to this auth identity.
|
|
12
|
+
*/
|
|
9
13
|
const POST = async (req, res) => {
|
|
10
14
|
const config = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
|
|
11
|
-
const loginOptions = (0, config_1.resolveCustomerRegistrationOptions)(config);
|
|
12
|
-
if (loginOptions.login.identifier === "email") {
|
|
13
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, "Phone login is not enabled. Please log in with your email.");
|
|
14
|
-
}
|
|
15
15
|
const service = req.scope.resolve(utils_1.Modules.AUTH);
|
|
16
16
|
const authData = {
|
|
17
17
|
url: req.url,
|
|
@@ -20,86 +20,31 @@ const POST = async (req, res) => {
|
|
|
20
20
|
body: req.body,
|
|
21
21
|
protocol: req.protocol,
|
|
22
22
|
};
|
|
23
|
-
const { success, error, authIdentity } = await service.
|
|
24
|
-
if (success
|
|
25
|
-
|
|
26
|
-
await enforcePhoneVerification({ authIdentity, req });
|
|
27
|
-
let customerId = authIdentity.app_metadata?.customer_id;
|
|
28
|
-
if (!customerId) {
|
|
29
|
-
const knex = req.scope.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
|
|
30
|
-
if (knex) {
|
|
31
|
-
const result = await knex.raw(`SELECT id FROM customer WHERE phone = ? LIMIT 1`, [phone]);
|
|
32
|
-
const row = result.rows?.[0] ?? result[0]?.[0];
|
|
33
|
-
if (row?.id) {
|
|
34
|
-
customerId = row.id;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (!customerId) {
|
|
39
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Unable to determine customer ID for authentication");
|
|
40
|
-
}
|
|
41
|
-
const accountDeletionService = req.scope.resolve(account_deletion_request_1.ACCOUNT_DELETION_REQUEST_MODULE);
|
|
42
|
-
if (await accountDeletionService.hasPendingRequest(customerId)) {
|
|
43
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, "Customer has an active account deletion request");
|
|
44
|
-
}
|
|
45
|
-
const authIdentityWithCustomerId = {
|
|
46
|
-
...authIdentity,
|
|
47
|
-
app_metadata: {
|
|
48
|
-
...authIdentity.app_metadata,
|
|
49
|
-
customer_id: customerId,
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
const { http } = config.projectConfig;
|
|
53
|
-
if (!http.jwtSecret) {
|
|
54
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "JWT secret is not configured");
|
|
55
|
-
}
|
|
56
|
-
let token;
|
|
57
|
-
try {
|
|
58
|
-
token = await (0, generate_jwt_token_1.generateJwtTokenForAuthIdentity)({
|
|
59
|
-
authIdentity: authIdentityWithCustomerId,
|
|
60
|
-
actorType: "customer",
|
|
61
|
-
}, {
|
|
62
|
-
secret: http.jwtSecret,
|
|
63
|
-
expiresIn: http.jwtExpiresIn || "7d",
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
catch (jwtError) {
|
|
67
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `JWT generation failed: ${jwtError instanceof Error ? jwtError.message : "Unknown error"}`);
|
|
68
|
-
}
|
|
69
|
-
if (!token || typeof token !== "string") {
|
|
70
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Failed to generate authentication token");
|
|
71
|
-
}
|
|
72
|
-
return res.status(200).json({ token });
|
|
23
|
+
const { success, error, authIdentity } = await service.register("phonepass", authData);
|
|
24
|
+
if (!success || !authIdentity) {
|
|
25
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, error || "Registration failed");
|
|
73
26
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const enforcePhoneVerification = async ({ authIdentity, req, }) => {
|
|
78
|
-
const configModule = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
|
|
79
|
-
const options = (0, config_1.resolveCustomerRegistrationOptions)(configModule);
|
|
80
|
-
const { require_verification } = options.registration;
|
|
81
|
-
if (!require_verification) {
|
|
82
|
-
return;
|
|
27
|
+
const { http } = config.projectConfig;
|
|
28
|
+
if (!http.jwtSecret) {
|
|
29
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "JWT secret is not configured");
|
|
83
30
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
31
|
+
let token;
|
|
32
|
+
try {
|
|
33
|
+
token = await (0, generate_jwt_token_1.generateJwtTokenForAuthIdentity)({
|
|
34
|
+
authIdentity,
|
|
35
|
+
actorType: "customer",
|
|
36
|
+
}, {
|
|
37
|
+
secret: http.jwtSecret,
|
|
38
|
+
expiresIn: http.jwtExpiresIn || "7d",
|
|
39
|
+
});
|
|
92
40
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (!verificationStatus.phone_verified) {
|
|
96
|
-
// Surface a helpful message when a phone change is pending verification
|
|
97
|
-
const knex = req.scope.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
|
|
98
|
-
const row = await knex.raw(`SELECT metadata->>'pending_phone' AS pending_phone FROM customer WHERE id = ? LIMIT 1`, [customerId]);
|
|
99
|
-
const pendingPhone = (row.rows?.[0] ?? row[0]?.[0])?.pending_phone ?? null;
|
|
100
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, pendingPhone
|
|
101
|
-
? "Your new phone number is pending verification. Please verify it to continue."
|
|
102
|
-
: "Phone not verified.");
|
|
41
|
+
catch (jwtError) {
|
|
42
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `JWT generation failed: ${jwtError instanceof Error ? jwtError.message : "Unknown error"}`);
|
|
103
43
|
}
|
|
44
|
+
if (!token || typeof token !== "string") {
|
|
45
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Failed to generate registration token");
|
|
46
|
+
}
|
|
47
|
+
return res.status(200).json({ token });
|
|
104
48
|
};
|
|
105
|
-
|
|
49
|
+
exports.POST = POST;
|
|
50
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2F1dGgvY3VzdG9tZXIvcGhvbmVwYXNzL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBLHFEQUlrQztBQUNsQywyRkFBb0c7QUFFcEc7Ozs7OztHQU1HO0FBQ0ksTUFBTSxJQUFJLEdBQUcsS0FBSyxFQUFFLEdBQWtCLEVBQUUsR0FBbUIsRUFBRSxFQUFFO0lBQ3BFLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLGFBQWEsQ0FBQyxDQUFBO0lBQ3pFLE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGVBQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUUvQyxNQUFNLFFBQVEsR0FBRztRQUNmLEdBQUcsRUFBRSxHQUFHLENBQUMsR0FBRztRQUNaLE9BQU8sRUFBRSxHQUFHLENBQUMsT0FBTztRQUNwQixLQUFLLEVBQUUsR0FBRyxDQUFDLEtBQUs7UUFDaEIsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJO1FBQ2QsUUFBUSxFQUFFLEdBQUcsQ0FBQyxRQUFRO0tBQ0EsQ0FBQTtJQUV4QixNQUFNLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxZQUFZLEVBQUUsR0FBRyxNQUFNLE9BQU8sQ0FBQyxRQUFRLENBQzdELFdBQVcsRUFDWCxRQUFRLENBQ1QsQ0FBQTtJQUVELElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUM5QixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5QixLQUFLLElBQUkscUJBQXFCLENBQy9CLENBQUE7SUFDSCxDQUFDO0lBRUQsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUE7SUFFckMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNwQixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5Qiw4QkFBOEIsQ0FDL0IsQ0FBQTtJQUNILENBQUM7SUFFRCxJQUFJLEtBQXlCLENBQUE7SUFDN0IsSUFBSSxDQUFDO1FBQ0gsS0FBSyxHQUFHLE1BQU0sSUFBQSxvREFBK0IsRUFDM0M7WUFDRSxZQUFZO1lBQ1osU0FBUyxFQUFFLFVBQVU7U0FDdEIsRUFDRDtZQUNFLE1BQU0sRUFBRSxJQUFJLENBQUMsU0FBUztZQUN0QixTQUFTLEVBQUUsSUFBSSxDQUFDLFlBQVksSUFBSSxJQUFJO1NBQ3JDLENBQ0YsQ0FBQTtJQUNILENBQUM7SUFBQyxPQUFPLFFBQVEsRUFBRSxDQUFDO1FBQ2xCLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsRUFDbEMsMEJBQTBCLFFBQVEsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLGVBQWUsRUFBRSxDQUMzRixDQUFBO0lBQ0gsQ0FBQztJQUVELElBQUksQ0FBQyxLQUFLLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDeEMsTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLGdCQUFnQixFQUNsQyx1Q0FBdUMsQ0FDeEMsQ0FBQTtJQUNILENBQUM7SUFFRCxPQUFPLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQTtBQUN4QyxDQUFDLENBQUE7QUE1RFksUUFBQSxJQUFJLFFBNERoQiJ9
|
package/.medusa/server/src/api/auth/customer/shared/__tests__/build-unified-login-auth-data.test.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const build_unified_login_auth_data_1 = require("../build-unified-login-auth-data");
|
|
5
|
+
function makeReq(partial) {
|
|
6
|
+
return {
|
|
7
|
+
url: "/auth/customer/emailpass",
|
|
8
|
+
headers: {},
|
|
9
|
+
query: partial.query ?? {},
|
|
10
|
+
body: partial.body,
|
|
11
|
+
protocol: "http",
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
(0, vitest_1.describe)("buildUnifiedLoginAuthData", () => {
|
|
15
|
+
const cases = [
|
|
16
|
+
{
|
|
17
|
+
name: "POST body only",
|
|
18
|
+
req: makeReq({
|
|
19
|
+
body: { email: "a@b.co", password: "x" },
|
|
20
|
+
}),
|
|
21
|
+
wantBody: { email: "a@b.co", password: "x" },
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "GET query fills missing body fields",
|
|
25
|
+
req: makeReq({
|
|
26
|
+
body: {},
|
|
27
|
+
query: { phone: "+1555", password: "secret" },
|
|
28
|
+
}),
|
|
29
|
+
wantBody: { phone: "+1555", password: "secret" },
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "body overrides query for same key",
|
|
33
|
+
req: makeReq({
|
|
34
|
+
body: { email: "primary@x.com", password: "p1" },
|
|
35
|
+
query: { email: "ignored@x.com", password: "p2" },
|
|
36
|
+
}),
|
|
37
|
+
wantBody: { email: "primary@x.com", password: "p1" },
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "array query param takes first string",
|
|
41
|
+
req: makeReq({
|
|
42
|
+
body: {},
|
|
43
|
+
query: { email: ["x@y.com", "z@y.com"], password: "pw" },
|
|
44
|
+
}),
|
|
45
|
+
wantBody: { email: "x@y.com", password: "pw" },
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
vitest_1.it.each(cases)("$name", ({ req, wantBody }) => {
|
|
49
|
+
const auth = (0, build_unified_login_auth_data_1.buildUnifiedLoginAuthData)(req);
|
|
50
|
+
(0, vitest_1.expect)(auth.body).toEqual(wantBody);
|
|
51
|
+
(0, vitest_1.expect)(auth.query).toBe(req.query);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVpbGQtdW5pZmllZC1sb2dpbi1hdXRoLWRhdGEudGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcGkvYXV0aC9jdXN0b21lci9zaGFyZWQvX190ZXN0c19fL2J1aWxkLXVuaWZpZWQtbG9naW4tYXV0aC1kYXRhLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxtQ0FBNkM7QUFFN0Msb0ZBQTRFO0FBRTVFLFNBQVMsT0FBTyxDQUFDLE9BR2hCO0lBQ0MsT0FBTztRQUNMLEdBQUcsRUFBRSwwQkFBMEI7UUFDL0IsT0FBTyxFQUFFLEVBQUU7UUFDWCxLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUssSUFBSSxFQUFFO1FBQzFCLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSTtRQUNsQixRQUFRLEVBQUUsTUFBTTtLQUNBLENBQUE7QUFDcEIsQ0FBQztBQUVELElBQUEsaUJBQVEsRUFBQywyQkFBMkIsRUFBRSxHQUFHLEVBQUU7SUFDekMsTUFBTSxLQUFLLEdBSU47UUFDSDtZQUNFLElBQUksRUFBRSxnQkFBZ0I7WUFDdEIsR0FBRyxFQUFFLE9BQU8sQ0FBQztnQkFDWCxJQUFJLEVBQUUsRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUU7YUFDekMsQ0FBQztZQUNGLFFBQVEsRUFBRSxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRTtTQUM3QztRQUNEO1lBQ0UsSUFBSSxFQUFFLHFDQUFxQztZQUMzQyxHQUFHLEVBQUUsT0FBTyxDQUFDO2dCQUNYLElBQUksRUFBRSxFQUFFO2dCQUNSLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRTthQUM5QyxDQUFDO1lBQ0YsUUFBUSxFQUFFLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFO1NBQ2pEO1FBQ0Q7WUFDRSxJQUFJLEVBQUUsbUNBQW1DO1lBQ3pDLEdBQUcsRUFBRSxPQUFPLENBQUM7Z0JBQ1gsSUFBSSxFQUFFLEVBQUUsS0FBSyxFQUFFLGVBQWUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFO2dCQUNoRCxLQUFLLEVBQUUsRUFBRSxLQUFLLEVBQUUsZUFBZSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUU7YUFDbEQsQ0FBQztZQUNGLFFBQVEsRUFBRSxFQUFFLEtBQUssRUFBRSxlQUFlLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRTtTQUNyRDtRQUNEO1lBQ0UsSUFBSSxFQUFFLHNDQUFzQztZQUM1QyxHQUFHLEVBQUUsT0FBTyxDQUFDO2dCQUNYLElBQUksRUFBRSxFQUFFO2dCQUNSLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUMsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFO2FBQ3pELENBQUM7WUFDRixRQUFRLEVBQUUsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUU7U0FDL0M7S0FDRixDQUFBO0lBRUQsV0FBRSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsRUFBRSxFQUFFO1FBQzVDLE1BQU0sSUFBSSxHQUFHLElBQUEseURBQXlCLEVBQUMsR0FBRyxDQUFDLENBQUE7UUFDM0MsSUFBQSxlQUFNLEVBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQTtRQUNuQyxJQUFBLGVBQU0sRUFBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUNwQyxDQUFDLENBQUMsQ0FBQTtBQUNKLENBQUMsQ0FBQyxDQUFBIn0=
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const parse_login_body_1 = require("../parse-login-body");
|
|
6
|
+
(0, vitest_1.describe)("parseCustomerLoginBody", () => {
|
|
7
|
+
const cases = [
|
|
8
|
+
{
|
|
9
|
+
name: "both config + email only",
|
|
10
|
+
body: { email: "a@b.co", password: "x" },
|
|
11
|
+
loginIdentifier: "both",
|
|
12
|
+
want: { mode: "email" },
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "both config + phone only",
|
|
16
|
+
body: { phone: "+15550001", password: "x" },
|
|
17
|
+
loginIdentifier: "both",
|
|
18
|
+
want: { mode: "phone" },
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "email config + email (trim)",
|
|
22
|
+
body: { email: " u@v.com ", password: "x" },
|
|
23
|
+
loginIdentifier: "email",
|
|
24
|
+
want: { mode: "email" },
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "phone config + phone",
|
|
28
|
+
body: { phone: "7428730894", password: "x" },
|
|
29
|
+
loginIdentifier: "phone",
|
|
30
|
+
want: { mode: "phone" },
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
vitest_1.it.each(cases)("$name", ({ body, loginIdentifier, want }) => {
|
|
34
|
+
(0, vitest_1.expect)((0, parse_login_body_1.parseCustomerLoginBody)(body, loginIdentifier)).toEqual(want);
|
|
35
|
+
});
|
|
36
|
+
const errorCases = [
|
|
37
|
+
{
|
|
38
|
+
name: "both present",
|
|
39
|
+
body: { email: "a@b.co", phone: "+1", password: "x" },
|
|
40
|
+
loginIdentifier: "both",
|
|
41
|
+
wantType: utils_1.MedusaError.Types.INVALID_DATA,
|
|
42
|
+
wantMessageSubstring: "not both",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "neither present",
|
|
46
|
+
body: { password: "x" },
|
|
47
|
+
loginIdentifier: "both",
|
|
48
|
+
wantType: utils_1.MedusaError.Types.INVALID_DATA,
|
|
49
|
+
wantMessageSubstring: "Either email or phone",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "email config rejects phone-only body",
|
|
53
|
+
body: { phone: "+1555", password: "x" },
|
|
54
|
+
loginIdentifier: "email",
|
|
55
|
+
wantType: utils_1.MedusaError.Types.UNAUTHORIZED,
|
|
56
|
+
wantMessageSubstring: "Email login is not enabled",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "phone config rejects email-only body",
|
|
60
|
+
body: { email: "a@b.co", password: "x" },
|
|
61
|
+
loginIdentifier: "phone",
|
|
62
|
+
wantType: utils_1.MedusaError.Types.UNAUTHORIZED,
|
|
63
|
+
wantMessageSubstring: "Phone login is not enabled",
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
vitest_1.it.each(errorCases)("$name", ({ body, loginIdentifier, wantType, wantMessageSubstring }) => {
|
|
67
|
+
try {
|
|
68
|
+
(0, parse_login_body_1.parseCustomerLoginBody)(body, loginIdentifier);
|
|
69
|
+
vitest_1.expect.fail("expected MedusaError");
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
(0, vitest_1.expect)(e).toBeInstanceOf(utils_1.MedusaError);
|
|
73
|
+
const err = e;
|
|
74
|
+
(0, vitest_1.expect)(err.type).toBe(wantType);
|
|
75
|
+
(0, vitest_1.expect)(err.message).toContain(wantMessageSubstring);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyc2UtbG9naW4tYm9keS50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwaS9hdXRoL2N1c3RvbWVyL3NoYXJlZC9fX3Rlc3RzX18vcGFyc2UtbG9naW4tYm9keS50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsbUNBQTZDO0FBQzdDLHFEQUF1RDtBQUN2RCwwREFHNEI7QUFFNUIsSUFBQSxpQkFBUSxFQUFDLHdCQUF3QixFQUFFLEdBQUcsRUFBRTtJQUN0QyxNQUFNLEtBQUssR0FLTjtRQUNIO1lBQ0UsSUFBSSxFQUFFLDBCQUEwQjtZQUNoQyxJQUFJLEVBQUUsRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUU7WUFDeEMsZUFBZSxFQUFFLE1BQU07WUFDdkIsSUFBSSxFQUFFLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRTtTQUN4QjtRQUNEO1lBQ0UsSUFBSSxFQUFFLDBCQUEwQjtZQUNoQyxJQUFJLEVBQUUsRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUU7WUFDM0MsZUFBZSxFQUFFLE1BQU07WUFDdkIsSUFBSSxFQUFFLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRTtTQUN4QjtRQUNEO1lBQ0UsSUFBSSxFQUFFLDZCQUE2QjtZQUNuQyxJQUFJLEVBQUUsRUFBRSxLQUFLLEVBQUUsYUFBYSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUU7WUFDN0MsZUFBZSxFQUFFLE9BQU87WUFDeEIsSUFBSSxFQUFFLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRTtTQUN4QjtRQUNEO1lBQ0UsSUFBSSxFQUFFLHNCQUFzQjtZQUM1QixJQUFJLEVBQUUsRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFFLFFBQVEsRUFBRSxHQUFHLEVBQUU7WUFDNUMsZUFBZSxFQUFFLE9BQU87WUFDeEIsSUFBSSxFQUFFLEVBQUUsSUFBSSxFQUFF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
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildUnifiedLoginAuthData = buildUnifiedLoginAuthData;
|
|
4
|
+
function firstQueryValue(value) {
|
|
5
|
+
if (typeof value === "string") {
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
if (Array.isArray(value) && typeof value[0] === "string") {
|
|
9
|
+
return value[0];
|
|
10
|
+
}
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Builds `AuthenticationInput` for customer login with credentials from the JSON body
|
|
15
|
+
* and, for missing fields, from query string (supports GET with `?email&password` or `?phone&password`).
|
|
16
|
+
* Body keys take precedence over query when both are present.
|
|
17
|
+
*/
|
|
18
|
+
function buildUnifiedLoginAuthData(req) {
|
|
19
|
+
const bodyRaw = req.body;
|
|
20
|
+
const body = bodyRaw !== null && typeof bodyRaw === "object" && !Array.isArray(bodyRaw)
|
|
21
|
+
? { ...bodyRaw }
|
|
22
|
+
: {};
|
|
23
|
+
const q = req.query !== null && typeof req.query === "object" && !Array.isArray(req.query)
|
|
24
|
+
? req.query
|
|
25
|
+
: {};
|
|
26
|
+
const merged = { ...body };
|
|
27
|
+
const qEmail = firstQueryValue(q.email);
|
|
28
|
+
const qPhone = firstQueryValue(q.phone);
|
|
29
|
+
const qPassword = firstQueryValue(q.password);
|
|
30
|
+
if (merged.email === undefined && qEmail !== undefined) {
|
|
31
|
+
merged.email = qEmail;
|
|
32
|
+
}
|
|
33
|
+
if (merged.phone === undefined && qPhone !== undefined) {
|
|
34
|
+
merged.phone = qPhone;
|
|
35
|
+
}
|
|
36
|
+
if (merged.password === undefined && qPassword !== undefined) {
|
|
37
|
+
merged.password = qPassword;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
url: req.url,
|
|
41
|
+
headers: req.headers,
|
|
42
|
+
query: req.query,
|
|
43
|
+
body: merged,
|
|
44
|
+
protocol: req.protocol,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVpbGQtdW5pZmllZC1sb2dpbi1hdXRoLWRhdGEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2F1dGgvY3VzdG9tZXIvc2hhcmVkL2J1aWxkLXVuaWZpZWQtbG9naW4tYXV0aC1kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBa0JBLDhEQW1DQztBQWxERCxTQUFTLGVBQWUsQ0FBQyxLQUFjO0lBQ3JDLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDOUIsT0FBTyxLQUFLLENBQUE7SUFDZCxDQUFDO0lBQ0QsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLE9BQU8sS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ3pELE9BQU8sS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBQ2pCLENBQUM7SUFDRCxPQUFPLFNBQVMsQ0FBQTtBQUNsQixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQWdCLHlCQUF5QixDQUFDLEdBQWtCO0lBQzFELE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUE7SUFDeEIsTUFBTSxJQUFJLEdBQ1IsT0FBTyxLQUFLLElBQUksSUFBSSxPQUFPLE9BQU8sS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQztRQUN4RSxDQUFDLENBQUMsRUFBRSxHQUFJLE9BQW1DLEVBQUU7UUFDN0MsQ0FBQyxDQUFDLEVBQUUsQ0FBQTtJQUVSLE1BQU0sQ0FBQyxHQUNMLEdBQUcsQ0FBQyxLQUFLLEtBQUssSUFBSSxJQUFJLE9BQU8sR0FBRyxDQUFDLEtBQUssS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUM7UUFDOUUsQ0FBQyxDQUFFLEdBQUcsQ0FBQyxLQUFpQztRQUN4QyxDQUFDLENBQUMsRUFBRSxDQUFBO0lBRVIsTUFBTSxNQUFNLEdBQTRCLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQTtJQUVuRCxNQUFNLE1BQU0sR0FBRyxlQUFlLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFBO0lBQ3ZDLE1BQU0sTUFBTSxHQUFHLGVBQWUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUE7SUFDdkMsTUFBTSxTQUFTLEdBQUcsZUFBZSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUU3QyxJQUFJLE1BQU0sQ0FBQyxLQUFLLEtBQUssU0FBUyxJQUFJLE1BQU0sS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUN2RCxNQUFNLENBQUMsS0FBSyxHQUFHLE1BQU0sQ0FBQTtJQUN2QixDQUFDO0lBQ0QsSUFBSSxNQUFNLENBQUMsS0FBSyxLQUFLLFNBQVMsSUFBSSxNQUFNLEtBQUssU0FBUyxFQUFFLENBQUM7UUFDdkQsTUFBTSxDQUFDLEtBQUssR0FBRyxNQUFNLENBQUE7SUFDdkIsQ0FBQztJQUNELElBQUksTUFBTSxDQUFDLFFBQVEsS0FBSyxTQUFTLElBQUksU0FBUyxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQzdELE1BQU0sQ0FBQyxRQUFRLEdBQUcsU0FBUyxDQUFBO0lBQzdCLENBQUM7SUFFRCxPQUFPO1FBQ0wsR0FBRyxFQUFFLEdBQUcsQ0FBQyxHQUFHO1FBQ1osT0FBTyxFQUFFLEdBQUcsQ0FBQyxPQUFPO1FBQ3BCLEtBQUssRUFBRSxHQUFHLENBQUMsS0FBSztRQUNoQixJQUFJLEVBQUUsTUFBTTtRQUNaLFFBQVEsRUFBRSxHQUFHLENBQUMsUUFBUTtLQUNBLENBQUE7QUFDMUIsQ0FBQyJ9
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.completeCustomerLogin = completeCustomerLogin;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const generate_jwt_token_1 = require("@medusajs/medusa/api/auth/utils/generate-jwt-token");
|
|
6
|
+
const account_deletion_request_1 = require("../../../../modules/account-deletion-request");
|
|
7
|
+
/**
|
|
8
|
+
* Pending deletion check, merge customer_id into app_metadata, issue JWT, respond 200 `{ token }`.
|
|
9
|
+
*/
|
|
10
|
+
async function completeCustomerLogin(req, res, config, authIdentity, customerId, logContext) {
|
|
11
|
+
const accountDeletionService = req.scope.resolve(account_deletion_request_1.ACCOUNT_DELETION_REQUEST_MODULE);
|
|
12
|
+
if (await accountDeletionService.hasPendingRequest(customerId)) {
|
|
13
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, "Customer has an active account deletion request");
|
|
14
|
+
}
|
|
15
|
+
const authIdentityWithCustomerId = {
|
|
16
|
+
...authIdentity,
|
|
17
|
+
app_metadata: {
|
|
18
|
+
...authIdentity.app_metadata,
|
|
19
|
+
customer_id: customerId,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
const { http } = config.projectConfig ?? {};
|
|
23
|
+
if (!http?.jwtSecret) {
|
|
24
|
+
if (logContext.kind === "email") {
|
|
25
|
+
console.error("[customer-auth] JWT secret is not configured", {
|
|
26
|
+
email: logContext.email,
|
|
27
|
+
customerId,
|
|
28
|
+
hasJwtSecret: !!http?.jwtSecret,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
console.error("[customer-auth] JWT secret is not configured", {
|
|
33
|
+
phone: logContext.phone,
|
|
34
|
+
customerId,
|
|
35
|
+
hasJwtSecret: !!http?.jwtSecret,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "JWT secret is not configured");
|
|
39
|
+
}
|
|
40
|
+
let token;
|
|
41
|
+
try {
|
|
42
|
+
token = await (0, generate_jwt_token_1.generateJwtTokenForAuthIdentity)({
|
|
43
|
+
authIdentity: authIdentityWithCustomerId,
|
|
44
|
+
actorType: "customer",
|
|
45
|
+
}, {
|
|
46
|
+
secret: http.jwtSecret,
|
|
47
|
+
expiresIn: http.jwtExpiresIn || "7d",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (jwtError) {
|
|
51
|
+
if (logContext.kind === "email") {
|
|
52
|
+
console.error("[customer-auth] JWT generation threw an exception", {
|
|
53
|
+
email: logContext.email,
|
|
54
|
+
customerId,
|
|
55
|
+
error: jwtError instanceof Error ? jwtError.message : String(jwtError),
|
|
56
|
+
stack: jwtError instanceof Error ? jwtError.stack : undefined,
|
|
57
|
+
jwtSecretExists: !!http.jwtSecret,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.error("[customer-auth] JWT generation threw an exception", {
|
|
62
|
+
phone: logContext.phone,
|
|
63
|
+
customerId,
|
|
64
|
+
error: jwtError instanceof Error ? jwtError.message : String(jwtError),
|
|
65
|
+
stack: jwtError instanceof Error ? jwtError.stack : undefined,
|
|
66
|
+
jwtSecretExists: !!http.jwtSecret,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `JWT generation failed: ${jwtError instanceof Error ? jwtError.message : "Unknown error"}`);
|
|
70
|
+
}
|
|
71
|
+
if (!token || typeof token !== "string") {
|
|
72
|
+
if (logContext.kind === "email") {
|
|
73
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Failed to generate authentication token for customer ${customerId}. Token type: ${typeof token}, Token value: ${token}`);
|
|
74
|
+
}
|
|
75
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Failed to generate authentication token");
|
|
76
|
+
}
|
|
77
|
+
res.status(200).json({ token });
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGxldGUtY3VzdG9tZXItbG9naW4uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2F1dGgvY3VzdG9tZXIvc2hhcmVkL2NvbXBsZXRlLWN1c3RvbWVyLWxvZ2luLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBcUJBLHNEQW1HQztBQXRIRCxxREFBdUQ7QUFDdkQsMkZBQW9HO0FBQ3BHLDJGQUE4RjtBQWM5Rjs7R0FFRztBQUNJLEtBQUssVUFBVSxxQkFBcUIsQ0FDekMsR0FBa0IsRUFDbEIsR0FBbUIsRUFDbkIsTUFBMEIsRUFDMUIsWUFBK0IsRUFDL0IsVUFBa0IsRUFDbEIsVUFBK0U7SUFFL0UsTUFBTSxzQkFBc0IsR0FDMUIsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQ2YsMERBQStCLENBQ2hDLENBQUE7SUFDSCxJQUFJLE1BQU0sc0JBQXNCLENBQUMsaUJBQWlCLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztRQUMvRCxNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5QixpREFBaUQsQ0FDbEQsQ0FBQTtJQUNILENBQUM7SUFFRCxNQUFNLDBCQUEwQixHQUFHO1FBQ2pDLEdBQUcsWUFBWTtRQUNmLFlBQVksRUFBRTtZQUNaLEdBQUcsWUFBWSxDQUFDLFlBQVk7WUFDNUIsV0FBVyxFQUFFLFVBQVU7U0FDeEI7S0FDRixDQUFBO0lBRUQsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLE1BQU0sQ0FBQyxhQUFhLElBQUksRUFBRSxDQUFBO0lBRTNDLElBQUksQ0FBQyxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7UUFDckIsSUFBSSxVQUFVLENBQUMsSUFBSSxLQUFLLE9BQU8sRUFBRSxDQUFDO1lBQ2hDLE9BQU8sQ0FBQyxLQUFLLENBQUMsOENBQThDLEVBQUU7Z0JBQzVELEtBQUssRUFBRSxVQUFVLENBQUMsS0FBSztnQkFDdkIsVUFB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
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runEmailPassAuthFlow = runEmailPassAuthFlow;
|
|
4
|
+
exports.runPhonePassAuthFlow = runPhonePassAuthFlow;
|
|
5
|
+
exports.handleCustomerLogin = handleCustomerLogin;
|
|
6
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
7
|
+
const config_1 = require("../../../../config");
|
|
8
|
+
const complete_customer_login_1 = require("./complete-customer-login");
|
|
9
|
+
const enforce_registration_verification_1 = require("./enforce-registration-verification");
|
|
10
|
+
const build_unified_login_auth_data_1 = require("./build-unified-login-auth-data");
|
|
11
|
+
const parse_login_body_1 = require("./parse-login-body");
|
|
12
|
+
function firstKnexRow(result) {
|
|
13
|
+
const fromRows = result.rows?.[0];
|
|
14
|
+
if (fromRows && typeof fromRows === "object" && !Array.isArray(fromRows)) {
|
|
15
|
+
return fromRows;
|
|
16
|
+
}
|
|
17
|
+
const batch = result[0];
|
|
18
|
+
if (Array.isArray(batch)) {
|
|
19
|
+
const first = batch[0];
|
|
20
|
+
if (first && typeof first === "object" && !Array.isArray(first)) {
|
|
21
|
+
return first;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Email-only login (used when unified parse selects email).
|
|
28
|
+
*/
|
|
29
|
+
async function runEmailPassAuthFlow(req, res, config, authData) {
|
|
30
|
+
const service = req.scope.resolve(utils_1.Modules.AUTH);
|
|
31
|
+
const { success, error, authIdentity, location } = await service.authenticate("emailpass", authData);
|
|
32
|
+
if (location) {
|
|
33
|
+
res.status(200).json({ location });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (success && authIdentity) {
|
|
37
|
+
const email = authIdentity.provider_identities?.[0]
|
|
38
|
+
?.entity_id ?? "";
|
|
39
|
+
const appMeta = authIdentity.app_metadata;
|
|
40
|
+
let customerId = typeof appMeta?.customer_id === "string" ? appMeta.customer_id : undefined;
|
|
41
|
+
if (!customerId) {
|
|
42
|
+
const customerModule = req.scope.resolve(utils_1.Modules.CUSTOMER);
|
|
43
|
+
const customers = await customerModule.listCustomers({
|
|
44
|
+
email: email.toLowerCase(),
|
|
45
|
+
});
|
|
46
|
+
if (customers && customers.length > 0) {
|
|
47
|
+
customerId = customers[0].id;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const resolvedCustomerId = typeof customerId === "string" && customerId.trim().length > 0
|
|
51
|
+
? customerId
|
|
52
|
+
: undefined;
|
|
53
|
+
if (!resolvedCustomerId) {
|
|
54
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Unable to determine customer ID for authentication");
|
|
55
|
+
}
|
|
56
|
+
await (0, enforce_registration_verification_1.enforceRegistrationVerification)({
|
|
57
|
+
customerId: resolvedCustomerId,
|
|
58
|
+
req,
|
|
59
|
+
logLabel: email,
|
|
60
|
+
});
|
|
61
|
+
await (0, complete_customer_login_1.completeCustomerLogin)(req, res, config, authIdentity, resolvedCustomerId, {
|
|
62
|
+
kind: "email",
|
|
63
|
+
email,
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
console.error("[customer-auth] Email authentication failed", {
|
|
68
|
+
success,
|
|
69
|
+
error,
|
|
70
|
+
hasAuthIdentity: !!authIdentity,
|
|
71
|
+
});
|
|
72
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, error || "Authentication failed");
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Phone-only login (phonepass provider).
|
|
76
|
+
*/
|
|
77
|
+
async function runPhonePassAuthFlow(req, res, config, authData) {
|
|
78
|
+
const service = req.scope.resolve(utils_1.Modules.AUTH);
|
|
79
|
+
const { success, error, authIdentity } = await service.authenticate("phonepass", authData);
|
|
80
|
+
if (!success || !authIdentity) {
|
|
81
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, error || "Authentication failed");
|
|
82
|
+
}
|
|
83
|
+
const phone = authIdentity.provider_identities?.[0]
|
|
84
|
+
?.entity_id ?? "";
|
|
85
|
+
const appMetaPhone = authIdentity.app_metadata;
|
|
86
|
+
let customerId = typeof appMetaPhone?.customer_id === "string"
|
|
87
|
+
? appMetaPhone.customer_id
|
|
88
|
+
: undefined;
|
|
89
|
+
if (!customerId) {
|
|
90
|
+
const knex = req.scope.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
|
|
91
|
+
if (knex) {
|
|
92
|
+
const result = await knex.raw(`SELECT id FROM customer WHERE phone = ? LIMIT 1`, [phone]);
|
|
93
|
+
const row = firstKnexRow(result);
|
|
94
|
+
if (row?.id) {
|
|
95
|
+
customerId = row.id;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!customerId) {
|
|
100
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Unable to determine customer ID for authentication");
|
|
101
|
+
}
|
|
102
|
+
let logLabel = phone;
|
|
103
|
+
const knexEmail = req.scope.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
|
|
104
|
+
if (knexEmail) {
|
|
105
|
+
const rowResult = await knexEmail.raw(`SELECT email FROM customer WHERE id = ? LIMIT 1`, [customerId]);
|
|
106
|
+
const row = firstKnexRow(rowResult);
|
|
107
|
+
const e = row?.email;
|
|
108
|
+
if (typeof e === "string" && e.trim()) {
|
|
109
|
+
logLabel = e.trim();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
await (0, enforce_registration_verification_1.enforceRegistrationVerification)({
|
|
113
|
+
customerId,
|
|
114
|
+
req,
|
|
115
|
+
logLabel,
|
|
116
|
+
});
|
|
117
|
+
await (0, complete_customer_login_1.completeCustomerLogin)(req, res, config, authIdentity, customerId, {
|
|
118
|
+
kind: "phone",
|
|
119
|
+
phone,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Unified customer login: POST (JSON body) or GET (query `email`|`phone` + `password`).
|
|
124
|
+
* Credentials are merged with body taking precedence over query (see `buildUnifiedLoginAuthData`).
|
|
125
|
+
*/
|
|
126
|
+
async function handleCustomerLogin(req, res) {
|
|
127
|
+
const config = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
|
|
128
|
+
const loginOptions = (0, config_1.resolveCustomerRegistrationOptions)(config);
|
|
129
|
+
const loginIdentifier = loginOptions.login.identifier;
|
|
130
|
+
const authData = (0, build_unified_login_auth_data_1.buildUnifiedLoginAuthData)(req);
|
|
131
|
+
const parseSource = authData.body !== null && typeof authData.body === "object" && !Array.isArray(authData.body)
|
|
132
|
+
? authData.body
|
|
133
|
+
: {};
|
|
134
|
+
const parsed = (0, parse_login_body_1.parseCustomerLoginBody)(parseSource, loginIdentifier);
|
|
135
|
+
if (parsed.mode === "email") {
|
|
136
|
+
return runEmailPassAuthFlow(req, res, config, authData);
|
|
137
|
+
}
|
|
138
|
+
return runPhonePassAuthFlow(req, res, config, authData);
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItbG9naW4tcG9zdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcGkvYXV0aC9jdXN0b21lci9zaGFyZWQvY3VzdG9tZXItbG9naW4tcG9zdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQWlEQSxvREEyRUM7QUFLRCxvREF1RkM7QUFNRCxrREF3QkM7QUFwUEQscURBSWtDO0FBRWxDLCtDQUF1RTtBQUN2RSx1RUFBMEY7QUFDMUYsMkZBQXFGO0FBQ3JGLG1GQUEyRTtBQUMzRSx5REFHMkI7QUFFM0IsU0FBUyxZQUFZLENBQ25CLE1BQW1DO0lBRW5DLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUNqQyxJQUFJLFFBQVEsSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7UUFDekUsT0FBTyxRQUFRLENBQUE7SUFDakIsQ0FBQztJQUNELE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUN2QixJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN6QixNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUE7UUFDdEIsSUFBSSxLQUFLLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ2hFLE9BQU8sS0FBVSxDQUFBO1FBQ25CLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUE7QUFDbEIsQ0FBQztBQWNEOztHQUVHO0FBQ0ksS0FBSyxVQUFVLG9CQUFvQixDQUN4QyxHQUFrQixFQUNsQixHQUFtQixFQUNuQixNQUE4QyxFQUM5QyxRQUE2QjtJQUU3QixNQUFNLE9B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
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enforceRegistrationVerification = enforceRegistrationVerification;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const otp_verification_1 = require("../../../../modules/otp-verification");
|
|
6
|
+
const config_1 = require("../../../../config");
|
|
7
|
+
/**
|
|
8
|
+
* Enforces email / phone OTP verification according to `registration.identifier` and `require_verification`.
|
|
9
|
+
* When phone is required but unverified, includes `pending_phone` UX (same as legacy phonepass route).
|
|
10
|
+
*/
|
|
11
|
+
async function enforceRegistrationVerification({ customerId, req, logLabel, }) {
|
|
12
|
+
const configModule = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
|
|
13
|
+
const options = (0, config_1.resolveCustomerRegistrationOptions)(configModule);
|
|
14
|
+
const { identifier, require_verification } = options.registration;
|
|
15
|
+
if (!require_verification) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const otpService = req.scope.resolve(otp_verification_1.OTP_VERIFICATION_MODULE);
|
|
19
|
+
const verificationStatus = await otpService.getCustomerVerificationByCustomerId(req.scope, customerId);
|
|
20
|
+
if ((identifier === "email" || identifier === "both") &&
|
|
21
|
+
!verificationStatus.email_verified) {
|
|
22
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, "Email not verified.");
|
|
23
|
+
}
|
|
24
|
+
if ((identifier === "phone" || identifier === "both") &&
|
|
25
|
+
!verificationStatus.phone_verified) {
|
|
26
|
+
const knex = req.scope.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
|
|
27
|
+
const row = await knex.raw(`SELECT metadata->>'pending_phone' AS pending_phone FROM customer WHERE id = ? LIMIT 1`, [customerId]);
|
|
28
|
+
const pendingPhone = (row.rows?.[0] ?? row[0]?.[0])?.pending_phone ?? null;
|
|
29
|
+
console.error("[customer-auth] Phone not verified", {
|
|
30
|
+
logLabel,
|
|
31
|
+
customerId,
|
|
32
|
+
});
|
|
33
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, pendingPhone
|
|
34
|
+
? "Your new phone number is pending verification. Please verify it to continue."
|
|
35
|
+
: "Phone not verified.");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW5mb3JjZS1yZWdpc3RyYXRpb24tdmVyaWZpY2F0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwaS9hdXRoL2N1c3RvbWVyL3NoYXJlZC9lbmZvcmNlLXJlZ2lzdHJhdGlvbi12ZXJpZmljYXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFrQkEsMEVBK0RDO0FBaEZELHFEQUdrQztBQUdsQywyRUFBOEU7QUFFOUUsK0NBRzJCO0FBRTNCOzs7R0FHRztBQUNJLEtBQUssVUFBVSwrQkFBK0IsQ0FBQyxFQUNwRCxVQUFVLEVBQ1YsR0FBRyxFQUNILFFBQVEsR0FNVDtJQUNDLE1BQU0sWUFBWSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUNwQyxpQ0FBeUIsQ0FBQyxhQUFhLENBQ3hDLENBQUE7SUFDRCxNQUFNLE9BQU8sR0FBRyxJQUFBLDJDQUFrQyxFQUFDLFlBQVksQ0FBQyxDQUFBO0lBQ2hFLE1BQU0sRUFBRSxVQUFVLEVBQUUsb0JBQW9CLEVBQUUsR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFBO0lBRWpFLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1FBQzFCLE9BQU07SUFDUixDQUFDO0lBRUQsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQ2xDLDBDQUF1QixDQUN4QixDQUFBO0lBRUQsTUFBTSxrQkFBa0IsR0FBRyxNQUFNLFVBQVUsQ0FBQyxtQ0FBbUMsQ0FDN0UsR0FBRyxDQUFDLEtBQStCLEVBQ25DLFVBQVUsQ0FDWCxDQUFBO0lBRUQsSUFDRSxDQUFDLFVBQVUsS0FBSyxPQUFPLElBQUksVUFBVSxLQUFLLE1BQU0sQ0FBQztRQUNqRCxDQUFDLGtCQUFrQixDQUFDLGNBQWMsRUFDbEMsQ0FBQztRQUNELE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLHFCQUFxQixDQUN0QixDQUFBO0lBQ0gsQ0FBQztJQUVELElBQ0UsQ0FBQyxVQUFVLEtBQUssT0FBTyxJQUFJLFVBQVUsS0FBSyxNQUFNLENBQUM7UUFDakQsQ0FBQyxrQkFBa0IsQ0FBQyxjQUFjLEVBQ2xDLENBQUM7UUFDRCxNQUFNLElBQUksR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxhQUFhLENBQUMsQ0FBQTtRQUN2RSxNQUFNLEdBQUcsR0FBRyxNQUFNLElBQUksQ0FBQyxHQUFHLENBQ3hCLHVGQUF1RixFQUN2RixDQUFDLFVBQVUsQ0FBQyxDQUNiLENBQUE7UUFDRCxNQUFNLFlBQVksR0FDaEIsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxhQUFhLElBQUksSUFBSSxDQUFBO1FBRXZELE9BQU8sQ0FBQyxLQUFLLENBQUMsb0NBQW9DLEVBQUU7WUFDbEQsUUFBUTtZQUNSLFVBQVU7U0FDWCxDQUFDLENBQUE7UUFFRixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5QixZQUFZO1lBQ1YsQ0FBQyxDQUFDLDhFQUE4RTtZQUNoRixDQUFDLENBQUMscUJBQXFCLENBQzFCLENBQUE7SUFDSCxDQUFDO0FBQ0gsQ0FBQyJ9
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseCustomerLoginBody = parseCustomerLoginBody;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
function nonEmptyString(value) {
|
|
6
|
+
if (typeof value !== "string") {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const t = value.trim();
|
|
10
|
+
return t.length > 0 ? t : undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Determines email vs phone login from the request body and enforces `login.identifier` from plugin config.
|
|
14
|
+
* Requires exactly one identifier: XOR on non-empty `email` / `phone` (plus `password` for auth).
|
|
15
|
+
*/
|
|
16
|
+
function parseCustomerLoginBody(body, loginIdentifier) {
|
|
17
|
+
const record = body !== null && typeof body === "object"
|
|
18
|
+
? body
|
|
19
|
+
: {};
|
|
20
|
+
const email = nonEmptyString(record.email);
|
|
21
|
+
const phone = nonEmptyString(record.phone);
|
|
22
|
+
if (email && phone) {
|
|
23
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Provide either email or phone, not both.");
|
|
24
|
+
}
|
|
25
|
+
if (!email && !phone) {
|
|
26
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Either email or phone is required.");
|
|
27
|
+
}
|
|
28
|
+
if (loginIdentifier === "email") {
|
|
29
|
+
if (phone && !email) {
|
|
30
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, "Email login is not enabled. Please log in with your email.");
|
|
31
|
+
}
|
|
32
|
+
return { mode: "email" };
|
|
33
|
+
}
|
|
34
|
+
if (loginIdentifier === "phone") {
|
|
35
|
+
if (email && !phone) {
|
|
36
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNAUTHORIZED, "Phone login is not enabled. Please log in with your phone number.");
|
|
37
|
+
}
|
|
38
|
+
return { mode: "phone" };
|
|
39
|
+
}
|
|
40
|
+
return email ? { mode: "email" } : { mode: "phone" };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyc2UtbG9naW4tYm9keS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcGkvYXV0aC9jdXN0b21lci9zaGFyZWQvcGFyc2UtbG9naW4tYm9keS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQW9CQSx3REErQ0M7QUFuRUQscURBQXVEO0FBUXZELFNBQVMsY0FBYyxDQUFDLEtBQWM7SUFDcEMsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUM5QixPQUFPLFNBQVMsQ0FBQTtJQUNsQixDQUFDO0lBQ0QsTUFBTSxDQUFDLEdBQUcsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFBO0lBQ3RCLE9BQU8sQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFBO0FBQ3JDLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxTQUFnQixzQkFBc0IsQ0FDcEMsSUFBYSxFQUNiLGVBQXNDO0lBRXRDLE1BQU0sTUFBTSxHQUNWLElBQUksS0FBSyxJQUFJLElBQUksT0FBTyxJQUFJLEtBQUssUUFBUTtRQUN2QyxDQUFDLENBQUUsSUFBZ0M7UUFDbkMsQ0FBQyxDQUFDLEVBQUUsQ0FBQTtJQUVSLE1BQU0sS0FBSyxHQUFHLGNBQWMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUE7SUFDMUMsTUFBTSxLQUFLLEdBQUcsY0FBYyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUUxQyxJQUFJLEtBQUssSUFBSSxLQUFLLEVBQUUsQ0FBQztRQUNuQixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5QiwwQ0FBMEMsQ0FDM0MsQ0FBQTtJQUNILENBQUM7SUFFRCxJQUFJLENBQUMsS0FBSyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDckIsTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLFlBQVksRUFDOUIsb0NBQW9DLENBQ3JDLENBQUE7SUFDSCxDQUFDO0lBRUQsSUFBSSxlQUFlLEtBQUssT0FBTyxFQUFFLENBQUM7UUFDaEMsSUFBSSxLQUFLLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNwQixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5Qiw0REFBNEQsQ0FDN0QsQ0FBQTtRQUNILENBQUM7UUFDRCxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUFBO0lBQzFCLENBQUM7SUFFRCxJQUFJLGVBQWUsS0FBSyxPQUFPLEVBQUUsQ0FBQztRQUNoQyxJQUFJLEtBQUssSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ3BCLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQzlCLG1FQUFtRSxDQUNwRSxDQUFBO1FBQ0gsQ0FBQztRQUNELE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQUE7SUFDMUIsQ0FBQztJQUVELE9BQU8sS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQUE7QUFDdEQsQ0FBQyJ9
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ A comprehensive Medusa v2 plugin that provides OTP-based verification for email
|
|
|
13
13
|
- **Throttling & Rate Limiting**: Built-in protection against OTP spam
|
|
14
14
|
- **Database Migrations**: Automatic schema updates for verification columns
|
|
15
15
|
- **Account Deletion Request Flow**: Two-step OTP flow to request and confirm account deletion with optional cancel flow
|
|
16
|
-
- **Phone
|
|
16
|
+
- **Phone + unified login**: `phonepass` provider for **registering** with phone + password (`POST /auth/customer/phonepass`); **login** is always `POST /auth/customer/emailpass` with `{ phone, password }` or `{ email, password }` depending on `login.identifier`
|
|
17
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
|
|
18
18
|
|
|
19
19
|
## Quick Start
|
|
@@ -131,9 +131,11 @@ Controls which auth providers are accepted at login. Defaults to `registration.i
|
|
|
131
131
|
|
|
132
132
|
| Value | Accepted login methods |
|
|
133
133
|
|---|---|
|
|
134
|
-
| `"email"` | `POST /auth/customer/emailpass` only |
|
|
135
|
-
| `"phone"` | `
|
|
136
|
-
| `"both"` |
|
|
134
|
+
| `"email"` | `POST /auth/customer/emailpass` with `{ "email", "password" }` only |
|
|
135
|
+
| `"phone"` | Same endpoint with `{ "phone", "password" }` only |
|
|
136
|
+
| `"both"` | Same endpoint; **either** email **or** phone (not both) plus `password` in the JSON body |
|
|
137
|
+
|
|
138
|
+
All of the above use the **same URL**; there is no separate `/auth/customer/phonepass` **login** route in this plugin.
|
|
137
139
|
|
|
138
140
|
### Full options reference
|
|
139
141
|
|
|
@@ -223,14 +225,17 @@ POST /store/customers/otp/verify
|
|
|
223
225
|
|
|
224
226
|
---
|
|
225
227
|
|
|
226
|
-
### Login
|
|
228
|
+
### Login & registration (customer auth)
|
|
229
|
+
|
|
230
|
+
Implementation lives under [`src/api/auth/customer/`](src/api/auth/customer/): **`emailpass/`** (login + password reset), **`phonepass/`** (phone **registration** only), **`shared/`** (unified login: parse body, merge body/query for `AuthenticationInput`, verification, JWT).
|
|
227
231
|
|
|
228
232
|
| Endpoint | Method | Description |
|
|
229
233
|
|---|---|---|
|
|
230
|
-
| `/auth/customer/emailpass` | POST | Login
|
|
231
|
-
| `/auth/customer/
|
|
232
|
-
| `/auth/customer/
|
|
233
|
-
|
|
234
|
+
| `/auth/customer/emailpass` | POST | **Login.** JSON: `password` plus **exactly one** of `email` or `phone` (XOR), validated against `login.identifier`. Handler: [`emailpass/route.ts`](src/api/auth/customer/emailpass/route.ts) → [`handleCustomerLogin`](src/api/auth/customer/shared/customer-login-post.ts). |
|
|
235
|
+
| `/auth/customer/emailpass/register` | POST | Create **emailpass** identity (standard Medusa auth route; use when registering with email). |
|
|
236
|
+
| `/auth/customer/phonepass` | POST | Create **phonepass** identity only — [`phonepass/route.ts`](src/api/auth/customer/phonepass/route.ts). Returns a short-lived JWT for `POST /store/customers`. **Not** used for login. |
|
|
237
|
+
|
|
238
|
+
Password reset URLs are unchanged; see [Password Reset](#password-reset).
|
|
234
239
|
|
|
235
240
|
---
|
|
236
241
|
|
|
@@ -412,7 +417,7 @@ export default defineConfig({
|
|
|
412
417
|
|
|
413
418
|
```bash
|
|
414
419
|
# Step 1 — create phonepass auth identity
|
|
415
|
-
POST /auth/customer/phonepass
|
|
420
|
+
POST /auth/customer/phonepass
|
|
416
421
|
{ "phone": "+15551234567", "password": "SecretPass1!" }
|
|
417
422
|
# Response: { "token": "<pre-customer jwt>" }
|
|
418
423
|
|
|
@@ -435,7 +440,7 @@ POST /store/customers/otp/verify
|
|
|
435
440
|
### 3. Login flow
|
|
436
441
|
|
|
437
442
|
```bash
|
|
438
|
-
POST /auth/customer/
|
|
443
|
+
POST /auth/customer/emailpass
|
|
439
444
|
{ "phone": "+15551234567", "password": "SecretPass1!" }
|
|
440
445
|
# Response: { "token": "<jwt>" }
|
|
441
446
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "customer-registration",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.115",
|
|
4
4
|
"description": "Medusa plugin that overrides store customer registration, enforces email/phone verification flags, and provides OTP management module.",
|
|
5
5
|
"author": "Medusa (https://medusajs.com)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.POST = void 0;
|
|
4
|
-
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
-
const generate_jwt_token_1 = require("@medusajs/medusa/api/auth/utils/generate-jwt-token");
|
|
6
|
-
/**
|
|
7
|
-
* Register a new customer with phone + password.
|
|
8
|
-
*
|
|
9
|
-
* On success the caller receives a short-lived JWT token. Use it as the
|
|
10
|
-
* Bearer token when calling `POST /store/customers` to create the customer
|
|
11
|
-
* record and link it to this auth identity.
|
|
12
|
-
*/
|
|
13
|
-
const POST = async (req, res) => {
|
|
14
|
-
const config = req.scope.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
|
|
15
|
-
const service = req.scope.resolve(utils_1.Modules.AUTH);
|
|
16
|
-
const authData = {
|
|
17
|
-
url: req.url,
|
|
18
|
-
headers: req.headers,
|
|
19
|
-
query: req.query,
|
|
20
|
-
body: req.body,
|
|
21
|
-
protocol: req.protocol,
|
|
22
|
-
};
|
|
23
|
-
const { success, error, authIdentity } = await service.register("phonepass", authData);
|
|
24
|
-
if (!success || !authIdentity) {
|
|
25
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, error || "Registration failed");
|
|
26
|
-
}
|
|
27
|
-
const { http } = config.projectConfig;
|
|
28
|
-
if (!http.jwtSecret) {
|
|
29
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "JWT secret is not configured");
|
|
30
|
-
}
|
|
31
|
-
let token;
|
|
32
|
-
try {
|
|
33
|
-
token = await (0, generate_jwt_token_1.generateJwtTokenForAuthIdentity)({
|
|
34
|
-
authIdentity,
|
|
35
|
-
actorType: "customer",
|
|
36
|
-
}, {
|
|
37
|
-
secret: http.jwtSecret,
|
|
38
|
-
expiresIn: http.jwtExpiresIn || "7d",
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
catch (jwtError) {
|
|
42
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `JWT generation failed: ${jwtError instanceof Error ? jwtError.message : "Unknown error"}`);
|
|
43
|
-
}
|
|
44
|
-
if (!token || typeof token !== "string") {
|
|
45
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Failed to generate registration token");
|
|
46
|
-
}
|
|
47
|
-
return res.status(200).json({ token });
|
|
48
|
-
};
|
|
49
|
-
exports.POST = POST;
|
|
50
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2F1dGgvY3VzdG9tZXIvcGhvbmVwYXNzL3JlZ2lzdGVyL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBLHFEQUlrQztBQUNsQywyRkFBb0c7QUFFcEc7Ozs7OztHQU1HO0FBQ0ksTUFBTSxJQUFJLEdBQUcsS0FBSyxFQUFFLEdBQWtCLEVBQUUsR0FBbUIsRUFBRSxFQUFFO0lBQ3BFLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLGFBQWEsQ0FBQyxDQUFBO0lBQ3pFLE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGVBQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUUvQyxNQUFNLFFBQVEsR0FBRztRQUNmLEdBQUcsRUFBRSxHQUFHLENBQUMsR0FBRztRQUNaLE9BQU8sRUFBRSxHQUFHLENBQUMsT0FBTztRQUNwQixLQUFLLEVBQUUsR0FBRyxDQUFDLEtBQUs7UUFDaEIsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJO1FBQ2QsUUFBUSxFQUFFLEdBQUcsQ0FBQyxRQUFRO0tBQ0EsQ0FBQTtJQUV4QixNQUFNLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxZQUFZLEVBQUUsR0FBRyxNQUFNLE9BQU8sQ0FBQyxRQUFRLENBQzdELFdBQVcsRUFDWCxRQUFRLENBQ1QsQ0FBQTtJQUVELElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUM5QixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5QixLQUFLLElBQUkscUJBQXFCLENBQy9CLENBQUE7SUFDSCxDQUFDO0lBRUQsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUE7SUFFckMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUNwQixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsWUFBWSxFQUM5Qiw4QkFBOEIsQ0FDL0IsQ0FBQTtJQUNILENBQUM7SUFFRCxJQUFJLEtBQXlCLENBQUE7SUFDN0IsSUFBSSxDQUFDO1FBQ0gsS0FBSyxHQUFHLE1BQU0sSUFBQSxvREFBK0IsRUFDM0M7WUFDRSxZQUFZO1lBQ1osU0FBUyxFQUFFLFVBQVU7U0FDdEIsRUFDRDtZQUNFLE1BQU0sRUFBRSxJQUFJLENBQUMsU0FBUztZQUN0QixTQUFTLEVBQUUsSUFBSSxDQUFDLFlBQVksSUFBSSxJQUFJO1NBQ3JDLENBQ0YsQ0FBQTtJQUNILENBQUM7SUFBQyxPQUFPLFFBQVEsRUFBRSxDQUFDO1FBQ2xCLE1BQU0sSUFBSSxtQkFBVyxDQUNuQixtQkFBVyxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsRUFDbEMsMEJBQTBCLFFBQVEsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLGVBQWUsRUFBRSxDQUMzRixDQUFBO0lBQ0gsQ0FBQztJQUVELElBQUksQ0FBQyxLQUFLLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDeEMsTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLGdCQUFnQixFQUNsQyx1Q0FBdUMsQ0FDeEMsQ0FBQTtJQUNILENBQUM7SUFFRCxPQUFPLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQTtBQUN4QyxDQUFDLENBQUE7QUE1RFksUUFBQSxJQUFJLFFBNERoQiJ9
|