@hartl-services/medusa-base 2.6.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ImageSettingInput.d.ts +3 -2
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ImageSettingInput.js +4 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginActionsSection.js +4 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginBreadcrumb.d.ts +16 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginBreadcrumb.js +25 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginDetailPage.js +1 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeatureSections.d.ts +5 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeatureSections.js +8 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesCard.d.ts +4 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesCard.js +24 -27
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.d.ts +2 -2
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.js +9 -9
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginHubCard.d.ts +5 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginHubCard.js +11 -15
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginPageShell.d.ts +10 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginPageShell.js +10 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginSettingsForm.d.ts +0 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginSettingsForm.js +73 -26
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginStatusChips.d.ts +5 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginStatusChips.js +8 -6
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ShopMasterDataBanner.d.ts +11 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ShopMasterDataBanner.js +15 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.d.ts +36 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +144 -20
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +36 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSwitch.d.ts +15 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSwitch.js +66 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-features-admin-client.d.ts +144 -20
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-view.d.ts +49 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-view.js +72 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-draft.d.ts +1 -12
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-draft.js +4 -20
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.d.ts +43 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.js +66 -0
- package/.medusa/server/src/admin/admin-components/i18n/texts.d.ts +22 -2
- package/.medusa/server/src/admin/admin-components/i18n/texts.js +29 -7
- package/.medusa/server/src/admin/admin-components/index.d.ts +12 -0
- package/.medusa/server/src/admin/admin-components/index.js +11 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminPageHeader.d.ts +22 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminPageHeader.js +8 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminSection.d.ts +26 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminSection.js +12 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/BreadcrumbTrail.d.ts +23 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/BreadcrumbTrail.js +20 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FileDropField.d.ts +21 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FileDropField.js +33 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FocusModalShell.d.ts +27 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FocusModalShell.js +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/InfoTooltip.d.ts +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/InfoTooltip.js +8 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/MultiSelectDropdown.d.ts +22 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/MultiSelectDropdown.js +14 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/PluginIcon.d.ts +10 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/PluginIcon.js +10 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/SwitchRow.d.ts +27 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/SwitchRow.js +15 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/WidgetHeader.d.ts +15 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/WidgetHeader.js +8 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/file-drop.d.ts +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/file-drop.js +16 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/multi-select.d.ts +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/multi-select.js +11 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/plugin-icon.d.ts +12 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/plugin-icon.js +29 -0
- package/.medusa/server/src/admin/index.js +3118 -2020
- package/.medusa/server/src/admin/index.mjs +3121 -2023
- package/.medusa/server/src/api/admin-first-login-password-policy.js +56 -0
- package/.medusa/server/src/api/answer-actor-type-casing-not-found.js +26 -0
- package/.medusa/server/src/api/answer-code-provider-route-not-found.js +34 -0
- package/.medusa/server/src/api/code-account-password-routes.js +92 -0
- package/.medusa/server/src/api/middlewares.js +82 -17
- package/.medusa/server/src/api/password-policy.js +55 -10
- package/.medusa/server/src/api/require-login-code-customer-email.js +40 -0
- package/.medusa/server/src/api/store/customer-auth/login-code/route.js +17 -0
- package/.medusa/server/src/api/store/customer-auth/password-policy/route.js +19 -0
- package/.medusa/server/src/customer-auth/application/prepare-code-account-password-reset.js +111 -0
- package/.medusa/server/src/customer-auth/application/request-login-code.js +145 -0
- package/.medusa/server/src/customer-auth/customer-auth-methods-snapshot.js +55 -0
- package/.medusa/server/src/customer-auth/customer-password-policy.js +40 -0
- package/.medusa/server/src/customer-auth/domain/account-linking.js +53 -0
- package/.medusa/server/src/customer-auth/domain/auth-methods.js +32 -0
- package/.medusa/server/src/customer-auth/domain/login-code.js +39 -0
- package/.medusa/server/src/customer-auth/login-code-accounts.js +146 -0
- package/.medusa/server/src/customer-auth/login-code-state.js +139 -0
- package/.medusa/server/src/customer-auth/plugin-features.js +80 -0
- package/.medusa/server/src/customer-auth/remove-provider-identity.js +18 -0
- package/.medusa/server/src/customer-auth/start-customer-auth-methods.js +20 -0
- package/.medusa/server/src/documents/plugin-features.js +8 -2
- package/.medusa/server/src/email/application/deliver-ephemeral-auth-mail.js +186 -0
- package/.medusa/server/src/email/application/deliver-login-code.js +89 -0
- package/.medusa/server/src/email/application/handle-customer-verification-event.js +29 -168
- package/.medusa/server/src/email/application/handle-password-reset-event.js +27 -166
- package/.medusa/server/src/email/customer-verification-snapshot.js +20 -108
- package/.medusa/server/src/email/domain/business-keys.js +15 -2
- package/.medusa/server/src/email/plugin-features.js +8 -3
- package/.medusa/server/src/email/rendering/default-templates.js +2 -1
- package/.medusa/server/src/email/templates/blocks/dynamic-blocks.js +19 -1
- package/.medusa/server/src/email/templates/defaults/documents.js +7 -1
- package/.medusa/server/src/email/templates/defaults/subjects.js +3 -1
- package/.medusa/server/src/email/templates/defaults/texts.js +5 -2
- package/.medusa/server/src/email/templates/render.js +2 -1
- package/.medusa/server/src/email/templates/sample-data.js +2 -1
- package/.medusa/server/src/email/templates/validate.js +8 -3
- package/.medusa/server/src/email/types/email-kinds.js +4 -2
- package/.medusa/server/src/email/types/template-data.js +9 -1
- package/.medusa/server/src/modules/customer-auth/index.js +14 -0
- package/.medusa/server/src/modules/customer-auth/service.js +35 -0
- package/.medusa/server/src/modules/email/migrations/Migration20261010090000.js +20 -0
- package/.medusa/server/src/modules/plugin-features/domain/plugin-settings.js +4 -1
- package/.medusa/server/src/modules/plugin-features/service.js +26 -1
- package/.medusa/server/src/providers/email-otp/index.js +8 -0
- package/.medusa/server/src/providers/email-otp/service.js +96 -0
- package/.medusa/server/src/public/descriptor.js +117 -5
- package/.medusa/server/src/public/effective-features.js +22 -1
- package/.medusa/server/src/public/index.js +4 -2
- package/.medusa/server/src/public/plugin-icons.js +28 -0
- package/.medusa/server/src/public/shop-master-data.js +33 -14
- package/.medusa/server/src/shared/lib/index.js +2 -1
- package/.medusa/server/src/shared/lib/refreshing-snapshot.js +122 -0
- package/.medusa/server/src/shop-config/build-food-shop-config.js +38 -1
- package/README.md +436 -87
- package/dist/customer-auth/customer-auth-methods-snapshot.d.ts +25 -0
- package/dist/customer-auth/customer-auth-methods-snapshot.js +54 -0
- package/dist/customer-auth/domain/auth-methods.d.ts +16 -0
- package/dist/customer-auth/domain/auth-methods.js +31 -0
- package/dist/customer-auth/plugin-features.d.ts +2 -0
- package/dist/customer-auth/plugin-features.js +79 -0
- package/dist/email/customer-verification-snapshot.d.ts +3 -15
- package/dist/email/customer-verification-snapshot.js +19 -107
- package/dist/email/types/email-kinds.d.ts +4 -3
- package/dist/email/types/email-kinds.js +3 -1
- package/dist/email/types/template-data.d.ts +8 -0
- package/dist/email/types/template-data.js +8 -0
- package/dist/public/descriptor.js +116 -4
- package/dist/public/effective-features.d.ts +10 -0
- package/dist/public/effective-features.js +21 -0
- package/dist/public/index.d.ts +2 -1
- package/dist/public/index.js +3 -1
- package/dist/public/plugin-icons.d.ts +7 -0
- package/dist/public/plugin-icons.js +27 -0
- package/dist/public/shop-master-data.js +32 -13
- package/dist/public/types.d.ts +63 -1
- package/dist/shared/lib/index.d.ts +1 -0
- package/dist/shared/lib/index.js +1 -0
- package/dist/shared/lib/refreshing-snapshot.d.ts +52 -0
- package/dist/shared/lib/refreshing-snapshot.js +121 -0
- package/dist/shop-config/build-food-shop-config.js +37 -0
- package/package.json +8 -3
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prepareCodeAccountPasswordReset = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const customer_auth_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/customer-auth");
|
|
6
|
+
const transaction_1 = require("../../shared/lib/transaction");
|
|
7
|
+
const auth_methods_1 = require("../domain/auth-methods");
|
|
8
|
+
const login_code_accounts_1 = require("../login-code-accounts");
|
|
9
|
+
const remove_provider_identity_1 = require("../remove-provider-identity");
|
|
10
|
+
/** `code_provider` of the verification that records "proven by a code login". */
|
|
11
|
+
const VERIFICATION_CODE_PROVIDER = "email-otp";
|
|
12
|
+
/**
|
|
13
|
+
* Records that the address was proven for the auth identity: a verified Medusa
|
|
14
|
+
* auth verification (`entity_type` "email"), the one check the linking rule,
|
|
15
|
+
* the clean-up after a code login and `customer_verification` all use. A
|
|
16
|
+
* leftover unverified row of an earlier password identity is verified instead
|
|
17
|
+
* of duplicated.
|
|
18
|
+
*/
|
|
19
|
+
const confirmAddress = async (auth, authIdentityId, entityId) => {
|
|
20
|
+
const now = new Date();
|
|
21
|
+
const [existing] = await auth.listAuthVerifications({ auth_identity_id: authIdentityId, entity_id: entityId, entity_type: "email" }, { select: ["id", "verified_at"], take: 1 });
|
|
22
|
+
if (existing?.id) {
|
|
23
|
+
if (!existing.verified_at) {
|
|
24
|
+
await auth.updateAuthVerifications({ id: existing.id, verified_at: now });
|
|
25
|
+
}
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
await auth.createAuthVerifications({
|
|
30
|
+
auth_identity_id: authIdentityId,
|
|
31
|
+
entity_id: entityId,
|
|
32
|
+
entity_type: "email",
|
|
33
|
+
code_provider: VERIFICATION_CODE_PROVIDER,
|
|
34
|
+
requested_at: now,
|
|
35
|
+
verified_at: now,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (!(0, transaction_1.isUniqueConstraintViolation)(error)) {
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
// A parallel request wrote the row; it is the same fact.
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Makes "Passwort vergessen" work for an account that so far only logged in
|
|
47
|
+
* with codes, and returns the identifier Medusa's reset route has to use
|
|
48
|
+
* (null: leave the request as it is). Medusa looks the provider identity up by
|
|
49
|
+
* the exact spelling and mails the address it was given, so:
|
|
50
|
+
*
|
|
51
|
+
* 1. Exactly one `emailpass` identity with a customer matches the address
|
|
52
|
+
* (letter case ignored): its stored spelling is the identifier, so a
|
|
53
|
+
* differently cased input still gets the mail.
|
|
54
|
+
* 2. Otherwise, if the address has a code login whose account carries a
|
|
55
|
+
* customer of that address, the address was proven by a code: customer-less
|
|
56
|
+
* `emailpass` identities of the address (left by a failed registration) are
|
|
57
|
+
* deleted for good, and an `emailpass` identity with the normalized address
|
|
58
|
+
* is added to the account, together with a verified auth verification of
|
|
59
|
+
* the address so `customer_verification` never blocks it and the code login
|
|
60
|
+
* never deletes it as unconfirmed. The identity has no password at all, and
|
|
61
|
+
* `emailpass` fails closed for a missing password, so nobody can log in
|
|
62
|
+
* with it before the reset sets one. The normalized address is the
|
|
63
|
+
* identifier.
|
|
64
|
+
* 3. Anything else (unknown address, several accounts) changes nothing.
|
|
65
|
+
*
|
|
66
|
+
* A parallel request that creates the identity first wins the unique index on
|
|
67
|
+
* `(entity_id, provider)`; the identity is then read again.
|
|
68
|
+
*/
|
|
69
|
+
const prepareCodeAccountPasswordReset = async (container, identifier) => {
|
|
70
|
+
const parsed = customer_auth_1.RequestLoginCodeSchema.safeParse({ email: identifier });
|
|
71
|
+
if (!parsed.success) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const normalized = (0, customer_auth_1.normalizeLoginEmail)(parsed.data.email);
|
|
75
|
+
const sql = container.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
|
|
76
|
+
const auth = container.resolve(utils_1.Modules.AUTH);
|
|
77
|
+
const passwordIdentities = await (0, login_code_accounts_1.findPasswordIdentities)(sql, normalized);
|
|
78
|
+
const withCustomer = passwordIdentities.filter(({ customerId }) => customerId);
|
|
79
|
+
if (withCustomer.length === 1) {
|
|
80
|
+
return withCustomer[0].entityId;
|
|
81
|
+
}
|
|
82
|
+
if (withCustomer.length > 1) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const authIdentityId = await (0, login_code_accounts_1.findCodeAccountAuthIdentityId)(sql, normalized);
|
|
86
|
+
if (!authIdentityId) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
for (const orphan of passwordIdentities) {
|
|
90
|
+
await (0, remove_provider_identity_1.removeProviderIdentity)(auth, orphan);
|
|
91
|
+
}
|
|
92
|
+
// The verification first: if the identity then fails to appear, a verified
|
|
93
|
+
// row of an address the code login proved is all that remains.
|
|
94
|
+
await confirmAddress(auth, authIdentityId, normalized);
|
|
95
|
+
try {
|
|
96
|
+
await auth.createProviderIdentities({
|
|
97
|
+
auth_identity_id: authIdentityId,
|
|
98
|
+
provider: auth_methods_1.EMAIL_PASSWORD_AUTH_PROVIDER,
|
|
99
|
+
entity_id: normalized,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (!(0, transaction_1.isUniqueConstraintViolation)(error)) {
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const created = (await (0, login_code_accounts_1.findPasswordIdentities)(sql, normalized)).find(({ entityId }) => entityId === normalized);
|
|
108
|
+
return created?.customerId ? normalized : null;
|
|
109
|
+
};
|
|
110
|
+
exports.prepareCodeAccountPasswordReset = prepareCodeAccountPasswordReset;
|
|
111
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlcGFyZS1jb2RlLWFjY291bnQtcGFzc3dvcmQtcmVzZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvY3VzdG9tZXItYXV0aC9hcHBsaWNhdGlvbi9wcmVwYXJlLWNvZGUtYWNjb3VudC1wYXNzd29yZC1yZXNldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSxxREFBOEU7QUFDOUUsMkdBR2dGO0FBRWhGLDhEQUEwRTtBQUMxRSx5REFBcUU7QUFDckUsZ0VBRytCO0FBRS9CLDBFQUFvRTtBQUVwRSxpRkFBaUY7QUFDakYsTUFBTSwwQkFBMEIsR0FBRyxXQUFXLENBQUE7QUF1QjlDOzs7Ozs7R0FNRztBQUNILE1BQU0sY0FBYyxHQUFHLEtBQUssRUFDMUIsSUFBNEIsRUFDNUIsY0FBc0IsRUFDdEIsUUFBZ0IsRUFDRCxFQUFFO0lBQ2pCLE1BQU0sR0FBRyxHQUFHLElBQUksSUFBSSxFQUFFLENBQUE7SUFDdEIsTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLE1BQU0sSUFBSSxDQUFDLHFCQUFxQixDQUNqRCxFQUFFLGdCQUFnQixFQUFFLGNBQWMsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxPQUFPLEVBQUUsRUFDL0UsRUFBRSxNQUFNLEVBQUUsQ0FBQyxJQUFJLEVBQUUsYUFBYSxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUMzQyxDQUFBO0lBQ0QsSUFBSSxRQUFRLEVBQUUsRUFBRSxFQUFFLENBQUM7UUFDakIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUMxQixNQUFNLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxFQUFFLEVBQUUsRUFBRSxRQUFRLENBQUMsRUFBRSxFQUFFLFdBQVcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFBO1FBQzNFLENBQUM7UUFDRCxPQUFNO0lBQ1IsQ0FBQztJQUNELElBQUksQ0FBQztRQUNILE1BQU0sSUFBSSxDQUFD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
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requestLoginCode = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const customer_auth_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/customer-auth");
|
|
6
|
+
const deliver_login_code_1 = require("../../email/application/deliver-login-code");
|
|
7
|
+
const enforcement_1 = require("../../public/enforcement");
|
|
8
|
+
const transaction_1 = require("../../shared/lib/transaction");
|
|
9
|
+
const account_linking_1 = require("../domain/account-linking");
|
|
10
|
+
const login_code_1 = require("../domain/login-code");
|
|
11
|
+
const login_code_accounts_1 = require("../login-code-accounts");
|
|
12
|
+
const login_code_state_1 = require("../login-code-state");
|
|
13
|
+
const plugin_features_1 = require("../plugin-features");
|
|
14
|
+
const remove_provider_identity_1 = require("../remove-provider-identity");
|
|
15
|
+
/**
|
|
16
|
+
* The key material of the code hashes: the shop's JWT secret, the same value
|
|
17
|
+
* the `email-otp` auth provider gets as its `hmacSecret` option (the shop
|
|
18
|
+
* preset sets both from `JWT_SECRET`). Both derive the HMAC key from it with
|
|
19
|
+
* `hashLoginCode`.
|
|
20
|
+
*/
|
|
21
|
+
const resolveJwtSecret = (container) => {
|
|
22
|
+
const secret = container.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE).projectConfig?.http?.jwtSecret;
|
|
23
|
+
if (typeof secret !== "string" || !secret.trim()) {
|
|
24
|
+
throw new Error("projectConfig.http.jwtSecret is not set");
|
|
25
|
+
}
|
|
26
|
+
return secret;
|
|
27
|
+
};
|
|
28
|
+
const logSafely = (container, level, message) => {
|
|
29
|
+
try {
|
|
30
|
+
container.resolve(utils_1.ContainerRegistrationKeys.LOGGER)[level](message);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// The caller runs detached: a broken logger must not become an
|
|
34
|
+
// unhandled rejection.
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const errorClass = (error) => error instanceof Error ? error.name : "unknown error";
|
|
38
|
+
/**
|
|
39
|
+
* The address's code-login identity, created when missing. A parallel first
|
|
40
|
+
* request loses the unique index (entity_id, provider) and reads the winner's
|
|
41
|
+
* row instead (create-or-get).
|
|
42
|
+
*/
|
|
43
|
+
const ensureProviderIdentity = async (sql, entityId, create) => {
|
|
44
|
+
try {
|
|
45
|
+
await create();
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (!(0, transaction_1.isUniqueConstraintViolation)(error)) {
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return await (0, login_code_accounts_1.findOwnLoginIdentity)(sql, entityId);
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Issues a login code for `email` and mails it. Never rejects and never
|
|
56
|
+
* reveals anything to the caller's client (the route answers 202 first): a
|
|
57
|
+
* failure is logged with a fixed message and the error class only, as the
|
|
58
|
+
* message of a database error can carry the address.
|
|
59
|
+
*
|
|
60
|
+
* 1. Find the address's own code-login identity and the customer accounts
|
|
61
|
+
* that registered it through `emailpass` (letter case ignored).
|
|
62
|
+
* 2. Decide where the code goes (`decideLoginCodeTarget`, the anti-takeover
|
|
63
|
+
* rule): use or create the code identity, link it to the account, or send
|
|
64
|
+
* nothing.
|
|
65
|
+
* 3. Store a new code with one conditional UPDATE that also enforces the
|
|
66
|
+
* resend interval and the daily budget of codes per address; no row means
|
|
67
|
+
* throttled or capped and no mail.
|
|
68
|
+
* 4. Mail the code (`auth.login_code`); it is never persisted or logged.
|
|
69
|
+
*/
|
|
70
|
+
const requestLoginCode = async (container, email) => {
|
|
71
|
+
try {
|
|
72
|
+
const entityId = (0, customer_auth_1.normalizeLoginEmail)(email);
|
|
73
|
+
const hmacSecret = resolveJwtSecret(container);
|
|
74
|
+
const auth = container.resolve(utils_1.Modules.AUTH);
|
|
75
|
+
const sql = container.resolve(utils_1.ContainerRegistrationKeys.PG_CONNECTION);
|
|
76
|
+
const [own, accounts, emailPasswordEnabled] = await Promise.all([
|
|
77
|
+
(0, login_code_accounts_1.findOwnLoginIdentity)(sql, entityId),
|
|
78
|
+
(0, login_code_accounts_1.findEmailPasswordAccounts)(sql, entityId),
|
|
79
|
+
(0, enforcement_1.isPluginFeatureEnabled)(container, plugin_features_1.customerAuthPluginFeatures, customer_auth_1.CUSTOMER_AUTH_FEATURES.emailPassword),
|
|
80
|
+
]);
|
|
81
|
+
const decision = (0, account_linking_1.decideLoginCodeTarget)({
|
|
82
|
+
emailPasswordEnabled,
|
|
83
|
+
normalizedEmail: entityId,
|
|
84
|
+
own,
|
|
85
|
+
accounts,
|
|
86
|
+
});
|
|
87
|
+
if (decision.action === "ambiguous") {
|
|
88
|
+
logSafely(container, "warn", "Login code not sent: more than one customer account matches the address (they differ only in letter case).");
|
|
89
|
+
return { status: "ambiguous" };
|
|
90
|
+
}
|
|
91
|
+
if (decision.action === "blocked") {
|
|
92
|
+
logSafely(container, "info", "Login code not sent: the address belongs to an account that may not be linked (address not confirmed or different from the customer's).");
|
|
93
|
+
return { status: "blocked" };
|
|
94
|
+
}
|
|
95
|
+
let target = own;
|
|
96
|
+
if (decision.action === "link") {
|
|
97
|
+
if (own) {
|
|
98
|
+
// A customer-less code identity (and its auth identity if it is then empty).
|
|
99
|
+
await (0, remove_provider_identity_1.removeProviderIdentity)(auth, own);
|
|
100
|
+
}
|
|
101
|
+
target = await ensureProviderIdentity(sql, entityId, () => auth.createProviderIdentities({
|
|
102
|
+
auth_identity_id: decision.authIdentityId,
|
|
103
|
+
provider: customer_auth_1.EMAIL_OTP_AUTH_PROVIDER,
|
|
104
|
+
entity_id: entityId,
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
else if (decision.action === "create") {
|
|
108
|
+
target = await ensureProviderIdentity(sql, entityId, () => auth.createAuthIdentities({
|
|
109
|
+
provider_identities: [
|
|
110
|
+
{ provider: customer_auth_1.EMAIL_OTP_AUTH_PROVIDER, entity_id: entityId },
|
|
111
|
+
],
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
if (!target) {
|
|
115
|
+
logSafely(container, "error", "Login code not sent: the code identity could not be created.");
|
|
116
|
+
return { status: "failed" };
|
|
117
|
+
}
|
|
118
|
+
const code = (0, login_code_1.generateLoginCode)();
|
|
119
|
+
const stored = await (0, login_code_state_1.storeLoginCode)(sql, target.providerIdentityId, (0, login_code_1.hashLoginCode)(hmacSecret, entityId, code));
|
|
120
|
+
if (stored === "capped") {
|
|
121
|
+
logSafely(container, "warn", "Login code not sent: an address used up its daily budget of codes.");
|
|
122
|
+
return { status: "capped" };
|
|
123
|
+
}
|
|
124
|
+
if (stored === "throttled") {
|
|
125
|
+
return { status: "throttled" };
|
|
126
|
+
}
|
|
127
|
+
const mail = await (0, deliver_login_code_1.sendLoginCodeMail)(container, {
|
|
128
|
+
code,
|
|
129
|
+
customerId: target.customerId,
|
|
130
|
+
expiresAt: new Date(Date.now() + customer_auth_1.LOGIN_CODE_TTL_MINUTES * 60_000),
|
|
131
|
+
to: entityId,
|
|
132
|
+
});
|
|
133
|
+
if (mail.status !== "sent") {
|
|
134
|
+
logSafely(container, "error", `Login code mail was not delivered (${mail.status}${mail.code ? `: ${mail.code}` : ""}).`);
|
|
135
|
+
return { status: "failed" };
|
|
136
|
+
}
|
|
137
|
+
return { status: "sent" };
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
logSafely(container, "error", `A login code could not be requested (${errorClass(error)}).`);
|
|
141
|
+
return { status: "failed" };
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
exports.requestLoginCode = requestLoginCode;
|
|
145
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVxdWVzdC1sb2dpbi1jb2RlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2N1c3RvbWVyLWF1dGgvYXBwbGljYXRpb24vcmVxdWVzdC1sb2dpbi1jb2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUtBLHFEQUdrQztBQUNsQywyR0FLZ0Y7QUFFaEYsbUZBQThFO0FBQzlFLDBEQUFpRTtBQUNqRSw4REFBMEU7QUFDMUUsK0RBR2tDO0FBQ2xDLHFEQUc2QjtBQUM3QixnRUFHK0I7QUFDL0IsMERBQXVFO0FBQ3ZFLHdEQUErRDtBQUMvRCwwRUFBb0U7QUFhcEU7Ozs7O0dBS0c7QUFDSCxNQUFNLGdCQUFnQixHQUFHLENBQUMsU0FBMEIsRUFBVSxFQUFFO0lBQzlELE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBRTdCLGlDQUF5QixDQUFDLGFBQWEsQ0FBQyxDQUFDLGFBQWEsRUFBRSxJQUFJLEVBQUUsU0FBUyxDQUFBO0lBQzFFLElBQUksT0FBTyxNQUFNLEtBQUssUUFBUSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxFQUFFLENBQUM7UUFDakQsTUFBTSxJQUFJLEtBQUssQ0FBQyx5Q0FBeUMsQ0FBQyxDQUFBO0lBQzVELENBQUM7SUFDRCxPQUFPLE1BQU0sQ0FBQTtBQUNmLENBQUMsQ0FBQTtBQUVELE1BQU0sU0FBUyxHQUFHLENBQ2hCLFNBQTBCLEVBQzFCLEtBQWdDLEVBQ2hDLE9BQWUsRUFDVCxFQUFFO0lBQ1IsSUFBSSxDQUFDO1FBQ0gsU0FBUyxDQUFDLE9BQU8sQ0FBUyxpQ0FBeUIsQ0FBQyxNQUFNLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUM3RSxDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsK0RBQStEO1FBQy9ELHVCQUF1QjtJQUN6QixDQUFDO0FBQ0gsQ0FBQyxDQUFBO0FBRUQsTUFBTSxVQUFVLEdBQUcsQ0FBQyxLQUFjLEVBQVUs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
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setCustomerAuthMethodsForTests = exports.customerAuthMethods = exports.startCustomerAuthMethodsSnapshot = exports.stopCustomerAuthMethodsSnapshot = exports.refreshCustomerAuthMethodsSnapshot = void 0;
|
|
4
|
+
const refreshing_snapshot_1 = require("../shared/lib/refreshing-snapshot");
|
|
5
|
+
const auth_methods_1 = require("./domain/auth-methods");
|
|
6
|
+
const plugin_features_1 = require("./plugin-features");
|
|
7
|
+
/**
|
|
8
|
+
* In-memory copy of the login methods customers may use, for the one place
|
|
9
|
+
* that cannot await them: Medusa reads
|
|
10
|
+
* `projectConfig.http.authMethodsPerActor.customer` synchronously on every
|
|
11
|
+
* request to an `/auth/customer/<provider>` route. The shop preset puts
|
|
12
|
+
* `customerAuthMethods()` behind a getter there, so a switch in the Admin
|
|
13
|
+
* takes effect without a restart. Loading, the 30 s refresh and the failure
|
|
14
|
+
* behavior are those of `createRefreshingSnapshot`.
|
|
15
|
+
*
|
|
16
|
+
* Start and fallback value are the descriptor defaults (`["emailpass"]`),
|
|
17
|
+
* never an empty list: an empty allowlist blocks every customer login, so a
|
|
18
|
+
* failed read must not produce one.
|
|
19
|
+
*/
|
|
20
|
+
const snapshot = (0, refreshing_snapshot_1.createRefreshingSnapshot)({
|
|
21
|
+
key: "@hartl-services/medusa-base/customer-auth-methods-snapshot",
|
|
22
|
+
initial: Object.freeze((0, auth_methods_1.defaultCustomerAuthMethods)(plugin_features_1.customerAuthPluginFeatures)),
|
|
23
|
+
describe: (methods) => `Customer login methods could not be read, keeping [${methods.join(", ")}]`,
|
|
24
|
+
});
|
|
25
|
+
/** Reads the switches once; never rejects. Concurrent calls share one read. */
|
|
26
|
+
const refreshCustomerAuthMethodsSnapshot = () => snapshot.refresh();
|
|
27
|
+
exports.refreshCustomerAuthMethodsSnapshot = refreshCustomerAuthMethodsSnapshot;
|
|
28
|
+
/** Stops the periodic refresh (application shutdown). The value stays. */
|
|
29
|
+
const stopCustomerAuthMethodsSnapshot = () => snapshot.stop();
|
|
30
|
+
exports.stopCustomerAuthMethodsSnapshot = stopCustomerAuthMethodsSnapshot;
|
|
31
|
+
/**
|
|
32
|
+
* Registers the reader, loads the switches once and keeps them fresh every
|
|
33
|
+
* 30 s (application start). `read` returns the effective feature map of
|
|
34
|
+
* `customer_auth`; `warn` is the container logger.
|
|
35
|
+
*/
|
|
36
|
+
const startCustomerAuthMethodsSnapshot = (input) => snapshot.start({
|
|
37
|
+
read: async () => Object.freeze((0, auth_methods_1.deriveCustomerAuthMethods)(await input.read())),
|
|
38
|
+
warn: input.warn,
|
|
39
|
+
});
|
|
40
|
+
exports.startCustomerAuthMethodsSnapshot = startCustomerAuthMethodsSnapshot;
|
|
41
|
+
/**
|
|
42
|
+
* The value of `authMethodsPerActor.customer`: the provider ids a customer
|
|
43
|
+
* may use. Synchronous; starts a background refresh when the copy is stale.
|
|
44
|
+
* A fresh array per call.
|
|
45
|
+
*/
|
|
46
|
+
const customerAuthMethods = () => [...snapshot.current()];
|
|
47
|
+
exports.customerAuthMethods = customerAuthMethods;
|
|
48
|
+
/**
|
|
49
|
+
* Test hook: sets the methods and counts as just refreshed, so they hold for
|
|
50
|
+
* 30 s without a reader; `null` forgets reader and value (back to the
|
|
51
|
+
* defaults).
|
|
52
|
+
*/
|
|
53
|
+
const setCustomerAuthMethodsForTests = (methods) => snapshot.setForTests(methods === null ? null : Object.freeze([...methods]));
|
|
54
|
+
exports.setCustomerAuthMethodsForTests = setCustomerAuthMethodsForTests;
|
|
55
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItYXV0aC1tZXRob2RzLXNuYXBzaG90LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2N1c3RvbWVyLWF1dGgvY3VzdG9tZXItYXV0aC1tZXRob2RzLXNuYXBzaG90LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDJFQUE0RTtBQUU1RSx3REFHOEI7QUFDOUIsdURBQThEO0FBRTlEOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILE1BQU0sUUFBUSxHQUFHLElBQUEsOENBQXdCLEVBQW9CO0lBQzNELEdBQUcsRUFBRSw0REFBNEQ7SUFDakUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBQSx5Q0FBMEIsRUFBQyw0Q0FBMEIsQ0FBQyxDQUFDO0lBQzlFLFFBQVEsRUFBRSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQ3BCLHNEQUFzRCxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHO0NBQzlFLENBQUMsQ0FBQTtBQUVGLCtFQUErRTtBQUN4RSxNQUFNLGtDQUFrQyxHQUFHLEdBQWtCLEVBQUUsQ0FDcEUsUUFBUSxDQUFDLE9BQU8sRUFBRSxDQUFBO0FBRFAsUUFBQSxrQ0FBa0Msc0NBQzNCO0FBRXBCLDBFQUEwRTtBQUNuRSxNQUFNLCtCQUErQixHQUFHLEdBQVMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQTtBQUE3RCxRQUFBLCtCQUErQixtQ0FBOEI7QUFFMUU7Ozs7R0FJRztBQUNJLE1BQU0sZ0NBQWdDLEdBQUcsQ0FDOUMsS0FHRSxFQUNhLEVBQUUsQ0FDakIsUUFBUSxDQUFDLEtBQUssQ0FBQztJQUNiLElBQUksRUFBRSxLQUFLLElBQUksRUFBRSxDQUNmLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBQSx3Q0FBeUIsRUFBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzlELElBQUksRUFBRSxLQUFLLENBQUMsSUFBSTtDQUNqQixDQUFDLENBQUE7QUFWUyxRQUFBLGdDQUFnQyxvQ0FVekM7QUFFSjs7OztHQUlHO0FBQ0ksTUFBTSxtQkFBbUIsR0FBRyxHQUFhLEVBQUUsQ0FBQyxDQUFDLEdBQUcsUUFBUSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUE7QUFBN0QsUUFBQSxtQkFBbUIsdUJBQTBDO0FBRTFFOzs7O0dBSUc7QUFDSSxNQUFNLDhCQUE4QixHQUFHLENBQzVDLE9BQWlDLEVBQzNCLEVBQUUsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLE9BQU8sS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFBO0FBRnpFLFFBQUEsOEJBQThCLGtDQUUyQyJ9
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveCustomerPasswordPolicy = exports.toCustomerPasswordPolicy = void 0;
|
|
4
|
+
const password_policy_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/password-policy");
|
|
5
|
+
const settings_1 = require("../public/settings");
|
|
6
|
+
const plugin_features_1 = require("./plugin-features");
|
|
7
|
+
/**
|
|
8
|
+
* Pure part: turns the resolved `customer_auth` settings into the policy.
|
|
9
|
+
* A missing or mistyped value (or a length outside the allowed range) falls
|
|
10
|
+
* back to the default for that rule, so a broken value never blocks a
|
|
11
|
+
* registration with an unusable rule.
|
|
12
|
+
*/
|
|
13
|
+
const toCustomerPasswordPolicy = (values) => {
|
|
14
|
+
const { min, max } = password_policy_1.CUSTOMER_PASSWORD_MIN_LENGTH_RANGE;
|
|
15
|
+
const length = values.password_min_length;
|
|
16
|
+
const flag = (key, fallback) => typeof values[key] === "boolean" ? values[key] : fallback;
|
|
17
|
+
return {
|
|
18
|
+
min_length: typeof length === "number" &&
|
|
19
|
+
Number.isInteger(length) &&
|
|
20
|
+
length >= min &&
|
|
21
|
+
length <= max
|
|
22
|
+
? length
|
|
23
|
+
: password_policy_1.DEFAULT_CUSTOMER_PASSWORD_POLICY.min_length,
|
|
24
|
+
require_mixed_case: flag("password_require_mixed_case", password_policy_1.DEFAULT_CUSTOMER_PASSWORD_POLICY.require_mixed_case),
|
|
25
|
+
require_digit: flag("password_require_digit", password_policy_1.DEFAULT_CUSTOMER_PASSWORD_POLICY.require_digit),
|
|
26
|
+
require_symbol: flag("password_require_symbol", password_policy_1.DEFAULT_CUSTOMER_PASSWORD_POLICY.require_symbol),
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
exports.toCustomerPasswordPolicy = toCustomerPasswordPolicy;
|
|
30
|
+
/**
|
|
31
|
+
* The password rules customers currently have to meet: stored in the Admin >
|
|
32
|
+
* declared default, per rule. Read failures propagate; the callers decide
|
|
33
|
+
* what to do (the middleware falls back to the default policy).
|
|
34
|
+
*/
|
|
35
|
+
const resolveCustomerPasswordPolicy = async (container) => {
|
|
36
|
+
const { values } = await (0, settings_1.getPluginSettings)(container, plugin_features_1.customerAuthPluginFeatures);
|
|
37
|
+
return (0, exports.toCustomerPasswordPolicy)(values);
|
|
38
|
+
};
|
|
39
|
+
exports.resolveCustomerPasswordPolicy = resolveCustomerPasswordPolicy;
|
|
40
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItcGFzc3dvcmQtcG9saWN5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2N1c3RvbWVyLWF1dGgvY3VzdG9tZXItcGFzc3dvcmQtcG9saWN5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLCtHQUlrRjtBQUVsRixpREFBc0Q7QUFDdEQsdURBQThEO0FBRTlEOzs7OztHQUtHO0FBQ0ksTUFBTSx3QkFBd0IsR0FBRyxDQUN0QyxNQUF5QyxFQUNqQixFQUFFO0lBQzFCLE1BQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsb0RBQWtDLENBQUE7SUFDdkQsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLG1CQUFtQixDQUFBO0lBQ3pDLE1BQU0sSUFBSSxHQUFHLENBQUMsR0FBVyxFQUFFLFFBQWlCLEVBQVcsRUFBRSxDQUN2RCxPQUFPLE1BQU0sQ0FBQyxHQUFHLENBQUMsS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFFLE1BQU0sQ0FBQyxHQUFHLENBQWEsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFBO0lBRXhFLE9BQU87UUFDTCxVQUFVLEVBQ1IsT0FBTyxNQUFNLEtBQUssUUFBUTtZQUMxQixNQUFNLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQztZQUN4QixNQUFNLElBQUksR0FBRztZQUNiLE1BQU0sSUFBSSxHQUFHO1lBQ1gsQ0FBQyxDQUFDLE1BQU07WUFDUixDQUFDLENBQUMsa0RBQWdDLENBQUMsVUFBVTtRQUNqRCxrQkFBa0IsRUFBRSxJQUFJLENBQ3RCLDZCQUE2QixFQUM3QixrREFBZ0MsQ0FBQyxrQkFBa0IsQ0FDcEQ7UUFDRCxhQUFhLEVBQUUsSUFBSSxDQUNqQix3QkFBd0IsRUFDeEIsa0RBQWdDLENBQUMsYUFBYSxDQUMvQztRQUNELGNBQWMsRUFBRSxJQUFJLENBQ2xCLHlCQUF5QixFQUN6QixrREFBZ0MsQ0FBQyxjQUFjLENBQ2hEO0tBQ0YsQ0FBQTtBQUNILENBQUMsQ0FBQTtBQTdCWSxRQUFBLHdCQUF3Qiw0QkE2QnBDO0FBRUQ7Ozs7R0FJRztBQUNJLE1BQU0sNkJBQTZCLEdBQUcsS0FBSyxFQUNoRCxTQUEwQixFQUNPLEVBQUU7SUFDbkMsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU0sSUFBQSw0QkFBaUIsRUFDeEMsU0FBUyxFQUNULDRDQUEwQixDQUMzQixDQUFBO0lBQ0QsT0FBTyxJQUFBLGdDQUF3QixFQUFDLE1BQU0sQ0FBQyxDQUFBO0FBQ3pDLENBQUMsQ0FBQTtBQVJZLFFBQUEsNkJBQTZCLGlDQVF6QyJ9
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decideLoginCodeTarget = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Where a login code for an address goes. The rule that prevents account
|
|
6
|
+
* takeover: an existing `emailpass` account is linked only when password
|
|
7
|
+
* login is off in this shop, or when that address was confirmed through
|
|
8
|
+
* Medusa's auth verification, and only when its customer record has that very
|
|
9
|
+
* address. Otherwise anyone could register an account with somebody else's
|
|
10
|
+
* address and password, and the owner of the address would land in that
|
|
11
|
+
* account the first time they ask for a code. A customer record is never
|
|
12
|
+
* matched by `customer.email` alone, and guest customers are never touched.
|
|
13
|
+
*
|
|
14
|
+
* - An `email-otp` identity that already belongs to a customer is used as is.
|
|
15
|
+
* - Otherwise accounts are the candidates. First those that must not be
|
|
16
|
+
* linked are dropped (unconfirmed while password login is on, customer
|
|
17
|
+
* address differs); a candidate that cannot be linked never makes the
|
|
18
|
+
* address ambiguous next to a linkable one. None left (but some existed)
|
|
19
|
+
* -> send nothing: an unconfirmed password account somebody else
|
|
20
|
+
* registered for the address then blocks the code login while password
|
|
21
|
+
* login is on, until the owner resets that password and confirms the
|
|
22
|
+
* address (or the shop switches password login off). Of the rest, confirmed
|
|
23
|
+
* ones are preferred, then the one whose stored address equals the
|
|
24
|
+
* requested spelling exactly; if more than one is still left -> send
|
|
25
|
+
* nothing. No account at all -> use the customer-less own identity or
|
|
26
|
+
* create one.
|
|
27
|
+
*/
|
|
28
|
+
const decideLoginCodeTarget = (input) => {
|
|
29
|
+
const { emailPasswordEnabled, normalizedEmail, own, accounts } = input;
|
|
30
|
+
if (own?.customerId) {
|
|
31
|
+
return { action: "use_own" };
|
|
32
|
+
}
|
|
33
|
+
if (!accounts.length) {
|
|
34
|
+
return own ? { action: "use_own" } : { action: "create" };
|
|
35
|
+
}
|
|
36
|
+
let remaining = accounts.filter((account) => account.customerEmailMatches && (!emailPasswordEnabled || account.confirmed));
|
|
37
|
+
if (!remaining.length) {
|
|
38
|
+
return { action: "blocked" };
|
|
39
|
+
}
|
|
40
|
+
if (remaining.length > 1) {
|
|
41
|
+
const confirmed = remaining.filter((account) => account.confirmed);
|
|
42
|
+
remaining = confirmed.length ? confirmed : remaining;
|
|
43
|
+
}
|
|
44
|
+
if (remaining.length > 1) {
|
|
45
|
+
const exact = remaining.filter((account) => account.entityId === normalizedEmail);
|
|
46
|
+
remaining = exact.length ? exact : remaining;
|
|
47
|
+
}
|
|
48
|
+
return remaining.length === 1
|
|
49
|
+
? { action: "link", authIdentityId: remaining[0].authIdentityId }
|
|
50
|
+
: { action: "ambiguous" };
|
|
51
|
+
};
|
|
52
|
+
exports.decideLoginCodeTarget = decideLoginCodeTarget;
|
|
53
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNjb3VudC1saW5raW5nLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2N1c3RvbWVyLWF1dGgvZG9tYWluL2FjY291bnQtbGlua2luZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFxQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBdUJHO0FBQ0ksTUFBTSxxQkFBcUIsR0FBRyxDQUNuQyxLQU1FLEVBQ3VCLEVBQUU7SUFDM0IsTUFBTSxFQUFFLG9CQUFvQixFQUFFLGVBQWUsRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLEdBQUcsS0FBSyxDQUFBO0lBQ3RFLElBQUksR0FBRyxFQUFFLFVBQVUsRUFBRSxDQUFDO1FBQ3BCLE9BQU8sRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLENBQUE7SUFDOUIsQ0FBQztJQUNELElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDckIsT0FBTyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsQ0FBQTtJQUMzRCxDQUFDO0lBRUQsSUFBSSxTQUFTLEdBQUcsUUFBUSxDQUFDLE1BQU0sQ0FDN0IsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUNWLE9BQU8sQ0FBQyxvQkFBb0IsSUFBSSxDQUFDLENBQUMsb0JBQW9CLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUMvRSxDQUFBO0lBQ0QsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUN0QixPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxDQUFBO0lBQzlCLENBQUM7SUFDRCxJQUFJLFNBQVMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDekIsTUFBTSxTQUFTLEdBQUcsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFBO1FBQ2xFLFNBQVMsR0FBRyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQTtJQUN0RCxDQUFDO0lBQ0QsSUFBSSxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3pCLE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxNQUFNLENBQzVCLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsUUFBUSxLQUFLLGVBQWUsQ0FDbEQsQ0FBQTtRQUNELFNBQVMsR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQTtJQUM5QyxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUM7UUFDM0IsQ0FBQyxDQUFDLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxjQUFjLEVBQUUsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLGNBQWMsRUFBRTtRQUNqRSxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLENBQUE7QUFDN0IsQ0FBQyxDQUFBO0FBckNZLFFBQUEscUJBQXFCLHlCQXFDakMifQ==
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultCustomerAuthMethods = exports.deriveCustomerAuthMethods = exports.EMAIL_PASSWORD_AUTH_PROVIDER = void 0;
|
|
4
|
+
const customer_auth_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/customer-auth");
|
|
5
|
+
/** Medusa's built-in e-mail + password provider. */
|
|
6
|
+
exports.EMAIL_PASSWORD_AUTH_PROVIDER = "emailpass";
|
|
7
|
+
/**
|
|
8
|
+
* The auth providers a customer may use for the given effective switches:
|
|
9
|
+
* `emailpass` while `email_password` is on, `email-otp` while `email_otp` is
|
|
10
|
+
* on. Empty only when both are off, which blocks every customer login (Medusa
|
|
11
|
+
* treats an empty allowlist as "nothing allowed"). A fresh array per call.
|
|
12
|
+
*/
|
|
13
|
+
const deriveCustomerAuthMethods = (features) => [
|
|
14
|
+
...(features[customer_auth_1.CUSTOMER_AUTH_FEATURES.emailPassword] === true
|
|
15
|
+
? [exports.EMAIL_PASSWORD_AUTH_PROVIDER]
|
|
16
|
+
: []),
|
|
17
|
+
...(features[customer_auth_1.CUSTOMER_AUTH_FEATURES.emailOtp] === true
|
|
18
|
+
? [customer_auth_1.EMAIL_OTP_AUTH_PROVIDER]
|
|
19
|
+
: []),
|
|
20
|
+
];
|
|
21
|
+
exports.deriveCustomerAuthMethods = deriveCustomerAuthMethods;
|
|
22
|
+
/**
|
|
23
|
+
* The methods of a shop whose switches were never stored: the defaults of
|
|
24
|
+
* the descriptor. This is the start value and the fallback of the snapshot,
|
|
25
|
+
* so an unreadable database never locks every customer out.
|
|
26
|
+
*/
|
|
27
|
+
const defaultCustomerAuthMethods = (descriptor) => (0, exports.deriveCustomerAuthMethods)(Object.fromEntries(descriptor.features.map((feature) => [
|
|
28
|
+
feature.key,
|
|
29
|
+
feature.default_enabled,
|
|
30
|
+
])));
|
|
31
|
+
exports.defaultCustomerAuthMethods = defaultCustomerAuthMethods;
|
|
32
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aC1tZXRob2RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2N1c3RvbWVyLWF1dGgvZG9tYWluL2F1dGgtbWV0aG9kcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwyR0FHZ0Y7QUFJaEYsb0RBQW9EO0FBQ3ZDLFFBQUEsNEJBQTRCLEdBQUcsV0FBVyxDQUFBO0FBRXZEOzs7OztHQUtHO0FBQ0ksTUFBTSx5QkFBeUIsR0FBRyxDQUN2QyxRQUF1RCxFQUM3QyxFQUFFLENBQUM7SUFDYixHQUFHLENBQUMsUUFBUSxDQUFDLHNDQUFzQixDQUFDLGFBQWEsQ0FBQyxLQUFLLElBQUk7UUFDekQsQ0FBQyxDQUFDLENBQUMsb0NBQTRCLENBQUM7UUFDaEMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUNQLEdBQUcsQ0FBQyxRQUFRLENBQUMsc0NBQXNCLENBQUMsUUFBUSxDQUFDLEtBQUssSUFBSTtRQUNwRCxDQUFDLENBQUMsQ0FBQyx1Q0FBdUIsQ0FBQztRQUMzQixDQUFDLENBQUMsRUFBRSxDQUFDO0NBQ1IsQ0FBQTtBQVRZLFFBQUEseUJBQXlCLDZCQVNyQztBQUVEOzs7O0dBSUc7QUFDSSxNQUFNLDBCQUEwQixHQUFHLENBQ3hDLFVBQW1DLEVBQ3pCLEVBQUUsQ0FDWixJQUFBLGlDQUF5QixFQUN2QixNQUFNLENBQUMsV0FBVyxDQUNoQixVQUFVLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUM7SUFDbkMsT0FBTyxDQUFDLEdBQUc7SUFDWCxPQUFPLENBQUMsZUFBZTtDQUN4QixDQUFDLENBQ0gsQ0FDRixDQUFBO0FBVlUsUUFBQSwwQkFBMEIsOEJBVXBDIn0=
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loginCodeHashesEqual = exports.hashLoginCode = exports.deriveLoginCodeKey = exports.generateLoginCode = exports.LOGIN_CODE_REJECTED_MESSAGE = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const customer_auth_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/customer-auth");
|
|
6
|
+
/** One message for a wrong, expired, used-up or unknown code: never says which. */
|
|
7
|
+
exports.LOGIN_CODE_REJECTED_MESSAGE = "Der Code ist falsch oder abgelaufen. Fordere bei Bedarf einen neuen Code an.";
|
|
8
|
+
/** HKDF `info` label of the login code key. */
|
|
9
|
+
const LOGIN_CODE_KEY_INFO = "email-otp-login-code";
|
|
10
|
+
/** A uniformly random code of exactly `LOGIN_CODE_LENGTH` digits, leading zeros kept. */
|
|
11
|
+
const generateLoginCode = () => String((0, node_crypto_1.randomInt)(0, 10 ** customer_auth_1.LOGIN_CODE_LENGTH)).padStart(customer_auth_1.LOGIN_CODE_LENGTH, "0");
|
|
12
|
+
exports.generateLoginCode = generateLoginCode;
|
|
13
|
+
/**
|
|
14
|
+
* The HMAC key of the login code hashes, derived from the shop's JWT secret
|
|
15
|
+
* (HKDF-SHA256, fixed purpose label) so the secret itself never keys the
|
|
16
|
+
* hashes and a code hash is of no use anywhere else. Both sides, the request
|
|
17
|
+
* use case and the `email-otp` provider, call this one function with the same
|
|
18
|
+
* secret (`projectConfig.http.jwtSecret`).
|
|
19
|
+
*/
|
|
20
|
+
const deriveLoginCodeKey = (secret) => Buffer.from((0, node_crypto_1.hkdfSync)("sha256", secret, "", LOGIN_CODE_KEY_INFO, 32));
|
|
21
|
+
exports.deriveLoginCodeKey = deriveLoginCodeKey;
|
|
22
|
+
/**
|
|
23
|
+
* What is stored instead of the code: hex HMAC-SHA256 over the address and
|
|
24
|
+
* the code, so a copy of the row neither reveals the code (the space is only
|
|
25
|
+
* a million values, a plain hash would be brute-forced at once) nor works for
|
|
26
|
+
* another address. `secret` is the raw JWT secret; the key is derived here.
|
|
27
|
+
*/
|
|
28
|
+
const hashLoginCode = (secret, entityId, code) => (0, node_crypto_1.createHmac)("sha256", (0, exports.deriveLoginCodeKey)(secret))
|
|
29
|
+
.update(`email-otp:v1:${entityId}:${code}`)
|
|
30
|
+
.digest("hex");
|
|
31
|
+
exports.hashLoginCode = hashLoginCode;
|
|
32
|
+
/** Constant-time comparison of two hex digests; false for other lengths. */
|
|
33
|
+
const loginCodeHashesEqual = (left, right) => {
|
|
34
|
+
const a = Buffer.from(left, "utf8");
|
|
35
|
+
const b = Buffer.from(right, "utf8");
|
|
36
|
+
return a.length === b.length && (0, node_crypto_1.timingSafeEqual)(a, b);
|
|
37
|
+
};
|
|
38
|
+
exports.loginCodeHashesEqual = loginCodeHashesEqual;
|
|
39
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9naW4tY29kZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9jdXN0b21lci1hdXRoL2RvbWFpbi9sb2dpbi1jb2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDZDQUE4RTtBQUU5RSwyR0FBMkc7QUFFM0csbUZBQW1GO0FBQ3RFLFFBQUEsMkJBQTJCLEdBQ3RDLDhFQUE4RSxDQUFBO0FBRWhGLCtDQUErQztBQUMvQyxNQUFNLG1CQUFtQixHQUFHLHNCQUFzQixDQUFBO0FBRWxELHlGQUF5RjtBQUNsRixNQUFNLGlCQUFpQixHQUFHLEdBQVcsRUFBRSxDQUM1QyxNQUFNLENBQUMsSUFBQSx1QkFBUyxFQUFDLENBQUMsRUFBRSxFQUFFLElBQUksaUNBQWlCLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxpQ0FBaUIsRUFBRSxHQUFHLENBQUMsQ0FBQTtBQURuRSxRQUFBLGlCQUFpQixxQkFDa0Q7QUFFaEY7Ozs7OztHQU1HO0FBQ0ksTUFBTSxrQkFBa0IsR0FBRyxDQUFDLE1BQWMsRUFBVSxFQUFFLENBQzNELE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBQSxzQkFBUSxFQUFDLFFBQVEsRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLG1CQUFtQixFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUE7QUFEekQsUUFBQSxrQkFBa0Isc0JBQ3VDO0FBRXRFOzs7OztHQUtHO0FBQ0ksTUFBTSxhQUFhLEdBQUcsQ0FDM0IsTUFBYyxFQUNkLFFBQWdCLEVBQ2hCLElBQVksRUFDSixFQUFFLENBQ1YsSUFBQSx3QkFBVSxFQUFDLFFBQVEsRUFBRSxJQUFBLDBCQUFrQixFQUFDLE1BQU0sQ0FBQyxDQUFDO0tBQzdDLE1BQU0sQ0FBQyxnQkFBZ0IsUUFBUSxJQUFJLElBQUksRUFBRSxDQUFDO0tBQzFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQTtBQVBMLFFBQUEsYUFBYSxpQkFPUjtBQUVsQiw0RUFBNEU7QUFDckUsTUFBTSxvQkFBb0IsR0FBRyxDQUFDLElBQVksRUFBRSxLQUFhLEVBQVcsRUFBRTtJQUMzRSxNQUFNLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQTtJQUNuQyxNQUFNLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQTtJQUNwQyxPQUFPLENBQUMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDLE1BQU0sSUFBSSxJQUFBLDZCQUFlLEVBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFBO0FBQ3ZELENBQUMsQ0FBQTtBQUpZLFFBQUEsb0JBQW9CLHdCQUloQyJ9
|