@endora-commerce/mod-mfa 0.100.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/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/admin/index.d.ts +37 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +12 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/AdminSecuritySettings.d.ts +3 -0
- package/dist/admin/pages/AdminSecuritySettings.d.ts.map +1 -0
- package/dist/admin/pages/AdminSecuritySettings.js +71 -0
- package/dist/admin/pages/AdminSecuritySettings.js.map +1 -0
- package/dist/backend/entities/mfa-enrolment.entity.d.ts +33 -0
- package/dist/backend/entities/mfa-enrolment.entity.d.ts.map +1 -0
- package/dist/backend/entities/mfa-enrolment.entity.js +95 -0
- package/dist/backend/entities/mfa-enrolment.entity.js.map +1 -0
- package/dist/backend/entities/mfa-organization-policy.entity.d.ts +19 -0
- package/dist/backend/entities/mfa-organization-policy.entity.d.ts.map +1 -0
- package/dist/backend/entities/mfa-organization-policy.entity.js +60 -0
- package/dist/backend/entities/mfa-organization-policy.entity.js.map +1 -0
- package/dist/backend/entities/mfa-recovery-code.entity.d.ts +17 -0
- package/dist/backend/entities/mfa-recovery-code.entity.d.ts.map +1 -0
- package/dist/backend/entities/mfa-recovery-code.entity.js +54 -0
- package/dist/backend/entities/mfa-recovery-code.entity.js.map +1 -0
- package/dist/backend/entities/mfa-social-identity.entity.d.ts +24 -0
- package/dist/backend/entities/mfa-social-identity.entity.d.ts.map +1 -0
- package/dist/backend/entities/mfa-social-identity.entity.js +73 -0
- package/dist/backend/entities/mfa-social-identity.entity.js.map +1 -0
- package/dist/backend/index.d.ts +131 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +173 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +99 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +111 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +23 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +93 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.oauth.d.ts +26 -0
- package/dist/backend/routes.oauth.d.ts.map +1 -0
- package/dist/backend/routes.oauth.js +155 -0
- package/dist/backend/routes.oauth.js.map +1 -0
- package/dist/backend/routes.org.d.ts +21 -0
- package/dist/backend/routes.org.d.ts.map +1 -0
- package/dist/backend/routes.org.js +17 -0
- package/dist/backend/routes.org.js.map +1 -0
- package/dist/backend/routes.public.d.ts +21 -0
- package/dist/backend/routes.public.d.ts.map +1 -0
- package/dist/backend/routes.public.js +118 -0
- package/dist/backend/routes.public.js.map +1 -0
- package/dist/backend/routes.self-service.d.ts +34 -0
- package/dist/backend/routes.self-service.d.ts.map +1 -0
- package/dist/backend/routes.self-service.js +113 -0
- package/dist/backend/routes.self-service.js.map +1 -0
- package/dist/backend/services/account-identity.d.ts +71 -0
- package/dist/backend/services/account-identity.d.ts.map +1 -0
- package/dist/backend/services/account-identity.js +87 -0
- package/dist/backend/services/account-identity.js.map +1 -0
- package/dist/backend/services/challenge-store.d.ts +40 -0
- package/dist/backend/services/challenge-store.d.ts.map +1 -0
- package/dist/backend/services/challenge-store.js +90 -0
- package/dist/backend/services/challenge-store.js.map +1 -0
- package/dist/backend/services/mfa-enrolment-count.service.d.ts +26 -0
- package/dist/backend/services/mfa-enrolment-count.service.d.ts.map +1 -0
- package/dist/backend/services/mfa-enrolment-count.service.js +34 -0
- package/dist/backend/services/mfa-enrolment-count.service.js.map +1 -0
- package/dist/backend/services/mfa-enrolment-service.d.ts +54 -0
- package/dist/backend/services/mfa-enrolment-service.d.ts.map +1 -0
- package/dist/backend/services/mfa-enrolment-service.js +217 -0
- package/dist/backend/services/mfa-enrolment-service.js.map +1 -0
- package/dist/backend/services/mfa-enrolment-state.service.d.ts +27 -0
- package/dist/backend/services/mfa-enrolment-state.service.d.ts.map +1 -0
- package/dist/backend/services/mfa-enrolment-state.service.js +33 -0
- package/dist/backend/services/mfa-enrolment-state.service.js.map +1 -0
- package/dist/backend/services/mfa-login-service.d.ts +56 -0
- package/dist/backend/services/mfa-login-service.d.ts.map +1 -0
- package/dist/backend/services/mfa-login-service.js +89 -0
- package/dist/backend/services/mfa-login-service.js.map +1 -0
- package/dist/backend/services/mfa-org-policy-service.d.ts +16 -0
- package/dist/backend/services/mfa-org-policy-service.d.ts.map +1 -0
- package/dist/backend/services/mfa-org-policy-service.js +43 -0
- package/dist/backend/services/mfa-org-policy-service.js.map +1 -0
- package/dist/backend/services/mfa-policy-resolver.d.ts +47 -0
- package/dist/backend/services/mfa-policy-resolver.d.ts.map +1 -0
- package/dist/backend/services/mfa-policy-resolver.js +82 -0
- package/dist/backend/services/mfa-policy-resolver.js.map +1 -0
- package/dist/backend/services/oauth-provider-service.d.ts +65 -0
- package/dist/backend/services/oauth-provider-service.d.ts.map +1 -0
- package/dist/backend/services/oauth-provider-service.js +80 -0
- package/dist/backend/services/oauth-provider-service.js.map +1 -0
- package/dist/backend/services/recovery-codes.d.ts +10 -0
- package/dist/backend/services/recovery-codes.d.ts.map +1 -0
- package/dist/backend/services/recovery-codes.js +18 -0
- package/dist/backend/services/recovery-codes.js.map +1 -0
- package/dist/backend/services/secret-cipher.d.ts +16 -0
- package/dist/backend/services/secret-cipher.d.ts.map +1 -0
- package/dist/backend/services/secret-cipher.js +52 -0
- package/dist/backend/services/secret-cipher.js.map +1 -0
- package/dist/backend/services/social-identity-service.d.ts +40 -0
- package/dist/backend/services/social-identity-service.d.ts.map +1 -0
- package/dist/backend/services/social-identity-service.js +69 -0
- package/dist/backend/services/social-identity-service.js.map +1 -0
- package/dist/backend/services/social-link-service.d.ts +63 -0
- package/dist/backend/services/social-link-service.d.ts.map +1 -0
- package/dist/backend/services/social-link-service.js +121 -0
- package/dist/backend/services/social-link-service.js.map +1 -0
- package/dist/backend/services/totp.d.ts +11 -0
- package/dist/backend/services/totp.d.ts.map +1 -0
- package/dist/backend/services/totp.js +46 -0
- package/dist/backend/services/totp.js.map +1 -0
- package/dist/manifest.d.ts +184 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +264 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260611T140409_mfa_init.d.ts +22 -0
- package/dist/migrations/20260611T140409_mfa_init.d.ts.map +1 -0
- package/dist/migrations/20260611T140409_mfa_init.js +94 -0
- package/dist/migrations/20260611T140409_mfa_init.js.map +1 -0
- package/dist/migrations/index.d.ts +27 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +29 -0
- package/dist/migrations/index.js.map +1 -0
- package/i18n/en.json +14 -0
- package/i18n/pl.json +14 -0
- package/package.json +102 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
|
|
11
|
+
import { GlobalEntity } from '@endora-commerce/platform/tenancy';
|
|
12
|
+
import { randomUUID } from 'crypto';
|
|
13
|
+
/**
|
|
14
|
+
* Link between a platform account and an external provider identity
|
|
15
|
+
* (Google / Microsoft) — feature 042. Resolution is by the provider's
|
|
16
|
+
* verified primary email; the `providerSubject` (`sub` claim) is the stable
|
|
17
|
+
* provider-side id. Uniqueness constraints (declared in migration 067):
|
|
18
|
+
* - (provider, provider_subject): one platform account per IdP identity;
|
|
19
|
+
* - (subject_type, subject_id, provider): one link per provider per account.
|
|
20
|
+
*/
|
|
21
|
+
let MfaSocialIdentity = class MfaSocialIdentity {
|
|
22
|
+
[OptionalProps];
|
|
23
|
+
id = randomUUID();
|
|
24
|
+
subjectType;
|
|
25
|
+
subjectId;
|
|
26
|
+
/** `'google'` | `'microsoft'`. */
|
|
27
|
+
provider;
|
|
28
|
+
/** The IdP `sub` claim — stable provider-side identifier. */
|
|
29
|
+
providerSubject;
|
|
30
|
+
/** Verified email at link time (used for resolution). */
|
|
31
|
+
email;
|
|
32
|
+
linkedAt = new Date();
|
|
33
|
+
lastUsedAt;
|
|
34
|
+
};
|
|
35
|
+
__decorate([
|
|
36
|
+
PrimaryKey({ type: 'uuid' }),
|
|
37
|
+
__metadata("design:type", String)
|
|
38
|
+
], MfaSocialIdentity.prototype, "id", void 0);
|
|
39
|
+
__decorate([
|
|
40
|
+
Property({ type: 'string', length: 16 }),
|
|
41
|
+
__metadata("design:type", String)
|
|
42
|
+
], MfaSocialIdentity.prototype, "subjectType", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
Property({ type: 'uuid' }),
|
|
45
|
+
__metadata("design:type", String)
|
|
46
|
+
], MfaSocialIdentity.prototype, "subjectId", void 0);
|
|
47
|
+
__decorate([
|
|
48
|
+
Property({ type: 'string', length: 16 }),
|
|
49
|
+
__metadata("design:type", String)
|
|
50
|
+
], MfaSocialIdentity.prototype, "provider", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
Property({ type: 'string', length: 255 }),
|
|
53
|
+
__metadata("design:type", String)
|
|
54
|
+
], MfaSocialIdentity.prototype, "providerSubject", void 0);
|
|
55
|
+
__decorate([
|
|
56
|
+
Property({ type: 'string', length: 320 }),
|
|
57
|
+
Index(),
|
|
58
|
+
__metadata("design:type", String)
|
|
59
|
+
], MfaSocialIdentity.prototype, "email", void 0);
|
|
60
|
+
__decorate([
|
|
61
|
+
Property({ type: 'datetime', onCreate: () => new Date() }),
|
|
62
|
+
__metadata("design:type", Date)
|
|
63
|
+
], MfaSocialIdentity.prototype, "linkedAt", void 0);
|
|
64
|
+
__decorate([
|
|
65
|
+
Property({ type: 'datetime', nullable: true }),
|
|
66
|
+
__metadata("design:type", Object)
|
|
67
|
+
], MfaSocialIdentity.prototype, "lastUsedAt", void 0);
|
|
68
|
+
MfaSocialIdentity = __decorate([
|
|
69
|
+
GlobalEntity(),
|
|
70
|
+
Entity({ tableName: 'mfa_social_identities' })
|
|
71
|
+
], MfaSocialIdentity);
|
|
72
|
+
export { MfaSocialIdentity };
|
|
73
|
+
//# sourceMappingURL=mfa-social-identity.entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mfa-social-identity.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/mfa-social-identity.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;;;;;;GAOG;AAGI,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAC5B,CAAC,aAAa,CAAC,CAAoC;IAGnD,EAAE,GAAW,UAAU,EAAE,CAAC;IAG1B,WAAW,CAAU;IAGrB,SAAS,CAAU;IAEnB,kCAAkC;IAElC,QAAQ,CAAU;IAElB,6DAA6D;IAE7D,eAAe,CAAU;IAEzB,yDAAyD;IAGzD,KAAK,CAAU;IAGf,QAAQ,GAAS,IAAI,IAAI,EAAE,CAAC;IAG5B,UAAU,CAAe;CAC1B,CAAA;AA1BC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;6CACH;AAG1B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;sDACpB;AAGrB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;oDACR;AAInB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;mDACvB;AAIlB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;0DACjB;AAKzB;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IACzC,KAAK,EAAE;;gDACO;AAGf;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BACjD,IAAI;mDAAc;AAG5B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qDACtB;AA7Bd,iBAAiB;IAF7B,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,uBAAuB,EAAE,CAAC;GAClC,iBAAiB,CA8B7B"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { FastifyRequest } from 'fastify';
|
|
3
|
+
import type { Redis } from 'ioredis';
|
|
4
|
+
import type { AuthSessionPort, MfaEnrolmentCountPort, MfaEnrolmentStatePort, MfaLoginPort } from '@endora-commerce/contracts';
|
|
5
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
6
|
+
import type { CommandBus } from '@endora-commerce/platform/commands';
|
|
7
|
+
import type { ModuleContext } from '@endora-commerce/platform/kernel';
|
|
8
|
+
import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
|
|
9
|
+
import type { SettingsReader } from './services/mfa-policy-resolver.js';
|
|
10
|
+
import { type OAuthProviderPort } from './services/oauth-provider-service.js';
|
|
11
|
+
import type { SocialIdentityDeps } from './services/social-identity-service.js';
|
|
12
|
+
import { type MfaModuleHandle } from './plugin.js';
|
|
13
|
+
import { MfaEnrolment } from './entities/mfa-enrolment.entity.js';
|
|
14
|
+
import { MfaOrganizationPolicy } from './entities/mfa-organization-policy.entity.js';
|
|
15
|
+
import { MfaRecoveryCode } from './entities/mfa-recovery-code.entity.js';
|
|
16
|
+
import { MfaSocialIdentity } from './entities/mfa-social-identity.entity.js';
|
|
17
|
+
/**
|
|
18
|
+
* `mfa` — the widest option surface in the wave, and why it is three names
|
|
19
|
+
* rather than fifteen (feature 072, wave 1, T096;
|
|
20
|
+
* `specs/110-instance-repository/` T118c).
|
|
21
|
+
*
|
|
22
|
+
* `MfaModuleOptions` had twenty-four fields. Wave 1 grouped six of them into a
|
|
23
|
+
* single `MfaActorBridge` contribution on the reasoning that they were the
|
|
24
|
+
* *shape a composition gives an actor* and that the two roots genuinely
|
|
25
|
+
* disagreed about it — production reading `request.actor`, the harness
|
|
26
|
+
* `request.testActor`. **T118c re-derived the six and the premise held for none
|
|
27
|
+
* of them.**
|
|
28
|
+
*
|
|
29
|
+
* - `resolveCustomerActor` was a duplicate spelling of `customerActorResolver`,
|
|
30
|
+
* byte-identical to the platform's contribution in production and to the
|
|
31
|
+
* harness's in the harness. One question, two answers, agreeing by hand.
|
|
32
|
+
* - `resolveAdminActor` was `adminContextResolver` plus a `promoteAdminActor`
|
|
33
|
+
* call that every one of its call sites had already made: each sits behind
|
|
34
|
+
* `requireAdmin`, whose `auth` implementation promotes and then refuses a
|
|
35
|
+
* non-admin, so the bridge re-asked a question its own guard had answered.
|
|
36
|
+
* - `resolveOrgAdmin` was the actor resolver plus one
|
|
37
|
+
* `customerAccountReadPort.findById` and a role check.
|
|
38
|
+
* - `resolveOrganizationCustomerIds`, `resolveAccountEmail` and
|
|
39
|
+
* `verifyAccountPassword` were four identity ports and nothing else.
|
|
40
|
+
*
|
|
41
|
+
* So the module resolves them itself now: two contributed actor names it reads
|
|
42
|
+
* off the cradle like the eight other modules that read them, and four
|
|
43
|
+
* `lazyPort` reads over `admin_users`' and `customer_accounts`' published
|
|
44
|
+
* contracts — both already in this module's manifest `dependencies`, both
|
|
45
|
+
* `nonDeactivatable`, so no operator loses a control to the edge.
|
|
46
|
+
*
|
|
47
|
+
* **Two of the six were asserted by nothing**, which is what a bridge with an
|
|
48
|
+
* optional half buys: the harness omitted `resolveAccountEmail` and
|
|
49
|
+
* `verifyAccountPassword` and production supplied them, so under test an
|
|
50
|
+
* authenticator entry was labelled with a UUID instead of an e-mail address and
|
|
51
|
+
* the password branch of `reauthenticate` did not exist. Neither difference was
|
|
52
|
+
* reachable by any test in the tree. They are not optional any more — the
|
|
53
|
+
* module always answers — and `backend/test/integration/mfa/account-identity-wiring.test.ts`
|
|
54
|
+
* is where both are now asserted.
|
|
55
|
+
*
|
|
56
|
+
* The remaining three names are genuinely independent:
|
|
57
|
+
*
|
|
58
|
+
* - `mfaOauthProvider` — absent on a deployment with no social sign-in.
|
|
59
|
+
* - `mfaSocialAccountResolvers` — same, and separately omissible because a
|
|
60
|
+
* deployment can carry the provider without standalone registration.
|
|
61
|
+
* - `mfaDefaultChannelIdResolver` — defaults to `async () => null`, which is
|
|
62
|
+
* exactly the `?? null` both roots already spell at the call site.
|
|
63
|
+
*
|
|
64
|
+
* `requireCustomer` is resolved rather than passed, and it is **not** this
|
|
65
|
+
* module's to own: it is `auth`'s customer-side guard, which `auth` provides as
|
|
66
|
+
* a port beside `requireAdmin` since issue #43. Until then each root declared
|
|
67
|
+
* its own — `composition.ts` inline, the harness as `requireTestCustomer()` —
|
|
68
|
+
* and the two disagreed on which request property carried the actor. Unifying
|
|
69
|
+
* them was filed as its own task rather than folded in here, because it touched
|
|
70
|
+
* twenty-seven call sites that have nothing to do with MFA.
|
|
71
|
+
*
|
|
72
|
+
* The base URLs come from the environment, read here rather than threaded from
|
|
73
|
+
* a root — a packaged module reads its own configuration.
|
|
74
|
+
*/
|
|
75
|
+
/**
|
|
76
|
+
* The origins MFA stamps into the links it sends and the OAuth redirects it
|
|
77
|
+
* builds. Defaults to the environment, because a packaged module reads its own
|
|
78
|
+
* configuration; contributed by a composition that has its own answer.
|
|
79
|
+
*/
|
|
80
|
+
export interface MfaBaseUrls {
|
|
81
|
+
readonly backend: string | undefined;
|
|
82
|
+
readonly storefront: string | undefined;
|
|
83
|
+
readonly admin: string | undefined;
|
|
84
|
+
}
|
|
85
|
+
export interface MfaCradle {
|
|
86
|
+
readonly emFactory: () => EntityManager;
|
|
87
|
+
readonly redis: Redis;
|
|
88
|
+
readonly auditLogService: AuditPort;
|
|
89
|
+
readonly commandBus: CommandBus;
|
|
90
|
+
readonly authSessionPort: AuthSessionPort;
|
|
91
|
+
readonly settingsReadPort: SettingsReader;
|
|
92
|
+
readonly requireAdmin: RequireAdminFactory;
|
|
93
|
+
readonly requireCustomer: (request: FastifyRequest) => Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* T118c — the two actor names the composition already contributes for every
|
|
96
|
+
* other module that asks who is calling. Declared structurally, as the eight
|
|
97
|
+
* other consumers declare them, so this module names no composition root.
|
|
98
|
+
*/
|
|
99
|
+
readonly customerActorResolver: (request: FastifyRequest) => {
|
|
100
|
+
customerAccountId: string;
|
|
101
|
+
organizationId: string | null;
|
|
102
|
+
};
|
|
103
|
+
readonly adminContextResolver: (request: FastifyRequest) => {
|
|
104
|
+
adminUserId: string;
|
|
105
|
+
};
|
|
106
|
+
readonly mfaOauthProvider: OAuthProviderPort | undefined;
|
|
107
|
+
readonly mfaSocialAccountResolvers: SocialIdentityDeps | undefined;
|
|
108
|
+
readonly mfaDefaultChannelIdResolver: () => Promise<string | null>;
|
|
109
|
+
readonly mfaBaseUrls: MfaBaseUrls;
|
|
110
|
+
readonly mfa: {
|
|
111
|
+
handle: () => MfaModuleHandle;
|
|
112
|
+
plugin: unknown;
|
|
113
|
+
};
|
|
114
|
+
readonly mfaLoginPort: MfaLoginPort;
|
|
115
|
+
readonly mfaEnrolmentCountPort: MfaEnrolmentCountPort;
|
|
116
|
+
readonly mfaEnrolmentStatePort: MfaEnrolmentStatePort;
|
|
117
|
+
}
|
|
118
|
+
export declare function registerModule(ctx: ModuleContext): void;
|
|
119
|
+
/**
|
|
120
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
121
|
+
* array and **no named class export** (D-168).
|
|
122
|
+
*
|
|
123
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
124
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
125
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
126
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
127
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
128
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
129
|
+
*/
|
|
130
|
+
export declare const entities: (typeof MfaOrganizationPolicy | typeof MfaEnrolment | typeof MfaSocialIdentity | typeof MfaRecoveryCode)[];
|
|
131
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { lazyPort } from '@endora-commerce/platform/kernel';
|
|
2
|
+
import { OpenIdOAuthProvider, readOAuthConfigFromEnv, } from './services/oauth-provider-service.js';
|
|
3
|
+
import { createAccountEmailResolver, createAccountPasswordVerifier, createOrganizationAdminResolver, createOrganizationCustomerIdsResolver, } from './services/account-identity.js';
|
|
4
|
+
import { MfaEnrolmentCountService } from './services/mfa-enrolment-count.service.js';
|
|
5
|
+
import { MfaEnrolmentStateService } from './services/mfa-enrolment-state.service.js';
|
|
6
|
+
import { mfaModule } from './plugin.js';
|
|
7
|
+
import { MfaEnrolment } from './entities/mfa-enrolment.entity.js';
|
|
8
|
+
import { MfaOrganizationPolicy } from './entities/mfa-organization-policy.entity.js';
|
|
9
|
+
import { MfaRecoveryCode } from './entities/mfa-recovery-code.entity.js';
|
|
10
|
+
import { MfaSocialIdentity } from './entities/mfa-social-identity.entity.js';
|
|
11
|
+
export function registerModule(ctx) {
|
|
12
|
+
ctx.di.register({
|
|
13
|
+
/**
|
|
14
|
+
* Federated sign-in, defaulted to whatever this deployment's environment
|
|
15
|
+
* configures (T143c).
|
|
16
|
+
*
|
|
17
|
+
* It used to default to `undefined` and be contributed by a root, which
|
|
18
|
+
* meant `composition.ts` imported this module's provider class and its env
|
|
19
|
+
* reader to decide, on the module's behalf, whether the module had social
|
|
20
|
+
* sign-in — the shape the composition checklist rules out for a knob the
|
|
21
|
+
* module can read itself. `MFA_OAUTH_*` is this module's configuration and
|
|
22
|
+
* nothing else reads it.
|
|
23
|
+
*
|
|
24
|
+
* The seam it leaves is the one `organizations`' VAT clients left in T138 /
|
|
25
|
+
* T140: production takes the real `openid-client` provider by default, the
|
|
26
|
+
* test harness contributes a deterministic fake over the same name, and
|
|
27
|
+
* neither root names the class. `undefined` still means "no social sign-in
|
|
28
|
+
* on this deployment", which is what an unconfigured environment produces.
|
|
29
|
+
*/
|
|
30
|
+
mfaOauthProvider: ctx
|
|
31
|
+
.asFunction(() => {
|
|
32
|
+
const config = readOAuthConfigFromEnv();
|
|
33
|
+
return config.google || config.microsoft ? new OpenIdOAuthProvider(config) : undefined;
|
|
34
|
+
})
|
|
35
|
+
.singleton(),
|
|
36
|
+
mfaSocialAccountResolvers: ctx
|
|
37
|
+
.asFunction(() => undefined)
|
|
38
|
+
.singleton(),
|
|
39
|
+
// `null` means "no system-default sales channel", which is what both roots
|
|
40
|
+
// already fall back to when the resolver finds none.
|
|
41
|
+
mfaDefaultChannelIdResolver: ctx
|
|
42
|
+
.asFunction(() => async () => null)
|
|
43
|
+
.singleton(),
|
|
44
|
+
mfaBaseUrls: ctx
|
|
45
|
+
.asFunction(() => ({
|
|
46
|
+
backend: process.env['BACKEND_PUBLIC_URL'],
|
|
47
|
+
storefront: process.env['STOREFRONT_BASE_URL'],
|
|
48
|
+
admin: process.env['ADMIN_BASE_URL'],
|
|
49
|
+
}))
|
|
50
|
+
.singleton(),
|
|
51
|
+
mfa: ctx
|
|
52
|
+
.asFunction(({ emFactory, redis, auditLogService, commandBus }) => {
|
|
53
|
+
const secretEncryptionKey = process.env['MFA_SECRET_ENCRYPTION_KEY'];
|
|
54
|
+
const { backend: backendBaseUrl, storefront: storefrontBaseUrl, admin: adminBaseUrl, } = ctx.cradle().mfaBaseUrls;
|
|
55
|
+
// Every cross-composition input below is read from the cradle at call
|
|
56
|
+
// time. Capturing them would freeze this module's view of the
|
|
57
|
+
// composition at the instant it happens to be constructed, which for
|
|
58
|
+
// a guard means it keeps admitting requests after `auth` goes away.
|
|
59
|
+
const { mfaOauthProvider: oauthProvider, mfaSocialAccountResolvers: socialAccountResolvers } = ctx.cradle();
|
|
60
|
+
// T118c — the four identity ports the retired bridge's members were
|
|
61
|
+
// built out of. `lazyPort` proxies, never resolved values: a captured
|
|
62
|
+
// gate keeps answering after its owner is switched off, and on an
|
|
63
|
+
// authentication path that is an account reachable without the factor
|
|
64
|
+
// it was supposed to require.
|
|
65
|
+
const customerAccounts = lazyPort(ctx, 'customerAccountReadPort');
|
|
66
|
+
const adminUsers = lazyPort(ctx, 'adminUserReadPort');
|
|
67
|
+
const resolveOrganizationAdmin = createOrganizationAdminResolver(customerAccounts);
|
|
68
|
+
return mfaModule({
|
|
69
|
+
emFactory,
|
|
70
|
+
redis,
|
|
71
|
+
// Deferred rather than destructured: both roots read `mfaLoginPort`
|
|
72
|
+
// to contribute the getter `customer_accounts` needs at login, and
|
|
73
|
+
// that read happens *before* either registers `settingsReadPort`.
|
|
74
|
+
// Taking it as a constructor argument would resolve this module at
|
|
75
|
+
// that moment and fail on a name that does not exist yet.
|
|
76
|
+
settingsService: {
|
|
77
|
+
get: (code, salesChannelId, schema) => ctx.cradle().settingsReadPort.get(code, salesChannelId, schema),
|
|
78
|
+
},
|
|
79
|
+
auditLogService,
|
|
80
|
+
commandBus,
|
|
81
|
+
// Issue #222 — `customer_accounts`' published answer to "does this
|
|
82
|
+
// account have a password on record". Resolved lazily like the
|
|
83
|
+
// session port beside it: a captured gate would keep answering
|
|
84
|
+
// after its owner went away, and this one decides whether somebody
|
|
85
|
+
// may remove their last way into their account.
|
|
86
|
+
customerPasswordState: lazyPort(ctx, 'customerPasswordStatePort'),
|
|
87
|
+
// Feature 075 Phase C — `auth`'s published session surface, not its
|
|
88
|
+
// `SessionService` class. The two fields the second factor reads
|
|
89
|
+
// (the cookie value and its expiry) travel as plain data; the
|
|
90
|
+
// `Session` entity stays behind the boundary, where its `tokenHash`
|
|
91
|
+
// belongs.
|
|
92
|
+
sessionService: lazyPort(ctx, 'authSessionPort'),
|
|
93
|
+
resolveDefaultChannelId: () => ctx.cradle().mfaDefaultChannelIdResolver(),
|
|
94
|
+
...(secretEncryptionKey === undefined ? {} : { secretEncryptionKey }),
|
|
95
|
+
...(backendBaseUrl === undefined ? {} : { backendBaseUrl }),
|
|
96
|
+
...(storefrontBaseUrl === undefined ? {} : { storefrontBaseUrl }),
|
|
97
|
+
...(adminBaseUrl === undefined ? {} : { adminBaseUrl }),
|
|
98
|
+
requireCustomer: (request) => ctx.cradle().requireCustomer(request),
|
|
99
|
+
requireAdmin: (permission) => async (req, reply) => ctx.cradle().requireAdmin(permission)(req, reply),
|
|
100
|
+
// T118c — read from the cradle at call time for the reason every
|
|
101
|
+
// other input here is: the composition's answer to "who is asking",
|
|
102
|
+
// not a second copy of it written in this module.
|
|
103
|
+
resolveCustomerActor: (request) => ctx.cradle().customerActorResolver(request),
|
|
104
|
+
// `adminContextResolver`, and no `promoteAdminActor` beside it:
|
|
105
|
+
// every call site is behind `requireAdmin`, whose `auth`
|
|
106
|
+
// implementation promotes and then refuses a non-admin, so the
|
|
107
|
+
// promotion has happened before this runs.
|
|
108
|
+
resolveAdminActor: (request) => ctx.cradle().adminContextResolver(request),
|
|
109
|
+
resolveOrgAdmin: (request) => resolveOrganizationAdmin(ctx.cradle().customerActorResolver(request).customerAccountId),
|
|
110
|
+
resolveOrganizationCustomerIds: createOrganizationCustomerIdsResolver(customerAccounts),
|
|
111
|
+
resolveAccountEmail: createAccountEmailResolver(customerAccounts, adminUsers),
|
|
112
|
+
verifyAccountPassword: createAccountPasswordVerifier(lazyPort(ctx, 'customerPasswordVerificationPort'), lazyPort(ctx, 'adminPasswordVerificationPort')),
|
|
113
|
+
// Spread rather than assigned: `exactOptionalPropertyTypes` makes
|
|
114
|
+
// "absent" and "present as undefined" different types, and these
|
|
115
|
+
// two are genuinely absent on a deployment without social sign-in.
|
|
116
|
+
...(oauthProvider === undefined ? {} : { oauthProvider }),
|
|
117
|
+
...(socialAccountResolvers === undefined ? {} : { socialAccountResolvers }),
|
|
118
|
+
});
|
|
119
|
+
})
|
|
120
|
+
.singleton(),
|
|
121
|
+
});
|
|
122
|
+
ctx.di.providePort('mfaLoginPort', ctx.asFunction(({ mfa }) => mfa.handle().mfaLoginPort).singleton());
|
|
123
|
+
/**
|
|
124
|
+
* The live half of the deactivation-confirmation dialog (owner ruling on
|
|
125
|
+
* D-96.5): how many people currently hold a second factor.
|
|
126
|
+
*
|
|
127
|
+
* Gated like every other port, and that is exactly right here — the dialog
|
|
128
|
+
* renders *before* the flip, while this module is still on, so the question
|
|
129
|
+
* is asked through an open gate. `/platform/modules` decides presence before
|
|
130
|
+
* it resolves this and renders "unavailable" rather than blocking the flip if
|
|
131
|
+
* the read fails.
|
|
132
|
+
*/
|
|
133
|
+
ctx.di.providePort('mfaEnrolmentCountPort', ctx
|
|
134
|
+
.asFunction(({ emFactory }) => new MfaEnrolmentCountService(emFactory))
|
|
135
|
+
.singleton());
|
|
136
|
+
/**
|
|
137
|
+
* Who holds a second factor, for the two identity modules that publish
|
|
138
|
+
* `twoFactorEnabled`.
|
|
139
|
+
*
|
|
140
|
+
* Gated like every other port, and unlike `mfaEnrolmentCountPort` above it is
|
|
141
|
+
* read on ordinary request paths rather than in a dialog that precedes the
|
|
142
|
+
* flip — so both consumers ask `effectiveState.isPresent('mfa')` before they
|
|
143
|
+
* resolve it and report `false` when this module is absent. That is a
|
|
144
|
+
* declared degrade, not a caught gate: with `mfa` off nothing asks for a
|
|
145
|
+
* second factor at sign-in, so no account is protected by one and `false` is
|
|
146
|
+
* the answer rather than a substitute for one.
|
|
147
|
+
*/
|
|
148
|
+
ctx.di.providePort('mfaEnrolmentStatePort', ctx
|
|
149
|
+
.asFunction(({ emFactory }) => new MfaEnrolmentStateService(emFactory))
|
|
150
|
+
.singleton());
|
|
151
|
+
ctx.routes(async (app) => {
|
|
152
|
+
const plugin = ctx.cradle().mfa.plugin;
|
|
153
|
+
await plugin(app);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
158
|
+
* array and **no named class export** (D-168).
|
|
159
|
+
*
|
|
160
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
161
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
162
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
163
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
164
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
165
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
166
|
+
*/
|
|
167
|
+
export const entities = [
|
|
168
|
+
MfaEnrolment,
|
|
169
|
+
MfaOrganizationPolicy,
|
|
170
|
+
MfaRecoveryCode,
|
|
171
|
+
MfaSocialIdentity,
|
|
172
|
+
];
|
|
173
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
|
3
|
+
import type { Redis } from 'ioredis';
|
|
4
|
+
import type { AuthSessionPort, CustomerPasswordStatePort, MfaLoginPort } from '@endora-commerce/contracts';
|
|
5
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
6
|
+
import type { CommandBus } from '@endora-commerce/platform/commands';
|
|
7
|
+
import { ChallengeStore } from './services/challenge-store.js';
|
|
8
|
+
import { MfaPolicyResolver, type SettingsReader } from './services/mfa-policy-resolver.js';
|
|
9
|
+
import { MfaEnrolmentService } from './services/mfa-enrolment-service.js';
|
|
10
|
+
import { type SocialIdentityDeps } from './services/social-identity-service.js';
|
|
11
|
+
import type { OAuthProviderPort } from './services/oauth-provider-service.js';
|
|
12
|
+
import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
|
|
13
|
+
/**
|
|
14
|
+
* The attach function this module hands its composition root.
|
|
15
|
+
*
|
|
16
|
+
* Typed on `fastify`'s own `FastifyInstance` rather than on the platform's
|
|
17
|
+
* `ModulePlugin`, which `contracts/host-package.md` §1.4g classifies **A**: the
|
|
18
|
+
* host does not publish it, so a packaged module cannot name it. The
|
|
19
|
+
* already-packaged `quote_requests` types its attach function the same way.
|
|
20
|
+
*/
|
|
21
|
+
type ModuleAttach = (app: FastifyInstance) => Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* MFA module composition root (feature 042).
|
|
24
|
+
*
|
|
25
|
+
* Owns 2FA enrolment, recovery codes, federated sign-in (later stories),
|
|
26
|
+
* policy resolution, and admin reset (later stories). Sibling services are
|
|
27
|
+
* injected so the module never imports another module's internals
|
|
28
|
+
* (Constitution Principle I). Exposes its `MfaLoginPort` via the handle for
|
|
29
|
+
* the per-surface login services.
|
|
30
|
+
*
|
|
31
|
+
* Enrolment requires `secretEncryptionKey`; when it is absent the module still
|
|
32
|
+
* composes and `beginLogin` works (password-only), but the enrolment + verify
|
|
33
|
+
* routes are not registered.
|
|
34
|
+
*/
|
|
35
|
+
export interface MfaModuleOptions {
|
|
36
|
+
emFactory: () => EntityManager;
|
|
37
|
+
redis: Redis;
|
|
38
|
+
settingsService: SettingsReader;
|
|
39
|
+
auditLogService: AuditPort;
|
|
40
|
+
/** Constitution XIII — the unlink is a security-relevant, audited write. */
|
|
41
|
+
commandBus: CommandBus;
|
|
42
|
+
/**
|
|
43
|
+
* Issue #222 — whether a customer account has a password on record, which is
|
|
44
|
+
* what decides whether its last federated identity may be severed. Mandatory:
|
|
45
|
+
* a composition without it would have to guess, and the two guesses are "lock
|
|
46
|
+
* the holder out" and "refuse forever".
|
|
47
|
+
*/
|
|
48
|
+
customerPasswordState: CustomerPasswordStatePort;
|
|
49
|
+
sessionService: AuthSessionPort;
|
|
50
|
+
/** Resolves the system-default sales-channel id for global setting reads. */
|
|
51
|
+
resolveDefaultChannelId?: () => Promise<string | null>;
|
|
52
|
+
/** base64 32-byte AES key for TOTP secrets at rest. */
|
|
53
|
+
secretEncryptionKey?: string | undefined;
|
|
54
|
+
/** Storefront customer guard + actor resolver (for self-service routes). */
|
|
55
|
+
requireCustomer: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
56
|
+
resolveCustomerActor: (req: FastifyRequest) => {
|
|
57
|
+
customerAccountId: string;
|
|
58
|
+
organizationId: string | null;
|
|
59
|
+
};
|
|
60
|
+
/** Admin guard factory + actor resolver (admin self-service US2 + reset US6). */
|
|
61
|
+
requireAdmin?: RequireAdminFactory;
|
|
62
|
+
resolveAdminActor: (req: FastifyRequest) => {
|
|
63
|
+
adminUserId: string;
|
|
64
|
+
};
|
|
65
|
+
/** Lists an organization's customer-account ids (US6 bulk reset). */
|
|
66
|
+
resolveOrganizationCustomerIds: (organizationId: string) => Promise<string[]>;
|
|
67
|
+
/** Resolves an org-admin customer's org + asserts the role (US3 storefront). */
|
|
68
|
+
resolveOrgAdmin: (req: FastifyRequest) => Promise<{
|
|
69
|
+
organizationId: string;
|
|
70
|
+
actor: string;
|
|
71
|
+
}>;
|
|
72
|
+
/** Federated sign-in (US4/US5) — provider port + account resolvers + URLs. */
|
|
73
|
+
oauthProvider?: OAuthProviderPort;
|
|
74
|
+
socialAccountResolvers?: SocialIdentityDeps;
|
|
75
|
+
backendBaseUrl?: string;
|
|
76
|
+
storefrontBaseUrl?: string;
|
|
77
|
+
adminBaseUrl?: string;
|
|
78
|
+
/**
|
|
79
|
+
* `specs/110-instance-repository/` T118c — required, where both were optional
|
|
80
|
+
* while a composition root supplied them. `mfa` resolves each from the owning
|
|
81
|
+
* identity module's published port now, so there is no composition that has
|
|
82
|
+
* one and not the other, and the optionality was a live divergence rather
|
|
83
|
+
* than a capability: the harness omitted both and no test could see it.
|
|
84
|
+
*/
|
|
85
|
+
resolveAccountEmail: (subjectType: 'customer' | 'admin', subjectId: string) => Promise<string | null>;
|
|
86
|
+
verifyAccountPassword: (subjectType: 'customer' | 'admin', subjectId: string, password: string) => Promise<boolean>;
|
|
87
|
+
}
|
|
88
|
+
export interface MfaModuleHandle {
|
|
89
|
+
mfaLoginPort: MfaLoginPort;
|
|
90
|
+
challengeStore: ChallengeStore;
|
|
91
|
+
policyResolver: MfaPolicyResolver;
|
|
92
|
+
enrolmentService: MfaEnrolmentService | null;
|
|
93
|
+
}
|
|
94
|
+
export declare function mfaModule(options: MfaModuleOptions): {
|
|
95
|
+
plugin: ModuleAttach;
|
|
96
|
+
handle: () => MfaModuleHandle;
|
|
97
|
+
};
|
|
98
|
+
export {};
|
|
99
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { ChallengeStore } from './services/challenge-store.js';
|
|
2
|
+
import { MfaPolicyResolver, } from './services/mfa-policy-resolver.js';
|
|
3
|
+
import { MfaLoginService } from './services/mfa-login-service.js';
|
|
4
|
+
import { MfaEnrolmentService } from './services/mfa-enrolment-service.js';
|
|
5
|
+
import { MfaOrgPolicyService } from './services/mfa-org-policy-service.js';
|
|
6
|
+
import { SocialIdentityService, } from './services/social-identity-service.js';
|
|
7
|
+
import { SocialLinkService } from './services/social-link-service.js';
|
|
8
|
+
import { SecretCipher } from './services/secret-cipher.js';
|
|
9
|
+
import { registerMfaPublicRoutes } from './routes.public.js';
|
|
10
|
+
import { registerMfaSelfServiceRoutes } from './routes.self-service.js';
|
|
11
|
+
import { registerMfaAdminRoutes } from './routes.admin.js';
|
|
12
|
+
import { registerMfaOrgRoutes } from './routes.org.js';
|
|
13
|
+
import { registerMfaOAuthRoutes } from './routes.oauth.js';
|
|
14
|
+
export function mfaModule(options) {
|
|
15
|
+
const challengeStore = new ChallengeStore(options.redis);
|
|
16
|
+
const policyResolver = new MfaPolicyResolver(options.settingsService, options.emFactory, options.resolveDefaultChannelId ?? (async () => null));
|
|
17
|
+
const cipher = options.secretEncryptionKey
|
|
18
|
+
? new SecretCipher(options.secretEncryptionKey)
|
|
19
|
+
: null;
|
|
20
|
+
const enrolmentService = cipher
|
|
21
|
+
? new MfaEnrolmentService(options.emFactory, cipher, options.auditLogService)
|
|
22
|
+
: null;
|
|
23
|
+
const loginService = new MfaLoginService(options.emFactory, challengeStore, policyResolver, enrolmentService ?? undefined);
|
|
24
|
+
const orgPolicyService = new MfaOrgPolicyService(options.emFactory, options.auditLogService);
|
|
25
|
+
// Deliberately not behind the `oauthProvider` check below: the links an
|
|
26
|
+
// account already holds stay listable and severable after an operator turns
|
|
27
|
+
// a provider off, which is precisely when somebody goes looking for them.
|
|
28
|
+
const socialLinkService = new SocialLinkService(options.emFactory, options.commandBus, options.customerPasswordState);
|
|
29
|
+
const plugin = async (app) => {
|
|
30
|
+
if (!enrolmentService)
|
|
31
|
+
return; // enrolment disabled without an encryption key
|
|
32
|
+
await registerMfaPublicRoutes(app, {
|
|
33
|
+
loginService,
|
|
34
|
+
sessionService: options.sessionService,
|
|
35
|
+
challengeStore,
|
|
36
|
+
enrolmentService,
|
|
37
|
+
auditLogService: options.auditLogService,
|
|
38
|
+
});
|
|
39
|
+
// Storefront customer self-service (US1).
|
|
40
|
+
await registerMfaSelfServiceRoutes(app, {
|
|
41
|
+
pathPrefix: '/api/v1/account/mfa',
|
|
42
|
+
subjectType: 'customer',
|
|
43
|
+
auditObjectType: 'customer_account',
|
|
44
|
+
requireGuard: options.requireCustomer,
|
|
45
|
+
resolveSubjectId: (req) => options.resolveCustomerActor(req).customerAccountId,
|
|
46
|
+
resolveOrganizationId: (req) => options.resolveCustomerActor(req).organizationId,
|
|
47
|
+
enrolmentService,
|
|
48
|
+
policyResolver,
|
|
49
|
+
socialLinkService,
|
|
50
|
+
auditLogService: options.auditLogService,
|
|
51
|
+
resolveAccountEmail: options.resolveAccountEmail,
|
|
52
|
+
verifyAccountPassword: options.verifyAccountPassword,
|
|
53
|
+
});
|
|
54
|
+
// Federated sign-in (US4 customer / US5 admin).
|
|
55
|
+
if (options.oauthProvider && options.socialAccountResolvers) {
|
|
56
|
+
const socialIdentityService = new SocialIdentityService(options.emFactory, options.socialAccountResolvers, options.auditLogService);
|
|
57
|
+
await registerMfaOAuthRoutes(app, {
|
|
58
|
+
oauthProvider: options.oauthProvider,
|
|
59
|
+
challengeStore,
|
|
60
|
+
policyResolver,
|
|
61
|
+
socialIdentityService,
|
|
62
|
+
sessionService: options.sessionService,
|
|
63
|
+
backendBaseUrl: options.backendBaseUrl ?? 'http://localhost:8080',
|
|
64
|
+
storefrontBaseUrl: options.storefrontBaseUrl ?? 'http://localhost:3000',
|
|
65
|
+
adminBaseUrl: options.adminBaseUrl ?? 'http://localhost:3002',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// Storefront org-admin enforcement (US3).
|
|
69
|
+
await registerMfaOrgRoutes(app, {
|
|
70
|
+
orgPolicyService,
|
|
71
|
+
auditLogService: options.auditLogService,
|
|
72
|
+
requireCustomer: options.requireCustomer,
|
|
73
|
+
resolveOrgAdmin: options.resolveOrgAdmin,
|
|
74
|
+
});
|
|
75
|
+
// Admin user self-service (US2) + admin reset (US6) + org enforcement (US3).
|
|
76
|
+
const { requireAdmin, resolveAdminActor } = options;
|
|
77
|
+
if (requireAdmin) {
|
|
78
|
+
await registerMfaSelfServiceRoutes(app, {
|
|
79
|
+
pathPrefix: '/api/v1/admin/account/mfa',
|
|
80
|
+
subjectType: 'admin',
|
|
81
|
+
auditObjectType: 'admin_user',
|
|
82
|
+
requireGuard: requireAdmin(),
|
|
83
|
+
resolveSubjectId: (req) => resolveAdminActor(req).adminUserId,
|
|
84
|
+
enrolmentService,
|
|
85
|
+
policyResolver,
|
|
86
|
+
socialLinkService,
|
|
87
|
+
auditLogService: options.auditLogService,
|
|
88
|
+
resolveAccountEmail: options.resolveAccountEmail,
|
|
89
|
+
verifyAccountPassword: options.verifyAccountPassword,
|
|
90
|
+
});
|
|
91
|
+
await registerMfaAdminRoutes(app, {
|
|
92
|
+
enrolmentService,
|
|
93
|
+
orgPolicyService,
|
|
94
|
+
auditLogService: options.auditLogService,
|
|
95
|
+
requireAdmin,
|
|
96
|
+
resolveAdminActor,
|
|
97
|
+
resolveOrganizationCustomerIds: options.resolveOrganizationCustomerIds,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
plugin,
|
|
103
|
+
handle: () => ({
|
|
104
|
+
mfaLoginPort: loginService,
|
|
105
|
+
challengeStore,
|
|
106
|
+
policyResolver,
|
|
107
|
+
enrolmentService,
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"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"}
|