@endora-commerce/mod-auth 0.0.0-stage → 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 +49 -2
- package/dist/backend/entities/session.entity.d.ts +31 -0
- package/dist/backend/entities/session.entity.d.ts.map +1 -0
- package/dist/backend/entities/session.entity.js +95 -0
- package/dist/backend/entities/session.entity.js.map +1 -0
- package/dist/backend/index.d.ts +120 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +194 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +41 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +164 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/require-admin.d.ts +35 -0
- package/dist/backend/require-admin.d.ts.map +1 -0
- package/dist/backend/require-admin.js +50 -0
- package/dist/backend/require-admin.js.map +1 -0
- package/dist/backend/require-customer.d.ts +4 -0
- package/dist/backend/require-customer.d.ts.map +1 -0
- package/dist/backend/require-customer.js +16 -0
- package/dist/backend/require-customer.js.map +1 -0
- package/dist/backend/services/session-port.d.ts +30 -0
- package/dist/backend/services/session-port.d.ts.map +1 -0
- package/dist/backend/services/session-port.js +93 -0
- package/dist/backend/services/session-port.js.map +1 -0
- package/dist/backend/services/session-service.d.ts +56 -0
- package/dist/backend/services/session-service.d.ts.map +1 -0
- package/dist/backend/services/session-service.js +200 -0
- package/dist/backend/services/session-service.js.map +1 -0
- package/dist/manifest.d.ts +169 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +71 -0
- package/dist/manifest.js.map +1 -0
- package/docs/auth.md +42 -0
- package/package.json +60 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
-
|
|
1
|
+
<!-- Generated by `pnpm --filter backend run manifests:generate`. Delete this line to take the file over; a README without it is never regenerated. -->
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# @endora-commerce/mod-auth
|
|
4
|
+
|
|
5
|
+
Authentication primitives — session cookies, bearer-token resolution, and request actor binding.
|
|
6
|
+
|
|
7
|
+
## What this is
|
|
8
|
+
|
|
9
|
+
An **Endora Commerce module package**. Its module id is `auth` — the identity of record in the platform's registries, in its migrations and in its setting, permission and translation codes.
|
|
10
|
+
|
|
11
|
+
A module is not imported by application code. The platform discovers the extension packages an instance has installed, reads the manifest on the root subpath and composes the module from it, so installing this package and starting the instance is the whole integration.
|
|
12
|
+
|
|
13
|
+
## Entry points
|
|
14
|
+
|
|
15
|
+
| Import | Contents |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `@endora-commerce/mod-auth` | the module manifest — its id, version, dependencies, settings and activation |
|
|
18
|
+
| `@endora-commerce/mod-auth/backend` | the composition root the platform calls, with the entities, services, routes and workers it registers |
|
|
19
|
+
|
|
20
|
+
## Depends on
|
|
21
|
+
|
|
22
|
+
Everything below is a **peer** dependency, so the application resolves exactly one copy of each — two copies of a host or of React are a runtime failure rather than a type error. An entry marked *optional* is needed only by the layers that use it.
|
|
23
|
+
|
|
24
|
+
**Endora packages**
|
|
25
|
+
|
|
26
|
+
- `@endora-commerce/contracts`
|
|
27
|
+
- `@endora-commerce/platform`
|
|
28
|
+
|
|
29
|
+
**Third-party**
|
|
30
|
+
|
|
31
|
+
- `@mikro-orm/core` ^6
|
|
32
|
+
- `@mikro-orm/postgresql` ^6
|
|
33
|
+
- `fastify` ^5
|
|
34
|
+
- `fastify-plugin` ^5
|
|
35
|
+
- `ioredis` ^5
|
|
36
|
+
|
|
37
|
+
## What the tarball carries
|
|
38
|
+
|
|
39
|
+
- `dist/` — the compiled JavaScript and its type declarations
|
|
40
|
+
- `docs/` — the package’s own pages in the Endora Commerce documentation site
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pnpm add @endora-commerce/mod-auth
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Licence
|
|
49
|
+
|
|
50
|
+
MIT — the text is in `LICENSE`, beside this file.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { OptionalProps } from '@mikro-orm/core';
|
|
2
|
+
/**
|
|
3
|
+
* Persistent session row. Backed by Postgres; hot reads are cached in Redis by
|
|
4
|
+
* SessionService. See data-model.md § Domain 1 and R-11.
|
|
5
|
+
*
|
|
6
|
+
* `auth` is the permitted singular-exception module folder per Principle VI; this
|
|
7
|
+
* entity's table name remains `sessions` because class-to-table is still pluralised.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Session {
|
|
10
|
+
[OptionalProps]?: 'id' | 'createdAt' | 'updatedAt';
|
|
11
|
+
id: string;
|
|
12
|
+
/** Opaque token shown to the client (cookie value). Never used for lookups directly —
|
|
13
|
+
* we hash it before storing. The actual cookie sent to the client is `${id}.${tokenHash}`. */
|
|
14
|
+
tokenHash: string;
|
|
15
|
+
/** Null for admin-user sessions; set for customer-account sessions. */
|
|
16
|
+
customerAccountId?: string | null;
|
|
17
|
+
/** Null for customer sessions; set for admin-user sessions. */
|
|
18
|
+
adminUserId?: string | null;
|
|
19
|
+
/**
|
|
20
|
+
* When an admin-user is impersonating a customer, both `customerAccountId` (target)
|
|
21
|
+
* and `impersonatorAdminUserId` (the admin) are set — see R-12.
|
|
22
|
+
*/
|
|
23
|
+
impersonatorAdminUserId?: string | null;
|
|
24
|
+
expiresAt: Date;
|
|
25
|
+
createdAt: Date;
|
|
26
|
+
updatedAt: Date;
|
|
27
|
+
lastSeenAt?: Date | null;
|
|
28
|
+
ipAddress?: string | null;
|
|
29
|
+
userAgent?: string | null;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=session.entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/session.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAIrF;;;;;;GAMG;AACH,qBAEa,OAAO;IAClB,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,CAAC;IAInD,EAAE,EAAE,MAAM,CAAgB;IAE1B;mGAC+F;IAG/F,SAAS,EAAG,MAAM,CAAC;IAEnB,uEAAuE;IAGvE,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAElC,+DAA+D;IAG/D,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B;;;OAGG;IAEH,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAIxC,SAAS,EAAG,IAAI,CAAC;IAGjB,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,EAAE,IAAI,CAAc;IAG7B,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAGzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B"}
|
|
@@ -0,0 +1,95 @@
|
|
|
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
|
+
* Persistent session row. Backed by Postgres; hot reads are cached in Redis by
|
|
15
|
+
* SessionService. See data-model.md § Domain 1 and R-11.
|
|
16
|
+
*
|
|
17
|
+
* `auth` is the permitted singular-exception module folder per Principle VI; this
|
|
18
|
+
* entity's table name remains `sessions` because class-to-table is still pluralised.
|
|
19
|
+
*/
|
|
20
|
+
let Session = class Session {
|
|
21
|
+
[OptionalProps];
|
|
22
|
+
id = randomUUID();
|
|
23
|
+
/** Opaque token shown to the client (cookie value). Never used for lookups directly —
|
|
24
|
+
* we hash it before storing. The actual cookie sent to the client is `${id}.${tokenHash}`. */
|
|
25
|
+
tokenHash;
|
|
26
|
+
/** Null for admin-user sessions; set for customer-account sessions. */
|
|
27
|
+
customerAccountId;
|
|
28
|
+
/** Null for customer sessions; set for admin-user sessions. */
|
|
29
|
+
adminUserId;
|
|
30
|
+
/**
|
|
31
|
+
* When an admin-user is impersonating a customer, both `customerAccountId` (target)
|
|
32
|
+
* and `impersonatorAdminUserId` (the admin) are set — see R-12.
|
|
33
|
+
*/
|
|
34
|
+
impersonatorAdminUserId;
|
|
35
|
+
expiresAt;
|
|
36
|
+
createdAt = new Date();
|
|
37
|
+
updatedAt = new Date();
|
|
38
|
+
lastSeenAt;
|
|
39
|
+
ipAddress;
|
|
40
|
+
userAgent;
|
|
41
|
+
};
|
|
42
|
+
__decorate([
|
|
43
|
+
PrimaryKey({ type: 'uuid' }),
|
|
44
|
+
__metadata("design:type", String)
|
|
45
|
+
], Session.prototype, "id", void 0);
|
|
46
|
+
__decorate([
|
|
47
|
+
Property({ type: 'string', length: 128 }),
|
|
48
|
+
Index(),
|
|
49
|
+
__metadata("design:type", String)
|
|
50
|
+
], Session.prototype, "tokenHash", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
Property({ type: 'uuid', nullable: true }),
|
|
53
|
+
Index(),
|
|
54
|
+
__metadata("design:type", Object)
|
|
55
|
+
], Session.prototype, "customerAccountId", void 0);
|
|
56
|
+
__decorate([
|
|
57
|
+
Property({ type: 'uuid', nullable: true }),
|
|
58
|
+
Index(),
|
|
59
|
+
__metadata("design:type", Object)
|
|
60
|
+
], Session.prototype, "adminUserId", void 0);
|
|
61
|
+
__decorate([
|
|
62
|
+
Property({ type: 'uuid', nullable: true }),
|
|
63
|
+
__metadata("design:type", Object)
|
|
64
|
+
], Session.prototype, "impersonatorAdminUserId", void 0);
|
|
65
|
+
__decorate([
|
|
66
|
+
Property({ type: 'datetime' }),
|
|
67
|
+
Index(),
|
|
68
|
+
__metadata("design:type", Date)
|
|
69
|
+
], Session.prototype, "expiresAt", void 0);
|
|
70
|
+
__decorate([
|
|
71
|
+
Property({ type: 'datetime', onCreate: () => new Date() }),
|
|
72
|
+
__metadata("design:type", Date)
|
|
73
|
+
], Session.prototype, "createdAt", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
Property({ type: 'datetime', onUpdate: () => new Date() }),
|
|
76
|
+
__metadata("design:type", Date)
|
|
77
|
+
], Session.prototype, "updatedAt", void 0);
|
|
78
|
+
__decorate([
|
|
79
|
+
Property({ type: 'datetime', nullable: true }),
|
|
80
|
+
__metadata("design:type", Object)
|
|
81
|
+
], Session.prototype, "lastSeenAt", void 0);
|
|
82
|
+
__decorate([
|
|
83
|
+
Property({ type: 'string', length: 45, nullable: true }),
|
|
84
|
+
__metadata("design:type", Object)
|
|
85
|
+
], Session.prototype, "ipAddress", void 0);
|
|
86
|
+
__decorate([
|
|
87
|
+
Property({ type: 'string', length: 255, nullable: true }),
|
|
88
|
+
__metadata("design:type", Object)
|
|
89
|
+
], Session.prototype, "userAgent", void 0);
|
|
90
|
+
Session = __decorate([
|
|
91
|
+
GlobalEntity(),
|
|
92
|
+
Entity({ tableName: 'sessions' })
|
|
93
|
+
], Session);
|
|
94
|
+
export { Session };
|
|
95
|
+
//# sourceMappingURL=session.entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/session.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;;;;;;GAMG;AAGI,IAAM,OAAO,GAAb,MAAM,OAAO;IAClB,CAAC,aAAa,CAAC,CAAoC;IAInD,EAAE,GAAW,UAAU,EAAE,CAAC;IAE1B;mGAC+F;IAG/F,SAAS,CAAU;IAEnB,uEAAuE;IAGvE,iBAAiB,CAAiB;IAElC,+DAA+D;IAG/D,WAAW,CAAiB;IAE5B;;;OAGG;IAEH,uBAAuB,CAAiB;IAIxC,SAAS,CAAQ;IAGjB,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,UAAU,CAAe;IAGzB,SAAS,CAAiB;IAG1B,SAAS,CAAiB;CAC3B,CAAA;AA3CC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;mCACH;AAM1B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IACzC,KAAK,EAAE;;0CACW;AAKnB;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,KAAK,EAAE;;kDAC0B;AAKlC;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,KAAK,EAAE;;4CACoB;AAO5B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wDACH;AAIxC;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC9B,KAAK,EAAE;8BACI,IAAI;0CAAC;AAGjB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;0CAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;0CAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACtB;AAGzB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CAC/B;AAG1B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CAChC;AA/Cf,OAAO;IAFnB,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;GACrB,OAAO,CAgDnB"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { Redis } from 'ioredis';
|
|
3
|
+
import type { ModuleContext } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import type { AdminPermissionChecker } from '@endora-commerce/platform/kernel';
|
|
5
|
+
import { Session } from './entities/session.entity.js';
|
|
6
|
+
import { SessionService } from './services/session-service.js';
|
|
7
|
+
/**
|
|
8
|
+
* `auth` — the module every other module's guards read (feature 072, T078).
|
|
9
|
+
*
|
|
10
|
+
* Three things about this conversion are not shared with the modules that
|
|
11
|
+
* follow it, and each is a property of what `auth` is rather than of how it was
|
|
12
|
+
* written.
|
|
13
|
+
*
|
|
14
|
+
* 1. **Its plugin goes through `ctx.rootPlugin`, not `ctx.routes`.** It
|
|
15
|
+
* contributes no routes; it decorates `request.actor`, which every module's
|
|
16
|
+
* guards read. Both routes seams encapsulate, and a decoration applied
|
|
17
|
+
* inside a Fastify child context is invisible to that context's siblings —
|
|
18
|
+
* so registering it through either would leave every module but this one
|
|
19
|
+
* with no actor.
|
|
20
|
+
* 2. **Its ports are the only ones a disabled `auth` could withdraw, and it
|
|
21
|
+
* cannot be disabled.** `requireAdmin` gates 205 call sites across 60
|
|
22
|
+
* modules; a platform whose admin guard is absent has no admin surface to
|
|
23
|
+
* speak of, so the manifest declares the module non-deactivatable and the
|
|
24
|
+
* orchestrator refuses to switch it off. The port gate is still registered
|
|
25
|
+
* rather than skipped: the check that can never fire costs one predicate,
|
|
26
|
+
* and an exception carved into the port machinery would cost a reader's
|
|
27
|
+
* confidence in every other gate.
|
|
28
|
+
* 3. **It consumes `permissionService`, which `admin_roles` owns.** The
|
|
29
|
+
* manifest declares that dependency (D-32); it is the edge that made `auth`
|
|
30
|
+
* the first module to need `check:port-dependencies` to pass.
|
|
31
|
+
*/
|
|
32
|
+
/** What `auth` resolves: its own registrations, plus what it needs from elsewhere. */
|
|
33
|
+
export interface AuthCradle {
|
|
34
|
+
readonly emFactory: () => EntityManager;
|
|
35
|
+
readonly redis: Redis;
|
|
36
|
+
/** `admin_roles`' permission checker, narrowed to what the guard uses. */
|
|
37
|
+
readonly permissionService: AdminPermissionChecker;
|
|
38
|
+
readonly sessionService: SessionService;
|
|
39
|
+
/**
|
|
40
|
+
* `api_keys`' bearer-token resolver. Registered by the composition root
|
|
41
|
+
* **after** this module composes: `api_keys` provides the port, and the
|
|
42
|
+
* single-pass composition orders it by the manifest graph rather than by
|
|
43
|
+
* this file. It is read per request, so the ordering is invisible here.
|
|
44
|
+
*/
|
|
45
|
+
readonly apiKeyResolver: ((token: string) => Promise<{
|
|
46
|
+
apiKeyId: string;
|
|
47
|
+
scopes: string[];
|
|
48
|
+
organizationId?: string | null;
|
|
49
|
+
salesChannelId?: string | null;
|
|
50
|
+
customerAccountId?: string | null;
|
|
51
|
+
} | null>) | undefined;
|
|
52
|
+
/** Resolves a customer account to its organisation. Same late-binding story. */
|
|
53
|
+
readonly customerOrgResolver: ((customerAccountId: string) => Promise<string | null>) | undefined;
|
|
54
|
+
}
|
|
55
|
+
export declare function registerModule(ctx: ModuleContext): void;
|
|
56
|
+
/**
|
|
57
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
58
|
+
* array and **no named class export** (D-168).
|
|
59
|
+
*
|
|
60
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
61
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
62
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
63
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
64
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
65
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
66
|
+
*/
|
|
67
|
+
export declare const entities: (typeof Session)[];
|
|
68
|
+
/**
|
|
69
|
+
* The module's own implementation classes and guard factories, on the
|
|
70
|
+
* `./backend` subpath.
|
|
71
|
+
*
|
|
72
|
+
* They are published for the reason `pim_ergonode` publishes
|
|
73
|
+
* `ErgonodeRequestError`: a consumer that needs the **class** must get the one
|
|
74
|
+
* the platform composed. `SessionService` reaches `Session` and
|
|
75
|
+
* `AuthSessionReadService` reaches it too, so a second copy of either is a
|
|
76
|
+
* second `Session` — a class the ORM never discovered, and `em.find` answers a
|
|
77
|
+
* `MetadataError` rather than rows (D-160.6.1). Publishing them is what lets a
|
|
78
|
+
* consumer name one copy instead of resolving a filesystem path into this
|
|
79
|
+
* package's source.
|
|
80
|
+
*
|
|
81
|
+
* No module reaches any of these; the consumers today are `backend/test/`,
|
|
82
|
+
* which is why they are not on a `./ports` subpath — that one is contract
|
|
83
|
+
* surface (D-171) and these are implementations.
|
|
84
|
+
*/
|
|
85
|
+
/**
|
|
86
|
+
* What `request.actor` **is** — and it is no longer this package's to say
|
|
87
|
+
* (`specs/110-instance-repository/` T118b).
|
|
88
|
+
*
|
|
89
|
+
* `plugin.ts` used to declare the four actor kinds and a
|
|
90
|
+
* `declare module 'fastify'` block adding `actor` and `adminActor` to
|
|
91
|
+
* `FastifyRequest`, and this barrel re-exported the five type names.
|
|
92
|
+
* `specs/117-instance-bring-up/` FR-030 published them for a real reason: an
|
|
93
|
+
* ambient augmentation reaches a consumer's program only if the file declaring
|
|
94
|
+
* it is *in* that program, and the production composition root was getting it
|
|
95
|
+
* incidentally through a **value** import of `promoteAdminActor`. Retiring that
|
|
96
|
+
* import took the augmentation with it and thirty reads of `request.actor`
|
|
97
|
+
* stopped compiling.
|
|
98
|
+
*
|
|
99
|
+
* The block said relocating the augmentation *"would take `Session` with it"*.
|
|
100
|
+
* That premise was false and is what T118b measured: across `packages/`,
|
|
101
|
+
* `backend/`, `admin/` and `storefront/`, `actor.session` was read in **zero**
|
|
102
|
+
* files outside this package. So the vocabulary is
|
|
103
|
+
* `@endora-commerce/contracts`' {@link Actor} — session-free, importing neither
|
|
104
|
+
* Fastify nor the ORM — and the augmentation is the platform's, on
|
|
105
|
+
* `@endora-commerce/platform/http`. A consumer names one of those two; neither
|
|
106
|
+
* is a boundary reach, and no consumer needs a type-only import of a module any
|
|
107
|
+
* more.
|
|
108
|
+
*
|
|
109
|
+
* The five names are **not** re-exported from here. A re-export of a
|
|
110
|
+
* `@endora-commerce/contracts` type on this package's `./backend` barrel is
|
|
111
|
+
* what `module-package-entity-surface.test.ts` refuses under D-168: it cannot
|
|
112
|
+
* follow the specifier, so whether a named entity class comes through it is
|
|
113
|
+
* unknown rather than false. That is the same rule that stopped this file
|
|
114
|
+
* re-exporting the two cookie names when the package landed.
|
|
115
|
+
*/
|
|
116
|
+
export { SessionService } from './services/session-service.js';
|
|
117
|
+
export { AuthSessionReadService, createAuthSessionPort } from './services/session-port.js';
|
|
118
|
+
export { createRequireAdmin, createRequireAdminAny } from './require-admin.js';
|
|
119
|
+
export { createRequireCustomer } from './require-customer.js';
|
|
120
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAK/E,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,sFAAsF;AACtF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,iBAAiB,EAAE,sBAAsB,CAAC;IACnD,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,EACnB,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;QAC1B,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACnC,GAAG,IAAI,CAAC,CAAC,GACV,SAAS,CAAC;IACd,gFAAgF;IAChF,QAAQ,CAAC,mBAAmB,EAAE,CAAC,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;CACnG;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA0HvD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,oBAAY,CAAC;AA+BlC;;;;;;;;;;;;;;;;GAgBG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAC3F,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { effectiveState } from '@endora-commerce/platform/kernel';
|
|
2
|
+
import { authPlugin, promoteAdminActor } from './plugin.js';
|
|
3
|
+
import { createRequireAdmin, createRequireAdminAny } from './require-admin.js';
|
|
4
|
+
import { createRequireCustomer } from './require-customer.js';
|
|
5
|
+
import { AuthSessionReadService, createAuthSessionPort } from './services/session-port.js';
|
|
6
|
+
import { Session } from './entities/session.entity.js';
|
|
7
|
+
import { SessionService } from './services/session-service.js';
|
|
8
|
+
export function registerModule(ctx) {
|
|
9
|
+
ctx.di.register({
|
|
10
|
+
sessionService: ctx
|
|
11
|
+
.asFunction(({ emFactory, redis }) => new SessionService(emFactory, redis))
|
|
12
|
+
.singleton(),
|
|
13
|
+
});
|
|
14
|
+
// `requireAdmin` is the platform's single admin guard and the most-resolved
|
|
15
|
+
// name in the tree. It is a port because it is resolved by 60 other modules;
|
|
16
|
+
// `requireAdminAny` is the same guard with an any-of-these-codes predicate,
|
|
17
|
+
// and lives here for the same reason (`promoteAdminActor` plus a permission
|
|
18
|
+
// check are both this module's business).
|
|
19
|
+
//
|
|
20
|
+
// Both guards read `permissionService` **per check**, not at construction,
|
|
21
|
+
// and that is a correctness requirement rather than a style choice.
|
|
22
|
+
// `permissionService` is a port owned by `admin_roles`, so its resolution is
|
|
23
|
+
// gated on that module's effective state; a guard that captured the instance
|
|
24
|
+
// once would keep answering from it after the module went away, which is the
|
|
25
|
+
// permissive failure the gate exists to prevent. Awilix enforces the same
|
|
26
|
+
// thing from the other side — a singleton may not depend on the transient
|
|
27
|
+
// gate wrapper — so the two agree.
|
|
28
|
+
const permissionChecker = () => ({
|
|
29
|
+
hasPermission: (adminUserId, permission) => ctx.cradle().permissionService.hasPermission(adminUserId, permission),
|
|
30
|
+
});
|
|
31
|
+
// Feature 075 Phase P — the published session surface. `sessionService` keeps
|
|
32
|
+
// its registration for the four modules Phase C has not rewired yet; these two
|
|
33
|
+
// are what they rewire *to*, and the difference is that neither hands a
|
|
34
|
+
// `Session` entity across the boundary.
|
|
35
|
+
//
|
|
36
|
+
// `providePort` rather than `register`, even though `auth` is
|
|
37
|
+
// non-deactivatable and the gate can therefore never close: this module made
|
|
38
|
+
// that call already for `requireAdmin` (see the note above), and a second
|
|
39
|
+
// answer inside one module would be worse than either answer.
|
|
40
|
+
ctx.di.providePort('authSessionPort', ctx
|
|
41
|
+
.asFunction(({ sessionService }) => createAuthSessionPort(sessionService))
|
|
42
|
+
.singleton());
|
|
43
|
+
ctx.di.providePort('authSessionReadPort', ctx.asFunction(({ emFactory }) => new AuthSessionReadService(emFactory)).singleton());
|
|
44
|
+
ctx.di.providePort('requireAdmin', ctx.asFunction(() => createRequireAdmin({ permissionService: permissionChecker() })).singleton());
|
|
45
|
+
ctx.di.providePort('requireAdminAny', ctx
|
|
46
|
+
.asFunction(() => createRequireAdminAny({ permissionService: permissionChecker() }))
|
|
47
|
+
.singleton());
|
|
48
|
+
// The promotion the two admin guards above already perform, published under
|
|
49
|
+
// its own name (`specs/117-instance-bring-up/` FR-030). It is not a third
|
|
50
|
+
// guard: it decides nothing and refuses nobody, it moves `request.adminActor`
|
|
51
|
+
// into `request.actor` so that an admin route works on a request that also
|
|
52
|
+
// carries a customer session.
|
|
53
|
+
//
|
|
54
|
+
// It is a port for one consumer — the production composition root, which
|
|
55
|
+
// calls it inside the actor bridge it contributes to `mfa`. That root is
|
|
56
|
+
// moving into `@endora-commerce/platform` (`specs/110-instance-repository/`
|
|
57
|
+
// T118), and a platform file may not import a module (D-52, D-53), so the
|
|
58
|
+
// function's *exported* spelling stops being available to it while the
|
|
59
|
+
// container name survives the move. This is the drain condition the barrel
|
|
60
|
+
// below and `test/contract/kernel/harness-parity.test.ts` both named.
|
|
61
|
+
//
|
|
62
|
+
// `providePort` like its four neighbours, on the same reasoning: the gate can
|
|
63
|
+
// never close, because `auth` is non-deactivatable, and a second answer
|
|
64
|
+
// inside one module would be worse than either answer. No type argument, for
|
|
65
|
+
// symmetry with the two guards above — the shape is
|
|
66
|
+
// `AdminActorPromotion` in the platform's `kernel/ports/require-admin.ts`,
|
|
67
|
+
// deliberately off the `./kernel` barrel because no module resolves it.
|
|
68
|
+
ctx.di.providePort('promoteAdminActor', ctx.asFunction(() => promoteAdminActor).singleton());
|
|
69
|
+
// The customer-side twin, and the last guard either composition root still
|
|
70
|
+
// declared for itself (issue #43). Both roots had one — reading different
|
|
71
|
+
// request properties and disagreeing on every request shape — while 16 route
|
|
72
|
+
// surfaces took production's and 11 the harness's. It reads nothing but the
|
|
73
|
+
// actor, so unlike the two above it closes over no collaborator.
|
|
74
|
+
ctx.di.providePort('requireCustomer', ctx.asFunction(() => createRequireCustomer()).singleton());
|
|
75
|
+
ctx.rootPlugin('decorates request.actor and request.adminActor, which every module’s route ' +
|
|
76
|
+
'guards read — a decoration applied inside an encapsulated context would ' +
|
|
77
|
+
'reach none of them', async (app) => {
|
|
78
|
+
const cradle = ctx.cradle();
|
|
79
|
+
await app.register(authPlugin, {
|
|
80
|
+
sessionService: cradle.sessionService,
|
|
81
|
+
// Read per request, so a resolver the root registers after this module
|
|
82
|
+
// composed is still found. Passing the cradle read rather than the
|
|
83
|
+
// value is what makes the late binding work without a mutable holder.
|
|
84
|
+
//
|
|
85
|
+
// The presence probe is D-44's `degrades-without` guard, and the `?.`
|
|
86
|
+
// beside it does **not** replace it: optional chaining defends against
|
|
87
|
+
// "nobody registered this name", while `apiKeyResolver` is a gated port
|
|
88
|
+
// and a closed gate throws. Without the probe an operator switching
|
|
89
|
+
// `api_keys` off would turn every API-key request into a 503 from the
|
|
90
|
+
// request hook instead of the declared degradation, which is that such
|
|
91
|
+
// a request is simply not authenticated — the same answer a request
|
|
92
|
+
// presenting no key at all gets.
|
|
93
|
+
apiKeyResolver: async (token) => {
|
|
94
|
+
if (!effectiveState.isPresent('api_keys'))
|
|
95
|
+
return null;
|
|
96
|
+
return (await cradle.apiKeyResolver?.(token)) ?? null;
|
|
97
|
+
},
|
|
98
|
+
customerOrgResolver: async (customerAccountId) => (await cradle.customerOrgResolver?.(customerAccountId)) ?? null,
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The module's persisted entity classes, on the `./backend` subpath, as one
|
|
104
|
+
* array and **no named class export** (D-168).
|
|
105
|
+
*
|
|
106
|
+
* This is the shape the platform reads when the package is *installed*: the
|
|
107
|
+
* boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
|
|
108
|
+
* and the static declaration reader (`scripts/lib/package-declarations.ts`),
|
|
109
|
+
* which is the third source of `check:module-boundary`'s `table→owner` map and
|
|
110
|
+
* the package pass of `check-entity-tenant-classification`. A missing array is
|
|
111
|
+
* answered with `[]` — zero entities registered, no error anywhere.
|
|
112
|
+
*/
|
|
113
|
+
export const entities = [Session];
|
|
114
|
+
/*
|
|
115
|
+
* **Actor promotion is no longer on this subpath**, and the deletion is the
|
|
116
|
+
* point rather than a tidy-up (`specs/117-instance-bring-up/` FR-030).
|
|
117
|
+
*
|
|
118
|
+
* `promoteAdminActor` was exported here for exactly one consumer, the
|
|
119
|
+
* production composition root, which calls it inside the actor bridge it
|
|
120
|
+
* contributes to `mfa`. The export said in its own words that it was published
|
|
121
|
+
* *"rather than relocated"* — correctly, because `auth` reads the function
|
|
122
|
+
* itself from `require-admin.ts` and promotion is about `request.actor` and
|
|
123
|
+
* `request.adminActor`, two decorations this module owns; `absolutizePublicUrl`
|
|
124
|
+
* could move to the platform (T040b, criterion 8) precisely because it had no
|
|
125
|
+
* consumer inside `email` and this one does. That reasoning is unchanged.
|
|
126
|
+
*
|
|
127
|
+
* What changed is the consumer. `specs/110-instance-repository/` T118 moves
|
|
128
|
+
* that root into `@endora-commerce/platform`, where importing a module is
|
|
129
|
+
* D-52/D-53's refusal — so the root's spelling had to stop being an import, and
|
|
130
|
+
* a value import does not retire by moving a type. It resolves the container
|
|
131
|
+
* name `promoteAdminActor` instead, which is the **further step** the old block
|
|
132
|
+
* here recorded as open and which `test/contract/kernel/harness-parity.test.ts`
|
|
133
|
+
* named as its drain condition: *"actor promotion published as a port, resolved
|
|
134
|
+
* from the container"*.
|
|
135
|
+
*
|
|
136
|
+
* The export is removed rather than left beside the registration, because two
|
|
137
|
+
* spellings of one seam is how a root comes to take the one that does not
|
|
138
|
+
* survive the move — and this file is where a future author would look for
|
|
139
|
+
* permission. The registration is above, in `registerModule`; the shape is
|
|
140
|
+
* `AdminActorPromotion` in the platform's `kernel/ports/require-admin.ts`.
|
|
141
|
+
*/
|
|
142
|
+
/**
|
|
143
|
+
* The module's own implementation classes and guard factories, on the
|
|
144
|
+
* `./backend` subpath.
|
|
145
|
+
*
|
|
146
|
+
* They are published for the reason `pim_ergonode` publishes
|
|
147
|
+
* `ErgonodeRequestError`: a consumer that needs the **class** must get the one
|
|
148
|
+
* the platform composed. `SessionService` reaches `Session` and
|
|
149
|
+
* `AuthSessionReadService` reaches it too, so a second copy of either is a
|
|
150
|
+
* second `Session` — a class the ORM never discovered, and `em.find` answers a
|
|
151
|
+
* `MetadataError` rather than rows (D-160.6.1). Publishing them is what lets a
|
|
152
|
+
* consumer name one copy instead of resolving a filesystem path into this
|
|
153
|
+
* package's source.
|
|
154
|
+
*
|
|
155
|
+
* No module reaches any of these; the consumers today are `backend/test/`,
|
|
156
|
+
* which is why they are not on a `./ports` subpath — that one is contract
|
|
157
|
+
* surface (D-171) and these are implementations.
|
|
158
|
+
*/
|
|
159
|
+
/**
|
|
160
|
+
* What `request.actor` **is** — and it is no longer this package's to say
|
|
161
|
+
* (`specs/110-instance-repository/` T118b).
|
|
162
|
+
*
|
|
163
|
+
* `plugin.ts` used to declare the four actor kinds and a
|
|
164
|
+
* `declare module 'fastify'` block adding `actor` and `adminActor` to
|
|
165
|
+
* `FastifyRequest`, and this barrel re-exported the five type names.
|
|
166
|
+
* `specs/117-instance-bring-up/` FR-030 published them for a real reason: an
|
|
167
|
+
* ambient augmentation reaches a consumer's program only if the file declaring
|
|
168
|
+
* it is *in* that program, and the production composition root was getting it
|
|
169
|
+
* incidentally through a **value** import of `promoteAdminActor`. Retiring that
|
|
170
|
+
* import took the augmentation with it and thirty reads of `request.actor`
|
|
171
|
+
* stopped compiling.
|
|
172
|
+
*
|
|
173
|
+
* The block said relocating the augmentation *"would take `Session` with it"*.
|
|
174
|
+
* That premise was false and is what T118b measured: across `packages/`,
|
|
175
|
+
* `backend/`, `admin/` and `storefront/`, `actor.session` was read in **zero**
|
|
176
|
+
* files outside this package. So the vocabulary is
|
|
177
|
+
* `@endora-commerce/contracts`' {@link Actor} — session-free, importing neither
|
|
178
|
+
* Fastify nor the ORM — and the augmentation is the platform's, on
|
|
179
|
+
* `@endora-commerce/platform/http`. A consumer names one of those two; neither
|
|
180
|
+
* is a boundary reach, and no consumer needs a type-only import of a module any
|
|
181
|
+
* more.
|
|
182
|
+
*
|
|
183
|
+
* The five names are **not** re-exported from here. A re-export of a
|
|
184
|
+
* `@endora-commerce/contracts` type on this package's `./backend` barrel is
|
|
185
|
+
* what `module-package-entity-surface.test.ts` refuses under D-168: it cannot
|
|
186
|
+
* follow the specifier, so whether a named entity class comes through it is
|
|
187
|
+
* unknown rather than false. That is the same rule that stopped this file
|
|
188
|
+
* re-exporting the two cookie names when the package landed.
|
|
189
|
+
*/
|
|
190
|
+
export { SessionService } from './services/session-service.js';
|
|
191
|
+
export { AuthSessionReadService, createAuthSessionPort } from './services/session-port.js';
|
|
192
|
+
export { createRequireAdmin, createRequireAdminAny } from './require-admin.js';
|
|
193
|
+
export { createRequireCustomer } from './require-customer.js';
|
|
194
|
+
//# 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,41 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
|
2
|
+
import '@endora-commerce/platform/http';
|
|
3
|
+
import type { SessionService } from './services/session-service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Authentication plugin. Resolves the caller's identity into one of four forms and
|
|
6
|
+
* exposes pre-handler factories that gate routes.
|
|
7
|
+
*
|
|
8
|
+
* See research.md R-11, R-12 and contracts/README.md § Authentication.
|
|
9
|
+
*/
|
|
10
|
+
export interface AuthPluginOptions {
|
|
11
|
+
sessionService: SessionService;
|
|
12
|
+
/** Resolves an API key bearer token to an ApiKey actor. Installed by the api_keys module. */
|
|
13
|
+
apiKeyResolver?: (token: string) => Promise<{
|
|
14
|
+
apiKeyId: string;
|
|
15
|
+
scopes: string[];
|
|
16
|
+
organizationId?: string | null;
|
|
17
|
+
salesChannelId?: string | null;
|
|
18
|
+
customerAccountId?: string | null;
|
|
19
|
+
} | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Resolves a customerAccountId to the customer's `organizationId`. Used to
|
|
22
|
+
* stamp `actor.organizationId` for customer + impersonation sessions so
|
|
23
|
+
* route handlers don't have to re-fetch the CustomerAccount per request.
|
|
24
|
+
*/
|
|
25
|
+
customerOrgResolver?: (customerAccountId: string) => Promise<string | null>;
|
|
26
|
+
/** Cookie name used to carry customer (storefront) sessions. */
|
|
27
|
+
cookieName?: string;
|
|
28
|
+
/** Cookie name used to carry admin (Admin UI) sessions. */
|
|
29
|
+
adminCookieName?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* If the request carries a valid admin session (resolved into `request.adminActor`
|
|
33
|
+
* by the auth plugin) but its ambient `request.actor` is not already an admin,
|
|
34
|
+
* promote the admin candidate. Admin guards call this so an admin route works
|
|
35
|
+
* even when a customer session is simultaneously present on the same request.
|
|
36
|
+
*/
|
|
37
|
+
export declare function promoteAdminActor(request: FastifyRequest): void;
|
|
38
|
+
declare function authPluginImpl(app: FastifyInstance, opts: AuthPluginOptions): Promise<void>;
|
|
39
|
+
export declare const authPlugin: typeof authPluginImpl;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAa/D,OAAO,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAEpE;;;;;GAKG;AAuBH,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,6FAA6F;IAC7F,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;QAC1C,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACnC,GAAG,IAAI,CAAC,CAAC;IACV;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5E,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAoBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAK/D;AAED,iBAAe,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuH1F;AAED,eAAO,MAAM,UAAU,uBAGrB,CAAC"}
|