@3sixty/api-auth-entity-storage-service 0.20.0-next.0 → 0.20.0-next.1
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/README.md +1 -1
- package/dist/es/entities/authenticationUser.js +1 -9
- package/dist/es/entities/authenticationUser.js.map +1 -1
- package/dist/es/entities/authenticationUserV0.js +69 -0
- package/dist/es/entities/authenticationUserV0.js.map +1 -0
- package/dist/es/index.js +1 -9
- package/dist/es/index.js.map +1 -1
- package/dist/es/models/IAuthHeaderProcessorConfig.js.map +1 -1
- package/dist/es/models/IAuthHeaderProcessorConstructorOptions.js.map +1 -1
- package/dist/es/models/IAuthTokenContext.js.map +1 -1
- package/dist/es/models/IEntityStorageAuthenticationAdminServiceConfig.js.map +1 -1
- package/dist/es/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.js.map +1 -1
- package/dist/es/models/IEntityStorageAuthenticationServiceConfig.js.map +1 -1
- package/dist/es/models/IEntityStorageAuthenticationServiceConstructorOptions.js.map +1 -1
- package/dist/es/processors/authHeaderProcessor.js +9 -14
- package/dist/es/processors/authHeaderProcessor.js.map +1 -1
- package/dist/es/restEntryPoints.js +1 -8
- package/dist/es/restEntryPoints.js.map +1 -1
- package/dist/es/routes/entityStorageAuthenticationAdminRoutes.js +22 -14
- package/dist/es/routes/entityStorageAuthenticationAdminRoutes.js.map +1 -1
- package/dist/es/routes/entityStorageAuthenticationRoutes.js +45 -1
- package/dist/es/routes/entityStorageAuthenticationRoutes.js.map +1 -1
- package/dist/es/schema.js +2 -4
- package/dist/es/schema.js.map +1 -1
- package/dist/es/services/entityStorageAuthenticationAdminService.js +69 -68
- package/dist/es/services/entityStorageAuthenticationAdminService.js.map +1 -1
- package/dist/es/services/entityStorageAuthenticationService.js +29 -17
- package/dist/es/services/entityStorageAuthenticationService.js.map +1 -1
- package/dist/es/utils/passwordHelper.js +7 -5
- package/dist/es/utils/passwordHelper.js.map +1 -1
- package/dist/es/utils/tokenHelper.js +2 -23
- package/dist/es/utils/tokenHelper.js.map +1 -1
- package/dist/types/entities/authenticationUser.d.ts +0 -4
- package/dist/types/entities/authenticationUserV0.d.ts +33 -0
- package/dist/types/index.d.ts +1 -9
- package/dist/types/models/IAuthHeaderProcessorConfig.d.ts +1 -2
- package/dist/types/models/IAuthTokenContext.d.ts +0 -4
- package/dist/types/models/IEntityStorageAuthenticationAdminServiceConfig.d.ts +14 -3
- package/dist/types/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.d.ts +6 -1
- package/dist/types/models/IEntityStorageAuthenticationServiceConfig.d.ts +7 -2
- package/dist/types/restEntryPoints.d.ts +1 -1
- package/dist/types/routes/entityStorageAuthenticationRoutes.d.ts +10 -2
- package/dist/types/services/entityStorageAuthenticationAdminService.d.ts +6 -2
- package/dist/types/services/entityStorageAuthenticationService.d.ts +6 -1
- package/dist/types/utils/passwordHelper.d.ts +6 -3
- package/dist/types/utils/tokenHelper.d.ts +2 -11
- package/docs/changelog.md +19 -0
- package/docs/examples.md +9 -93
- package/docs/open-api/spec.json +178 -956
- package/docs/reference/classes/AuthHeaderProcessor.md +4 -0
- package/docs/reference/classes/AuthenticationUser.md +0 -8
- package/docs/reference/classes/AuthenticationUserV0.md +69 -0
- package/docs/reference/classes/EntityStorageAuthenticationAdminService.md +11 -3
- package/docs/reference/classes/EntityStorageAuthenticationService.md +18 -0
- package/docs/reference/classes/PasswordHelper.md +4 -4
- package/docs/reference/classes/TokenHelper.md +2 -44
- package/docs/reference/functions/authenticationPasswordPolicy.md +31 -0
- package/docs/reference/index.md +2 -15
- package/docs/reference/interfaces/IAuthHeaderProcessorConfig.md +1 -2
- package/docs/reference/interfaces/IAuthTokenContext.md +0 -8
- package/docs/reference/interfaces/IEntityStorageAuthenticationAdminServiceConfig.md +38 -4
- package/docs/reference/interfaces/IEntityStorageAuthenticationAdminServiceConstructorOptions.md +15 -1
- package/docs/reference/interfaces/IEntityStorageAuthenticationServiceConfig.md +15 -1
- package/docs/reference/variables/restEntryPoints.md +1 -1
- package/locales/en.json +1 -16
- package/package.json +7 -8
- package/dist/es/entities/authenticationAuditEntry.js +0 -121
- package/dist/es/entities/authenticationAuditEntry.js.map +0 -1
- package/dist/es/entities/authenticationRateEntry.js +0 -37
- package/dist/es/entities/authenticationRateEntry.js.map +0 -1
- package/dist/es/models/IEntityStorageAuthenticationAuditServiceConfig.js +0 -4
- package/dist/es/models/IEntityStorageAuthenticationAuditServiceConfig.js.map +0 -1
- package/dist/es/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.js +0 -2
- package/dist/es/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.js.map +0 -1
- package/dist/es/models/IEntityStorageAuthenticationRateServiceConfig.js +0 -2
- package/dist/es/models/IEntityStorageAuthenticationRateServiceConfig.js.map +0 -1
- package/dist/es/models/IEntityStorageAuthenticationRateServiceConstructorOptions.js +0 -2
- package/dist/es/models/IEntityStorageAuthenticationRateServiceConstructorOptions.js.map +0 -1
- package/dist/es/routes/entityStorageAuthenticationAuditRoutes.js +0 -343
- package/dist/es/routes/entityStorageAuthenticationAuditRoutes.js.map +0 -1
- package/dist/es/services/entityStorageAuthenticationAuditService.js +0 -240
- package/dist/es/services/entityStorageAuthenticationAuditService.js.map +0 -1
- package/dist/es/services/entityStorageAuthenticationRateService.js +0 -213
- package/dist/es/services/entityStorageAuthenticationRateService.js.map +0 -1
- package/dist/types/entities/authenticationAuditEntry.d.ts +0 -49
- package/dist/types/entities/authenticationRateEntry.d.ts +0 -17
- package/dist/types/models/IEntityStorageAuthenticationAuditServiceConfig.d.ts +0 -9
- package/dist/types/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.d.ts +0 -15
- package/dist/types/models/IEntityStorageAuthenticationRateServiceConfig.d.ts +0 -10
- package/dist/types/models/IEntityStorageAuthenticationRateServiceConstructorOptions.d.ts +0 -25
- package/dist/types/routes/entityStorageAuthenticationAuditRoutes.d.ts +0 -54
- package/dist/types/services/entityStorageAuthenticationAuditService.d.ts +0 -72
- package/dist/types/services/entityStorageAuthenticationRateService.d.ts +0 -60
- package/docs/reference/classes/AuthenticationAuditEntry.md +0 -101
- package/docs/reference/classes/AuthenticationRateEntry.md +0 -37
- package/docs/reference/classes/EntityStorageAuthenticationAuditService.md +0 -241
- package/docs/reference/classes/EntityStorageAuthenticationRateService.md +0 -227
- package/docs/reference/functions/authenticationAuditCreate.md +0 -37
- package/docs/reference/functions/authenticationAuditGet.md +0 -31
- package/docs/reference/functions/authenticationAuditQuery.md +0 -31
- package/docs/reference/functions/authenticationAuditRemove.md +0 -31
- package/docs/reference/functions/authenticationAuditUpdate.md +0 -31
- package/docs/reference/functions/generateRestRoutesAuthenticationAudit.md +0 -25
- package/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConfig.md +0 -11
- package/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConstructorOptions.md +0 -25
- package/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConfig.md +0 -17
- package/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConstructorOptions.md +0 -53
- package/docs/reference/variables/tagsAuthenticationAudit.md +0 -5
package/README.md
CHANGED
|
@@ -22,4 +22,4 @@ The changes between each version can be found in [docs/changelog.md](docs/change
|
|
|
22
22
|
|
|
23
23
|
## Origin
|
|
24
24
|
|
|
25
|
-
This package is derived from the original [iotaledger/twin-api](https://github.com/iotaledger/twin-api/tree/next/packages/api-auth-entity-storage-service) repository.
|
|
25
|
+
This package is derived from the original [iotaledger/twin-api-auth](https://github.com/iotaledger/twin-api-auth/tree/next/packages/api-auth-entity-storage-service) repository.
|
|
@@ -25,10 +25,6 @@ let AuthenticationUser = class AuthenticationUser {
|
|
|
25
25
|
* The users organization.
|
|
26
26
|
*/
|
|
27
27
|
organization;
|
|
28
|
-
/**
|
|
29
|
-
* The scope assigned to the user, comma separated.
|
|
30
|
-
*/
|
|
31
|
-
scope;
|
|
32
28
|
/**
|
|
33
29
|
* The password version counter, incremented on every password change to invalidate existing tokens.
|
|
34
30
|
*/
|
|
@@ -54,16 +50,12 @@ __decorate([
|
|
|
54
50
|
property({ type: "string", maxLength: 255 }),
|
|
55
51
|
__metadata("design:type", String)
|
|
56
52
|
], AuthenticationUser.prototype, "organization", void 0);
|
|
57
|
-
__decorate([
|
|
58
|
-
property({ type: "string", maxLength: 4096 }),
|
|
59
|
-
__metadata("design:type", String)
|
|
60
|
-
], AuthenticationUser.prototype, "scope", void 0);
|
|
61
53
|
__decorate([
|
|
62
54
|
property({ type: "integer", optional: true }),
|
|
63
55
|
__metadata("design:type", Number)
|
|
64
56
|
], AuthenticationUser.prototype, "passwordVersion", void 0);
|
|
65
57
|
AuthenticationUser = __decorate([
|
|
66
|
-
entity()
|
|
58
|
+
entity({ version: 1 })
|
|
67
59
|
], AuthenticationUser);
|
|
68
60
|
export { AuthenticationUser };
|
|
69
61
|
//# sourceMappingURL=authenticationUser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticationUser.js","
|
|
1
|
+
{"version":3,"file":"authenticationUser.js","sources":["../../../src/entities/authenticationUser.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAElD;;GAEG;AAEI,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IAC9B;;OAEG;IAEI,KAAK,CAAU;IAEtB;;OAEG;IAEI,QAAQ,CAAU;IAEzB;;OAEG;IAEI,IAAI,CAAU;IAErB;;OAEG;IAEI,QAAQ,CAAU;IAEzB;;OAEG;IAEI,YAAY,CAAU;IAE7B;;OAEG;IAEI,eAAe,CAAU;CAChC,CAAA;AA/BO;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;;iDACzC;AAMf;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;oDACJ;AAMlB;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;gDACR;AAMd;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;;oDACvC;AAMlB;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;;wDAChB;AAMtB;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2DACd;AAnCpB,kBAAkB;IAD9B,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;GACV,kBAAkB,CAoC9B","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { entity, property } from \"@3sixty/entity\";\n\n/**\n * Class defining the storage for user login credentials.\n */\n@entity({ version: 1 })\nexport class AuthenticationUser {\n\t/**\n\t * The user e-mail address.\n\t */\n\t@property({ type: \"string\", format: \"email\", isPrimary: true })\n\tpublic email!: string;\n\n\t/**\n\t * The encrypted password for the user.\n\t */\n\t@property({ type: \"string\" })\n\tpublic password!: string;\n\n\t/**\n\t * The salt for the password.\n\t */\n\t@property({ type: \"string\" })\n\tpublic salt!: string;\n\n\t/**\n\t * The user identity.\n\t */\n\t@property({ type: \"string\", maxLength: 255, isSecondary: true })\n\tpublic identity!: string;\n\n\t/**\n\t * The users organization.\n\t */\n\t@property({ type: \"string\", maxLength: 255 })\n\tpublic organization!: string;\n\n\t/**\n\t * The password version counter, incremented on every password change to invalidate existing tokens.\n\t */\n\t@property({ type: \"integer\", optional: true })\n\tpublic passwordVersion?: number;\n}\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Copyright 2026 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { entity, property } from "@3sixty/entity";
|
|
4
|
+
/**
|
|
5
|
+
* Class defining the storage for user login credentials.
|
|
6
|
+
*/
|
|
7
|
+
let AuthenticationUserV0 = class AuthenticationUserV0 {
|
|
8
|
+
/**
|
|
9
|
+
* The user e-mail address.
|
|
10
|
+
*/
|
|
11
|
+
email;
|
|
12
|
+
/**
|
|
13
|
+
* The encrypted password for the user.
|
|
14
|
+
*/
|
|
15
|
+
password;
|
|
16
|
+
/**
|
|
17
|
+
* The salt for the password.
|
|
18
|
+
*/
|
|
19
|
+
salt;
|
|
20
|
+
/**
|
|
21
|
+
* The user identity.
|
|
22
|
+
*/
|
|
23
|
+
identity;
|
|
24
|
+
/**
|
|
25
|
+
* The users organization.
|
|
26
|
+
*/
|
|
27
|
+
organization;
|
|
28
|
+
/**
|
|
29
|
+
* The scope assigned to the user, comma separated.
|
|
30
|
+
*/
|
|
31
|
+
scope;
|
|
32
|
+
/**
|
|
33
|
+
* The password version counter, incremented on every password change to invalidate existing tokens.
|
|
34
|
+
*/
|
|
35
|
+
passwordVersion;
|
|
36
|
+
};
|
|
37
|
+
__decorate([
|
|
38
|
+
property({ type: "string", isPrimary: true }),
|
|
39
|
+
__metadata("design:type", String)
|
|
40
|
+
], AuthenticationUserV0.prototype, "email", void 0);
|
|
41
|
+
__decorate([
|
|
42
|
+
property({ type: "string" }),
|
|
43
|
+
__metadata("design:type", String)
|
|
44
|
+
], AuthenticationUserV0.prototype, "password", void 0);
|
|
45
|
+
__decorate([
|
|
46
|
+
property({ type: "string" }),
|
|
47
|
+
__metadata("design:type", String)
|
|
48
|
+
], AuthenticationUserV0.prototype, "salt", void 0);
|
|
49
|
+
__decorate([
|
|
50
|
+
property({ type: "string", isSecondary: true }),
|
|
51
|
+
__metadata("design:type", String)
|
|
52
|
+
], AuthenticationUserV0.prototype, "identity", void 0);
|
|
53
|
+
__decorate([
|
|
54
|
+
property({ type: "string" }),
|
|
55
|
+
__metadata("design:type", String)
|
|
56
|
+
], AuthenticationUserV0.prototype, "organization", void 0);
|
|
57
|
+
__decorate([
|
|
58
|
+
property({ type: "string" }),
|
|
59
|
+
__metadata("design:type", String)
|
|
60
|
+
], AuthenticationUserV0.prototype, "scope", void 0);
|
|
61
|
+
__decorate([
|
|
62
|
+
property({ type: "integer", optional: true }),
|
|
63
|
+
__metadata("design:type", Number)
|
|
64
|
+
], AuthenticationUserV0.prototype, "passwordVersion", void 0);
|
|
65
|
+
AuthenticationUserV0 = __decorate([
|
|
66
|
+
entity({ version: 0 })
|
|
67
|
+
], AuthenticationUserV0);
|
|
68
|
+
export { AuthenticationUserV0 };
|
|
69
|
+
//# sourceMappingURL=authenticationUserV0.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authenticationUserV0.js","sources":["../../../src/entities/authenticationUserV0.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAElD;;GAEG;AAEI,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAChC;;OAEG;IAEI,KAAK,CAAU;IAEtB;;OAEG;IAEI,QAAQ,CAAU;IAEzB;;OAEG;IAEI,IAAI,CAAU;IAErB;;OAEG;IAEI,QAAQ,CAAU;IAEzB;;OAEG;IAEI,YAAY,CAAU;IAE7B;;OAEG;IAEI,KAAK,CAAU;IAEtB;;OAEG;IAEI,eAAe,CAAU;CAChC,CAAA;AArCO;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;;mDACxB;AAMf;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;sDACJ;AAMlB;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;kDACR;AAMd;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;;sDACvB;AAMlB;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;0DACA;AAMtB;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;mDACP;AAMf;IADN,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6DACd;AAzCpB,oBAAoB;IADhC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;GACV,oBAAoB,CA0ChC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { entity, property } from \"@3sixty/entity\";\n\n/**\n * Class defining the storage for user login credentials.\n */\n@entity({ version: 0 })\nexport class AuthenticationUserV0 {\n\t/**\n\t * The user e-mail address.\n\t */\n\t@property({ type: \"string\", isPrimary: true })\n\tpublic email!: string;\n\n\t/**\n\t * The encrypted password for the user.\n\t */\n\t@property({ type: \"string\" })\n\tpublic password!: string;\n\n\t/**\n\t * The salt for the password.\n\t */\n\t@property({ type: \"string\" })\n\tpublic salt!: string;\n\n\t/**\n\t * The user identity.\n\t */\n\t@property({ type: \"string\", isSecondary: true })\n\tpublic identity!: string;\n\n\t/**\n\t * The users organization.\n\t */\n\t@property({ type: \"string\" })\n\tpublic organization!: string;\n\n\t/**\n\t * The scope assigned to the user, comma separated.\n\t */\n\t@property({ type: \"string\" })\n\tpublic scope!: string;\n\n\t/**\n\t * The password version counter, incremented on every password change to invalidate existing tokens.\n\t */\n\t@property({ type: \"integer\", optional: true })\n\tpublic passwordVersion?: number;\n}\n"]}
|
package/dist/es/index.js
CHANGED
|
@@ -1,28 +1,20 @@
|
|
|
1
1
|
// Copyright 2024 IOTA Stiftung.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
-
export * from "./entities/authenticationAuditEntry.js";
|
|
4
|
-
export * from "./entities/authenticationRateEntry.js";
|
|
5
3
|
export * from "./entities/authenticationUser.js";
|
|
4
|
+
export * from "./entities/authenticationUserV0.js";
|
|
6
5
|
export * from "./models/IAuthHeaderProcessorConfig.js";
|
|
7
6
|
export * from "./models/IAuthHeaderProcessorConstructorOptions.js";
|
|
8
7
|
export * from "./models/IAuthTokenContext.js";
|
|
9
8
|
export * from "./models/IEntityStorageAuthenticationAdminServiceConfig.js";
|
|
10
9
|
export * from "./models/IEntityStorageAuthenticationAdminServiceConstructorOptions.js";
|
|
11
|
-
export * from "./models/IEntityStorageAuthenticationAuditServiceConfig.js";
|
|
12
|
-
export * from "./models/IEntityStorageAuthenticationAuditServiceConstructorOptions.js";
|
|
13
|
-
export * from "./models/IEntityStorageAuthenticationRateServiceConfig.js";
|
|
14
|
-
export * from "./models/IEntityStorageAuthenticationRateServiceConstructorOptions.js";
|
|
15
10
|
export * from "./models/IEntityStorageAuthenticationServiceConfig.js";
|
|
16
11
|
export * from "./models/IEntityStorageAuthenticationServiceConstructorOptions.js";
|
|
17
12
|
export * from "./processors/authHeaderProcessor.js";
|
|
18
13
|
export * from "./restEntryPoints.js";
|
|
19
14
|
export * from "./routes/entityStorageAuthenticationAdminRoutes.js";
|
|
20
|
-
export * from "./routes/entityStorageAuthenticationAuditRoutes.js";
|
|
21
15
|
export * from "./routes/entityStorageAuthenticationRoutes.js";
|
|
22
16
|
export * from "./schema.js";
|
|
23
17
|
export * from "./services/entityStorageAuthenticationAdminService.js";
|
|
24
|
-
export * from "./services/entityStorageAuthenticationAuditService.js";
|
|
25
|
-
export * from "./services/entityStorageAuthenticationRateService.js";
|
|
26
18
|
export * from "./services/entityStorageAuthenticationService.js";
|
|
27
19
|
export * from "./utils/tokenHelper.js";
|
|
28
20
|
export * from "./utils/passwordHelper.js";
|
package/dist/es/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,kCAAkC,CAAC;AACjD,cAAc,oCAAoC,CAAC;AACnD,cAAc,wCAAwC,CAAC;AACvD,cAAc,oDAAoD,CAAC;AACnE,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4DAA4D,CAAC;AAC3E,cAAc,wEAAwE,CAAC;AACvF,cAAc,uDAAuD,CAAC;AACtE,cAAc,mEAAmE,CAAC;AAClF,cAAc,qCAAqC,CAAC;AACpD,cAAc,sBAAsB,CAAC;AACrC,cAAc,oDAAoD,CAAC;AACnE,cAAc,+CAA+C,CAAC;AAC9D,cAAc,aAAa,CAAC;AAC5B,cAAc,uDAAuD,CAAC;AACtE,cAAc,kDAAkD,CAAC;AACjE,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./entities/authenticationUser.js\";\nexport * from \"./entities/authenticationUserV0.js\";\nexport * from \"./models/IAuthHeaderProcessorConfig.js\";\nexport * from \"./models/IAuthHeaderProcessorConstructorOptions.js\";\nexport * from \"./models/IAuthTokenContext.js\";\nexport * from \"./models/IEntityStorageAuthenticationAdminServiceConfig.js\";\nexport * from \"./models/IEntityStorageAuthenticationAdminServiceConstructorOptions.js\";\nexport * from \"./models/IEntityStorageAuthenticationServiceConfig.js\";\nexport * from \"./models/IEntityStorageAuthenticationServiceConstructorOptions.js\";\nexport * from \"./processors/authHeaderProcessor.js\";\nexport * from \"./restEntryPoints.js\";\nexport * from \"./routes/entityStorageAuthenticationAdminRoutes.js\";\nexport * from \"./routes/entityStorageAuthenticationRoutes.js\";\nexport * from \"./schema.js\";\nexport * from \"./services/entityStorageAuthenticationAdminService.js\";\nexport * from \"./services/entityStorageAuthenticationService.js\";\nexport * from \"./utils/tokenHelper.js\";\nexport * from \"./utils/passwordHelper.js\";\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IAuthHeaderProcessorConfig.js","
|
|
1
|
+
{"version":3,"file":"IAuthHeaderProcessorConfig.js","sources":["../../../src/models/IAuthHeaderProcessorConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the authentication header processor\n */\nexport interface IAuthHeaderProcessorConfig {\n\t/**\n\t * The name of the key to retrieve from the vault for signing JWT.\n\t * @default auth-signing\n\t */\n\tsigningKeyName?: string;\n\n\t/**\n\t * The name of the cookie to use for the token.\n\t * @default access_token\n\t */\n\tcookieName?: string;\n\n\t/**\n\t * Include the stack with errors.\n\t */\n\tincludeErrorStack?: boolean;\n\n\t/**\n\t * The time in milliseconds a verified token is kept in the in-memory cache, counted from when it\n\t * was verified and not extended by use, set to 0 to disable caching. A hit skips the vault\n\t * signature check, the tenant lookup and the user lookup, so this is also the longest a password\n\t * change, a user removal or a signing key rotation can go unnoticed. An entry never outlives the\n\t * expiry claim of its own token either.\n\t * @default 30000\n\t */\n\ttokenCacheTtlMs?: number;\n\n\t/**\n\t * The maximum number of verified tokens to hold in the in-memory cache.\n\t * @default undefined (LfuCache default)\n\t */\n\ttokenCacheCapacity?: number;\n\n\t/**\n\t * Maximum time in milliseconds to wait for the token cache mutex during a cache population.\n\t * @default undefined (LfuCache default)\n\t */\n\ttokenCacheMutexTimeoutMs?: number;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IAuthHeaderProcessorConstructorOptions.js","
|
|
1
|
+
{"version":3,"file":"IAuthHeaderProcessorConstructorOptions.js","sources":["../../../src/models/IAuthHeaderProcessorConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IAuthHeaderProcessorConfig } from \"./IAuthHeaderProcessorConfig.js\";\n\n/**\n * Options for the AuthHeaderProcessor constructor.\n */\nexport interface IAuthHeaderProcessorConstructorOptions {\n\t/**\n\t * The entity storage for users.\n\t * @default authentication-user\n\t */\n\tuserEntityStorageType?: string;\n\n\t/**\n\t * The vault for the private keys.\n\t * @default vault\n\t */\n\tvaultConnectorType?: string;\n\n\t/**\n\t * The component to retrieve tenant information.\n\t * @default tenant-admin\n\t */\n\ttenantAdminComponentType?: string;\n\n\t/**\n\t * The configuration for the processor.\n\t */\n\tconfig?: IAuthHeaderProcessorConfig;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IAuthTokenContext.js","
|
|
1
|
+
{"version":3,"file":"IAuthTokenContext.js","sources":["../../../src/models/IAuthTokenContext.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The context resolved from an access token, everything a request needs that does not depend on\n * the route being called.\n */\nexport interface IAuthTokenContext {\n\t/**\n\t * The tenant id from the token, if it carried one.\n\t */\n\ttenantId?: string;\n\n\t/**\n\t * The organization id of the tenant the token belongs to.\n\t */\n\ttenantOrganizationId?: string;\n\n\t/**\n\t * The public origin of the tenant the token belongs to.\n\t */\n\ttenantPublicOrigin?: string;\n\n\t/**\n\t * The identity of the verified user.\n\t */\n\tuserIdentity?: string;\n\n\t/**\n\t * The organization of the verified user.\n\t */\n\tuserOrganization?: string;\n\n\t/**\n\t * The expiry of the token in milliseconds since the epoch, if it carried one.\n\t */\n\texpires?: number;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IEntityStorageAuthenticationAdminServiceConfig.js","
|
|
1
|
+
{"version":3,"file":"IEntityStorageAuthenticationAdminServiceConfig.js","sources":["../../../src/models/IEntityStorageAuthenticationAdminServiceConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the entity storage authentication admin service.\n */\nexport interface IEntityStorageAuthenticationAdminServiceConfig {\n\t/**\n\t * The minimum password length.\n\t * @default 15\n\t */\n\tminPasswordLength?: number;\n\n\t/**\n\t * The maximum password length.\n\t * @default 128\n\t */\n\tmaxPasswordLength?: number;\n\n\t/**\n\t * The role that grants escalated privilege, only callers holding it can modify accounts that hold it.\n\t * @default global-admin\n\t */\n\tescalatedPrivilegeRole?: string;\n\n\t/**\n\t * The authorization model ID used to look up roles.\n\t * @default system\n\t */\n\tauthorizationModelId?: string;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IEntityStorageAuthenticationAdminServiceConstructorOptions.js","
|
|
1
|
+
{"version":3,"file":"IEntityStorageAuthenticationAdminServiceConstructorOptions.js","sources":["../../../src/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IEntityStorageAuthenticationAdminServiceConfig } from \"./IEntityStorageAuthenticationAdminServiceConfig.js\";\n\n/**\n * Options for the EntityStorageAuthenticationAdminService constructor.\n */\nexport interface IEntityStorageAuthenticationAdminServiceConstructorOptions {\n\t/**\n\t * The entity storage for the users.\n\t * @default authentication-user\n\t */\n\tuserEntityStorageType?: string;\n\n\t/**\n\t * The audit component.\n\t * @default authentication-audit\n\t */\n\tauthenticationAuditServiceType?: string;\n\n\t/**\n\t * The authorization component used to guard accounts with escalated privilege.\n\t * @default authorization\n\t */\n\tauthorizationComponentType?: string;\n\n\t/**\n\t * The configuration for the authentication.\n\t */\n\tconfig?: IEntityStorageAuthenticationAdminServiceConfig;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IEntityStorageAuthenticationServiceConfig.js","
|
|
1
|
+
{"version":3,"file":"IEntityStorageAuthenticationServiceConfig.js","sources":["../../../src/models/IEntityStorageAuthenticationServiceConfig.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IAuthenticationRateActionConfig } from \"@3sixty/api-auth-models\";\n\n/**\n * Configuration for the entity storage authentication service.\n */\nexport interface IEntityStorageAuthenticationServiceConfig {\n\t/**\n\t * The name of the key to retrieve from the vault for signing JWT.\n\t * @default auth-signing\n\t */\n\tsigningKeyName?: string;\n\n\t/**\n\t * The default time to live for the JWT.\n\t * @default 60\n\t */\n\tdefaultTtlMinutes?: number;\n\n\t/**\n\t * The minimum password length for new password validation.\n\t * @default 15\n\t */\n\tminPasswordLength?: number;\n\n\t/**\n\t * The maximum password length for new password validation.\n\t * @default 128\n\t */\n\tmaxPasswordLength?: number;\n\n\t/**\n\t * Optional override for login failure rate limit.\n\t * @default { maxAttempts: 5, windowMinutes: 15 }\n\t */\n\tloginRateLimit?: IAuthenticationRateActionConfig;\n\n\t/**\n\t * Optional override for password change rate limit.\n\t * @default { maxAttempts: 5, windowMinutes: 15 }\n\t */\n\tpasswordChangeRateLimit?: IAuthenticationRateActionConfig;\n\n\t/**\n\t * Optional override for token refresh rate limit.\n\t * @default { maxAttempts: 30, windowMinutes: 60 }\n\t */\n\ttokenRefreshRateLimit?: IAuthenticationRateActionConfig;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IEntityStorageAuthenticationServiceConstructorOptions.js","
|
|
1
|
+
{"version":3,"file":"IEntityStorageAuthenticationServiceConstructorOptions.js","sources":["../../../src/models/IEntityStorageAuthenticationServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IEntityStorageAuthenticationServiceConfig } from \"./IEntityStorageAuthenticationServiceConfig.js\";\n\n/**\n * Options for the EntityStorageAuthenticationService constructor.\n */\nexport interface IEntityStorageAuthenticationServiceConstructorOptions {\n\t/**\n\t * The entity storage for the users.\n\t * @default authentication-user\n\t */\n\tuserEntityStorageType?: string;\n\n\t/**\n\t * The vault for the private keys.\n\t * @default vault\n\t */\n\tvaultConnectorType?: string;\n\n\t/**\n\t * The audit service.\n\t * @default authentication-audit\n\t */\n\tauthenticationAuditServiceType?: string;\n\n\t/**\n\t * The rate service.\n\t * @default authentication-rate\n\t */\n\tauthenticationRateServiceType?: string;\n\n\t/**\n\t * The component to retrieve tenant information.\n\t * @default tenant-admin\n\t */\n\ttenantAdminComponentType?: string;\n\n\t/**\n\t * The configuration for the authentication.\n\t */\n\tconfig?: IEntityStorageAuthenticationServiceConfig;\n}\n"]}
|
|
@@ -57,25 +57,25 @@ export class AuthHeaderProcessor {
|
|
|
57
57
|
*/
|
|
58
58
|
_includeErrorStack;
|
|
59
59
|
/**
|
|
60
|
-
* The
|
|
60
|
+
* The node identity.
|
|
61
61
|
* @internal
|
|
62
62
|
*/
|
|
63
|
-
|
|
63
|
+
_nodeId;
|
|
64
64
|
/**
|
|
65
|
-
*
|
|
65
|
+
* The organization ID for the single-tenant node, cached at startup.
|
|
66
66
|
* @internal
|
|
67
67
|
*/
|
|
68
|
-
|
|
68
|
+
_nodeOrganizationId;
|
|
69
69
|
/**
|
|
70
|
-
* The
|
|
70
|
+
* The cache of contexts resolved from verified tokens, undefined when caching is disabled.
|
|
71
71
|
* @internal
|
|
72
72
|
*/
|
|
73
|
-
|
|
73
|
+
_tokenCache;
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
75
|
+
* How long a cached token context may live, counted from when it was resolved.
|
|
76
76
|
* @internal
|
|
77
77
|
*/
|
|
78
|
-
|
|
78
|
+
_tokenCacheTtlMs;
|
|
79
79
|
/**
|
|
80
80
|
* Create a new instance of AuthHeaderProcessor.
|
|
81
81
|
* @param options Options for the processor.
|
|
@@ -139,9 +139,6 @@ export class AuthHeaderProcessor {
|
|
|
139
139
|
}
|
|
140
140
|
const tokenAndLocation = TokenHelper.extractTokenFromHeaders(request.headers, this._cookieName);
|
|
141
141
|
const tokenContext = await this.resolveTokenContext(this._nodeId, tokenAndLocation?.token, contextIds);
|
|
142
|
-
// The scopes a route demands vary from route to route, so this is checked for every
|
|
143
|
-
// request and never forms part of the cached context.
|
|
144
|
-
TokenHelper.verifyScopes(tokenContext.scope, route.requiredScope);
|
|
145
142
|
contextIds[ContextIdKeys.Tenant] = tokenContext.tenantId;
|
|
146
143
|
// In a multi-tenant environment the tenant organization ID is authoritative,
|
|
147
144
|
// in a single-tenant environment we fall back to the node organization ID.
|
|
@@ -149,7 +146,6 @@ export class AuthHeaderProcessor {
|
|
|
149
146
|
tokenContext.tenantOrganizationId ?? this._nodeOrganizationId;
|
|
150
147
|
contextIds[ContextIdKeys.User] = tokenContext.userIdentity;
|
|
151
148
|
contextIds[ContextIdKeys.UserOrganization] = tokenContext.userOrganization;
|
|
152
|
-
contextIds[HttpContextIdKeys.Scope] = tokenContext.scope;
|
|
153
149
|
// If the tenant has a custom public origin, we set it in the context for downstream processors to use.
|
|
154
150
|
if (Is.stringValue(tokenContext.tenantPublicOrigin)) {
|
|
155
151
|
contextIds[HttpContextIdKeys.PublicOrigin] = tokenContext.tenantPublicOrigin;
|
|
@@ -235,7 +231,7 @@ export class AuthHeaderProcessor {
|
|
|
235
231
|
let tenantId;
|
|
236
232
|
let tenantOrganizationId;
|
|
237
233
|
let tenantPublicOrigin;
|
|
238
|
-
const { payload } = await TokenHelper.verify(this._vaultConnector, nodeId, this._signingKeyName, token,
|
|
234
|
+
const { payload } = await TokenHelper.verify(this._vaultConnector, nodeId, this._signingKeyName, token, async (sub, org, tid, passwordVersion) => {
|
|
239
235
|
const validParts = [];
|
|
240
236
|
tenantId = tid;
|
|
241
237
|
if (Is.stringValue(tenantId)) {
|
|
@@ -269,7 +265,6 @@ export class AuthHeaderProcessor {
|
|
|
269
265
|
tenantPublicOrigin,
|
|
270
266
|
userIdentity: user?.identity,
|
|
271
267
|
userOrganization: user?.organization,
|
|
272
|
-
scope: Coerce.string(payload.scope),
|
|
273
268
|
expires: Is.integer(expiresSeconds) ? expiresSeconds * 1000 : undefined
|
|
274
269
|
};
|
|
275
270
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authHeaderProcessor.js","sourceRoot":"","sources":["../../../src/processors/authHeaderProcessor.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EACN,iBAAiB,EACjB,eAAe,EAMf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAoB,MAAM,iBAAiB,CAAC;AACnG,OAAO,EACN,SAAS,EACT,MAAM,EACN,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,EAAE,EACF,QAAQ,EACR,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EACN,6BAA6B,EAE7B,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,qBAAqB,EAAwB,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIxE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD;;GAEG;AACH,MAAM,OAAO,mBAAmB;IAC/B;;;OAGG;IACI,MAAM,CAAU,mBAAmB,GAAW,cAAc,CAAC;IAEpE;;OAEG;IACI,MAAM,CAAU,UAAU,yBAAyC;IAE1E;;;OAGG;IACK,MAAM,CAAU,2BAA2B,GAAW,KAAK,CAAC;IAEpE;;;OAGG;IACc,eAAe,CAAkB;IAElD;;;OAGG;IACc,qBAAqB,CAAyB;IAE/D;;;OAGG;IACc,kBAAkB,CAA8C;IAEjF;;;OAGG;IACc,eAAe,CAAS;IAEzC;;;OAGG;IACc,WAAW,CAAS;IAErC;;;OAGG;IACc,kBAAkB,CAAU;IAE7C;;;OAGG;IACc,WAAW,CAA+B;IAE3D;;;OAGG;IACc,gBAAgB,CAAS;IAE1C;;;OAGG;IACK,OAAO,CAAU;IAEzB;;;OAGG;IACK,mBAAmB,CAAU;IAErC;;;OAGG;IACH,YAAY,OAAgD;QAC3D,IAAI,CAAC,eAAe,GAAG,qBAAqB,CAAC,GAAG,CAAC,OAAO,EAAE,kBAAkB,IAAI,OAAO,CAAC,CAAC;QACzF,IAAI,CAAC,kBAAkB,GAAG,6BAA6B,CAAC,GAAG,CAC1D,OAAO,EAAE,qBAAqB,IAAI,qBAAqB,CACvD,CAAC;QACF,IAAI,CAAC,qBAAqB,GAAG,gBAAgB,CAAC,WAAW,CACxD,OAAO,EAAE,wBAAwB,IAAI,cAAc,CACnD,CAAC;QAEF,IAAI,CAAC,eAAe,GAAG,OAAO,EAAE,MAAM,EAAE,cAAc,IAAI,cAAc,CAAC;QACzE,IAAI,CAAC,WAAW,GAAG,OAAO,EAAE,MAAM,EAAE,UAAU,IAAI,mBAAmB,CAAC,mBAAmB,CAAC;QAC1F,IAAI,CAAC,kBAAkB,GAAG,OAAO,EAAE,MAAM,EAAE,iBAAiB,IAAI,KAAK,CAAC;QAEtE,IAAI,CAAC,gBAAgB;YACpB,OAAO,EAAE,MAAM,EAAE,eAAe,IAAI,mBAAmB,CAAC,2BAA2B,CAAC;QACrF,IAAI,IAAI,CAAC,gBAAgB,GAAG,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,WAAW,GAAG,IAAI,QAAQ,CAAoB;gBAClD,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,kBAAkB;gBAC7C,KAAK,EAAE,IAAI,CAAC,gBAAgB;gBAC5B,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,wBAAwB;aACzD,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,mBAAmB,CAAC,UAAU,CAAC;IACvC,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,KAAK,CAAC,wBAAiC;QACnD,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC;QACxD,eAAe,CAAC,KAAK,CAAC,UAAU,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,CAAC,mBAAmB,GAAG,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IACnE,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,IAAI;QAChB,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC;IAC7B,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,GAAG,CACf,OAA2B,EAC3B,QAAuB,EACvB,KAA6B,EAC7B,UAAuB,EACvB,cAAyC;QAEzC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,CAAC;YACpD,IAAI,CAAC;gBACJ,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;oBACnC,MAAM,IAAI,YAAY,CAAC,mBAAmB,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;gBACxE,CAAC;gBAED,MAAM,gBAAgB,GAAG,WAAW,CAAC,uBAAuB,CAC3D,OAAO,CAAC,OAAO,EACf,IAAI,CAAC,WAAW,CAChB,CAAC;gBAEF,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAClD,IAAI,CAAC,OAAO,EACZ,gBAAgB,EAAE,KAAK,EACvB,UAAU,CACV,CAAC;gBAEF,oFAAoF;gBACpF,sDAAsD;gBACtD,WAAW,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;gBAElE,UAAU,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,YAAY,CAAC,QAAQ,CAAC;gBACzD,6EAA6E;gBAC7E,2EAA2E;gBAC3E,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC;oBACrC,YAAY,CAAC,oBAAoB,IAAI,IAAI,CAAC,mBAAmB,CAAC;gBAC/D,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,YAAY,CAAC;gBAC3D,UAAU,CAAC,aAAa,CAAC,gBAAgB,CAAC,GAAG,YAAY,CAAC,gBAAgB,CAAC;gBAC3E,UAAU,CAAC,iBAAiB,CAAC,KAAK,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC;gBAEzD,uGAAuG;gBACvG,IAAI,EAAE,CAAC,WAAW,CAAC,YAAY,CAAC,kBAAkB,CAAC,EAAE,CAAC;oBACrD,UAAU,CAAC,iBAAiB,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC,kBAAkB,CAAC;gBAC9E,CAAC;gBAED,cAAc,CAAC,SAAS,GAAG,gBAAgB,EAAE,KAAK,CAAC;gBACnD,cAAc,CAAC,iBAAiB,GAAG,gBAAgB,EAAE,QAAQ,CAAC;YAC/D,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACvC,eAAe,CAAC,aAAa,CAC5B,QAAQ,EACR,KAAK,EACL,cAAc,CAAC,YAAY,EAC3B,IAAI,CAAC,kBAAkB,CACvB,CAAC;YACH,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,IAAI,CAChB,OAA2B,EAC3B,QAAuB,EACvB,KAA6B,EAC7B,UAAuB,EACvB,cAAyC;QAEzC,MAAM,qBAAqB,GAAG,cAAc,EAAE,aAAa,CAAC;QAC5D,MAAM,iBAAiB,GAAG,cAAc,EAAE,SAAS,CAAC;QAEpD,yFAAyF;QACzF,IACC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC;YAChB,EAAE,CAAC,WAAW,CAAC,qBAAqB,CAAC;YACrC,cAAc,CAAC,iBAAiB,KAAK,eAAe,EACnD,CAAC;YACF,IACC,CAAC,qBAAqB,KAAK,OAAO,IAAI,qBAAqB,KAAK,SAAS,CAAC;gBAC1E,EAAE,CAAC,WAAW,CAAC,iBAAiB,CAAC,EAChC,CAAC;gBACF,QAAQ,CAAC,OAAO,KAAK,EAAE,CAAC;gBACxB,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,GAAG,YAAY,CAAC,YAAY,CAClE,IAAI,CAAC,WAAW,EAChB,iBAAiB,EACjB;oBACC,MAAM,EAAE,IAAI;oBACZ,QAAQ,EAAE,IAAI;oBACd,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,GAAG;iBACT,CACD,CAAC;YACH,CAAC;iBAAM,IAAI,qBAAqB,KAAK,QAAQ,EAAE,CAAC;gBAC/C,QAAQ,CAAC,OAAO,KAAK,EAAE,CAAC;gBACxB,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,GAAG,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE;oBACrF,MAAM,EAAE,IAAI;oBACZ,QAAQ,EAAE,IAAI;oBACd,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,GAAG;iBACT,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,mBAAmB,CAChC,MAAc,EACd,KAAyB,EACzB,UAAuB;QAEvB,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QACpD,CAAC;QAED,+DAA+D;QAC/D,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAEpF,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CACnD,QAAQ,EACR,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,EACvD,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAClC,CAAC;QAEF,0DAA0D;QAC1D,IAAI,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC5E,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAClC,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,YAAY,CAAC;IACrB,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,WAAW,CACxB,MAAc,EACd,KAAyB,EACzB,UAAuB;QAEvB,IAAI,IAAoC,CAAC;QACzC,IAAI,QAA4B,CAAC;QACjC,IAAI,oBAAwC,CAAC;QAC7C,IAAI,kBAAsC,CAAC;QAE3C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,WAAW,CAAC,MAAM,CAC3C,IAAI,CAAC,eAAe,EACpB,MAAM,EACN,IAAI,CAAC,eAAe,EACpB,KAAK,EACL,SAAS,EACT,KAAK,EACJ,GAAW,EACX,GAAW,EACX,GAAuB,EACvB,eAAmC,EAClC,EAAE;YACH,MAAM,UAAU,GAAG,EAAE,CAAC;YAEtB,QAAQ,GAAG,GAAG,CAAC;YAEf,IAAI,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,qBAAqB,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;gBAC/D,IAAI,MAAM,EAAE,EAAE,KAAK,QAAQ,EAAE,CAAC;oBAC7B,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAC1B,oBAAoB,GAAG,MAAM,CAAC,cAAc,CAAC;oBAC7C,kBAAkB,GAAG,MAAM,CAAC,YAAY,CAAC;gBAC1C,CAAC;YACF,CAAC;YAED,kEAAkE;YAClE,mCAAmC;YACnC,MAAM,uBAAuB,GAAG;gBAC/B,GAAG,UAAU;gBACb,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,GAAG;aAC3B,CAAC;YAEF,8FAA8F;YAC9F,IAAI,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE,CACnE,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAC5C,CAAC;YAEF,IAAI,IAAI,EAAE,QAAQ,KAAK,GAAG,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC,EAAE,CAAC;gBACtF,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACzB,CAAC;YACD,IAAI,IAAI,EAAE,YAAY,KAAK,GAAG,EAAE,CAAC;gBAChC,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACjC,CAAC;YAED,OAAO,UAAU,CAAC;QACnB,CAAC,CACD,CAAC;QAEF,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAEnD,OAAO;YACN,QAAQ;YACR,oBAAoB;YACpB,kBAAkB;YAClB,YAAY,EAAE,IAAI,EAAE,QAAQ;YAC5B,gBAAgB,EAAE,IAAI,EAAE,YAAY;YACpC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YACnC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS;SACvE,CAAC;IACH,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tHttpContextIdKeys,\n\tHttpErrorHelper,\n\ttype IBaseRoute,\n\ttype IBaseRouteProcessor,\n\ttype IHttpResponse,\n\ttype IHttpServerRequest,\n\ttype ITenantAdminComponent\n} from \"@3sixty/api-models\";\nimport { ContextIdHelper, ContextIdKeys, ContextIdStore, type IContextIds } from \"@3sixty/context\";\nimport {\n\tBaseError,\n\tCoerce,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tIs,\n\tLfuCache\n} from \"@3sixty/core\";\nimport { Blake2b } from \"@3sixty/crypto\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@3sixty/entity-storage-models\";\nimport { nameof } from \"@3sixty/nameof\";\nimport { VaultConnectorFactory, type IVaultConnector } from \"@3sixty/vault-models\";\nimport { CookieHelper, HeaderTypes, HttpStatusCode } from \"@3sixty/web\";\nimport type { AuthenticationUser } from \"../entities/authenticationUser.js\";\nimport type { IAuthHeaderProcessorConstructorOptions } from \"../models/IAuthHeaderProcessorConstructorOptions.js\";\nimport type { IAuthTokenContext } from \"../models/IAuthTokenContext.js\";\nimport { TokenHelper } from \"../utils/tokenHelper.js\";\n\n/**\n * Handle a JWT token in the authorization header or cookies and validate it to populate request context identity.\n */\nexport class AuthHeaderProcessor implements IBaseRouteProcessor {\n\t/**\n\t * The default name for the access token as a cookie.\n\t * @internal\n\t */\n\tpublic static readonly DEFAULT_COOKIE_NAME: string = \"access_token\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<AuthHeaderProcessor>();\n\n\t/**\n\t * The default time to keep a verified token in the cache.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TOKEN_CACHE_TTL_MS: number = 30000;\n\n\t/**\n\t * The vault for the keys.\n\t * @internal\n\t */\n\tprivate readonly _vaultConnector: IVaultConnector;\n\n\t/**\n\t * The component to retrieve tenant information.\n\t * @internal\n\t */\n\tprivate readonly _tenantAdminComponent?: ITenantAdminComponent;\n\n\t/**\n\t * The entity storage for users.\n\t * @internal\n\t */\n\tprivate readonly _userEntityStorage: IEntityStorageConnector<AuthenticationUser>;\n\n\t/**\n\t * The name of the key to retrieve from the vault for signing JWT.\n\t * @internal\n\t */\n\tprivate readonly _signingKeyName: string;\n\n\t/**\n\t * The name of the cookie to use for the token.\n\t * @internal\n\t */\n\tprivate readonly _cookieName: string;\n\n\t/**\n\t * Include the stack with errors.\n\t * @internal\n\t */\n\tprivate readonly _includeErrorStack: boolean;\n\n\t/**\n\t * The cache of contexts resolved from verified tokens, undefined when caching is disabled.\n\t * @internal\n\t */\n\tprivate readonly _tokenCache?: LfuCache<IAuthTokenContext>;\n\n\t/**\n\t * How long a cached token context may live, counted from when it was resolved.\n\t * @internal\n\t */\n\tprivate readonly _tokenCacheTtlMs: number;\n\n\t/**\n\t * The node identity.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * The organization ID for the single-tenant node, cached at startup.\n\t * @internal\n\t */\n\tprivate _nodeOrganizationId?: string;\n\n\t/**\n\t * Create a new instance of AuthHeaderProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: IAuthHeaderProcessorConstructorOptions) {\n\t\tthis._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? \"vault\");\n\t\tthis._userEntityStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.userEntityStorageType ?? \"authentication-user\"\n\t\t);\n\t\tthis._tenantAdminComponent = ComponentFactory.getIfExists<ITenantAdminComponent>(\n\t\t\toptions?.tenantAdminComponentType ?? \"tenant-admin\"\n\t\t);\n\n\t\tthis._signingKeyName = options?.config?.signingKeyName ?? \"auth-signing\";\n\t\tthis._cookieName = options?.config?.cookieName ?? AuthHeaderProcessor.DEFAULT_COOKIE_NAME;\n\t\tthis._includeErrorStack = options?.config?.includeErrorStack ?? false;\n\n\t\tthis._tokenCacheTtlMs =\n\t\t\toptions?.config?.tokenCacheTtlMs ?? AuthHeaderProcessor._DEFAULT_TOKEN_CACHE_TTL_MS;\n\t\tif (this._tokenCacheTtlMs > 0) {\n\t\t\tthis._tokenCache = new LfuCache<IAuthTokenContext>({\n\t\t\t\tcapacity: options?.config?.tokenCacheCapacity,\n\t\t\t\tttiMs: this._tokenCacheTtlMs,\n\t\t\t\tmutexTimeoutMs: options?.config?.tokenCacheMutexTimeoutMs\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn AuthHeaderProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * The service needs to be started when the application is initialized.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns A promise that resolves when the node identity and organization ID have been cached.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.Node);\n\t\tthis._nodeId = contextIds[ContextIdKeys.Node];\n\t\tthis._nodeOrganizationId = contextIds[ContextIdKeys.Organization];\n\t}\n\n\t/**\n\t * Stop the processor and release the token cache.\n\t * @returns A promise that resolves when the processor has stopped.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis._tokenCache?.destroy();\n\t}\n\n\t/**\n\t * Pre process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t * @returns A promise that resolves when the JWT has been verified and the context populated, or an error response set.\n\t */\n\tpublic async pre(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tif (!Is.empty(route) && !(route.skipAuth ?? false)) {\n\t\t\ttry {\n\t\t\t\tif (!Is.stringValue(this._nodeId)) {\n\t\t\t\t\tthrow new GeneralError(AuthHeaderProcessor.CLASS_NAME, \"nodeIdNotSet\");\n\t\t\t\t}\n\n\t\t\t\tconst tokenAndLocation = TokenHelper.extractTokenFromHeaders(\n\t\t\t\t\trequest.headers,\n\t\t\t\t\tthis._cookieName\n\t\t\t\t);\n\n\t\t\t\tconst tokenContext = await this.resolveTokenContext(\n\t\t\t\t\tthis._nodeId,\n\t\t\t\t\ttokenAndLocation?.token,\n\t\t\t\t\tcontextIds\n\t\t\t\t);\n\n\t\t\t\t// The scopes a route demands vary from route to route, so this is checked for every\n\t\t\t\t// request and never forms part of the cached context.\n\t\t\t\tTokenHelper.verifyScopes(tokenContext.scope, route.requiredScope);\n\n\t\t\t\tcontextIds[ContextIdKeys.Tenant] = tokenContext.tenantId;\n\t\t\t\t// In a multi-tenant environment the tenant organization ID is authoritative,\n\t\t\t\t// in a single-tenant environment we fall back to the node organization ID.\n\t\t\t\tcontextIds[ContextIdKeys.Organization] =\n\t\t\t\t\ttokenContext.tenantOrganizationId ?? this._nodeOrganizationId;\n\t\t\t\tcontextIds[ContextIdKeys.User] = tokenContext.userIdentity;\n\t\t\t\tcontextIds[ContextIdKeys.UserOrganization] = tokenContext.userOrganization;\n\t\t\t\tcontextIds[HttpContextIdKeys.Scope] = tokenContext.scope;\n\n\t\t\t\t// If the tenant has a custom public origin, we set it in the context for downstream processors to use.\n\t\t\t\tif (Is.stringValue(tokenContext.tenantPublicOrigin)) {\n\t\t\t\t\tcontextIds[HttpContextIdKeys.PublicOrigin] = tokenContext.tenantPublicOrigin;\n\t\t\t\t}\n\n\t\t\t\tprocessorState.authToken = tokenAndLocation?.token;\n\t\t\t\tprocessorState.authTokenLocation = tokenAndLocation?.location;\n\t\t\t} catch (err) {\n\t\t\t\tconst error = BaseError.fromError(err);\n\t\t\t\tHttpErrorHelper.buildResponse(\n\t\t\t\t\tresponse,\n\t\t\t\t\terror,\n\t\t\t\t\tHttpStatusCode.unauthorized,\n\t\t\t\t\tthis._includeErrorStack\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Post process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t * @returns A promise that resolves when the Set-Cookie header has been applied to the response if required.\n\t */\n\tpublic async post(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tconst responseAuthOperation = processorState?.authOperation;\n\t\tconst responseAuthToken = processorState?.authToken;\n\n\t\t// We don't populate the cookie if the incoming request was from an authorization header.\n\t\tif (\n\t\t\t!Is.empty(route) &&\n\t\t\tIs.stringValue(responseAuthOperation) &&\n\t\t\tprocessorState.authTokenLocation !== \"authorization\"\n\t\t) {\n\t\t\tif (\n\t\t\t\t(responseAuthOperation === \"login\" || responseAuthOperation === \"refresh\") &&\n\t\t\t\tIs.stringValue(responseAuthToken)\n\t\t\t) {\n\t\t\t\tresponse.headers ??= {};\n\t\t\t\tresponse.headers[HeaderTypes.SetCookie] = CookieHelper.createCookie(\n\t\t\t\t\tthis._cookieName,\n\t\t\t\t\tresponseAuthToken,\n\t\t\t\t\t{\n\t\t\t\t\t\tsecure: true,\n\t\t\t\t\t\thttpOnly: true,\n\t\t\t\t\t\tsameSite: \"None\",\n\t\t\t\t\t\tpath: \"/\"\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t} else if (responseAuthOperation === \"logout\") {\n\t\t\t\tresponse.headers ??= {};\n\t\t\t\tresponse.headers[HeaderTypes.SetCookie] = CookieHelper.deleteCookie(this._cookieName, {\n\t\t\t\t\tsecure: true,\n\t\t\t\t\thttpOnly: true,\n\t\t\t\t\tsameSite: \"None\",\n\t\t\t\t\tpath: \"/\"\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the route independent context for a token, through the cache when one is enabled.\n\t * @param nodeId The node identifier the token must have been issued by.\n\t * @param token The token from the request, if there was one.\n\t * @param contextIds The context IDs of the request.\n\t * @returns The resolved context.\n\t * @internal\n\t */\n\tprivate async resolveTokenContext(\n\t\tnodeId: string,\n\t\ttoken: string | undefined,\n\t\tcontextIds: IContextIds\n\t): Promise<IAuthTokenContext> {\n\t\tif (Is.empty(this._tokenCache) || !Is.stringValue(token)) {\n\t\t\treturn this.verifyToken(nodeId, token, contextIds);\n\t\t}\n\n\t\t// Use hash of token so we don't store the raw token in memory.\n\t\tconst cacheKey = Converter.bytesToHex(Blake2b.sum256(Converter.utf8ToBytes(token)));\n\n\t\tconst tokenContext = await this._tokenCache.getOrSet(\n\t\t\tcacheKey,\n\t\t\tasync () => this.verifyToken(nodeId, token, contextIds),\n\t\t\tDate.now() + this._tokenCacheTtlMs\n\t\t);\n\n\t\t// A token can expire while its context sits in the cache.\n\t\tif (Is.integer(tokenContext.expires) && tokenContext.expires <= Date.now()) {\n\t\t\tthis._tokenCache.delete(cacheKey);\n\t\t\treturn this.verifyToken(nodeId, token, contextIds);\n\t\t}\n\n\t\treturn tokenContext;\n\t}\n\n\t/**\n\t * Verify a token against the vault and resolve the tenant and user it refers to.\n\t * @param nodeId The node identifier the token must have been issued by.\n\t * @param token The token from the request, if there was one.\n\t * @param contextIds The context IDs of the request.\n\t * @returns The resolved context.\n\t * @internal\n\t */\n\tprivate async verifyToken(\n\t\tnodeId: string,\n\t\ttoken: string | undefined,\n\t\tcontextIds: IContextIds\n\t): Promise<IAuthTokenContext> {\n\t\tlet user: AuthenticationUser | undefined;\n\t\tlet tenantId: string | undefined;\n\t\tlet tenantOrganizationId: string | undefined;\n\t\tlet tenantPublicOrigin: string | undefined;\n\n\t\tconst { payload } = await TokenHelper.verify(\n\t\t\tthis._vaultConnector,\n\t\t\tnodeId,\n\t\t\tthis._signingKeyName,\n\t\t\ttoken,\n\t\t\tundefined,\n\t\t\tasync (\n\t\t\t\tsub: string,\n\t\t\t\torg: string,\n\t\t\t\ttid: string | undefined,\n\t\t\t\tpasswordVersion: number | undefined\n\t\t\t) => {\n\t\t\t\tconst validParts = [];\n\n\t\t\t\ttenantId = tid;\n\n\t\t\t\tif (Is.stringValue(tenantId)) {\n\t\t\t\t\tconst tenant = await this._tenantAdminComponent?.get(tenantId);\n\t\t\t\t\tif (tenant?.id === tenantId) {\n\t\t\t\t\t\tvalidParts.push(\"tenant\");\n\t\t\t\t\t\ttenantOrganizationId = tenant.organizationId;\n\t\t\t\t\t\ttenantPublicOrigin = tenant.publicOrigin;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// We use the tenant id from the token, if the user is not in that\n\t\t\t\t// partition then the get will fail\n\t\t\t\tconst contextIdsForUserLookup = {\n\t\t\t\t\t...contextIds,\n\t\t\t\t\t[ContextIdKeys.Tenant]: tid\n\t\t\t\t};\n\n\t\t\t\t// Wrap the user lookup in the request context so partitioned storage uses the correct tenant.\n\t\t\t\tuser = await ContextIdStore.run(contextIdsForUserLookup, async () =>\n\t\t\t\t\tthis._userEntityStorage.get(sub, \"identity\")\n\t\t\t\t);\n\n\t\t\t\tif (user?.identity === sub && (passwordVersion ?? 0) === (user.passwordVersion ?? 0)) {\n\t\t\t\t\tvalidParts.push(\"user\");\n\t\t\t\t}\n\t\t\t\tif (user?.organization === org) {\n\t\t\t\t\tvalidParts.push(\"organization\");\n\t\t\t\t}\n\n\t\t\t\treturn validParts;\n\t\t\t}\n\t\t);\n\n\t\tconst expiresSeconds = Coerce.integer(payload.exp);\n\n\t\treturn {\n\t\t\ttenantId,\n\t\t\ttenantOrganizationId,\n\t\t\ttenantPublicOrigin,\n\t\t\tuserIdentity: user?.identity,\n\t\t\tuserOrganization: user?.organization,\n\t\t\tscope: Coerce.string(payload.scope),\n\t\t\texpires: Is.integer(expiresSeconds) ? expiresSeconds * 1000 : undefined\n\t\t};\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"authHeaderProcessor.js","sources":["../../../src/processors/authHeaderProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tHttpContextIdKeys,\n\tHttpErrorHelper,\n\ttype IBaseRoute,\n\ttype IBaseRouteProcessor,\n\ttype IHttpResponse,\n\ttype IHttpServerRequest,\n\ttype ITenantAdminComponent\n} from \"@3sixty/api-models\";\nimport { ContextIdHelper, ContextIdKeys, ContextIdStore, type IContextIds } from \"@3sixty/context\";\nimport {\n\tBaseError,\n\tCoerce,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tIs,\n\tLfuCache\n} from \"@3sixty/core\";\nimport { Blake2b } from \"@3sixty/crypto\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@3sixty/entity-storage-models\";\n\nimport { VaultConnectorFactory, type IVaultConnector } from \"@3sixty/vault-models\";\nimport { CookieHelper, HeaderTypes, HttpStatusCode } from \"@3sixty/web\";\nimport type { AuthenticationUser } from \"../entities/authenticationUser.js\";\nimport type { IAuthHeaderProcessorConstructorOptions } from \"../models/IAuthHeaderProcessorConstructorOptions.js\";\nimport type { IAuthTokenContext } from \"../models/IAuthTokenContext.js\";\nimport { TokenHelper } from \"../utils/tokenHelper.js\";\n\n/**\n * Handle a JWT token in the authorization header or cookies and validate it to populate request context identity.\n */\nexport class AuthHeaderProcessor implements IBaseRouteProcessor {\n\t/**\n\t * The default name for the access token as a cookie.\n\t * @internal\n\t */\n\tpublic static readonly DEFAULT_COOKIE_NAME: string = \"access_token\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = \"AuthHeaderProcessor\";\n\n\t/**\n\t * The default time to keep a verified token in the cache.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TOKEN_CACHE_TTL_MS: number = 30000;\n\n\t/**\n\t * The vault for the keys.\n\t * @internal\n\t */\n\tprivate readonly _vaultConnector: IVaultConnector;\n\n\t/**\n\t * The component to retrieve tenant information.\n\t * @internal\n\t */\n\tprivate readonly _tenantAdminComponent?: ITenantAdminComponent;\n\n\t/**\n\t * The entity storage for users.\n\t * @internal\n\t */\n\tprivate readonly _userEntityStorage: IEntityStorageConnector<AuthenticationUser>;\n\n\t/**\n\t * The name of the key to retrieve from the vault for signing JWT.\n\t * @internal\n\t */\n\tprivate readonly _signingKeyName: string;\n\n\t/**\n\t * The name of the cookie to use for the token.\n\t * @internal\n\t */\n\tprivate readonly _cookieName: string;\n\n\t/**\n\t * Include the stack with errors.\n\t * @internal\n\t */\n\tprivate readonly _includeErrorStack: boolean;\n\n\t/**\n\t * The node identity.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * The organization ID for the single-tenant node, cached at startup.\n\t * @internal\n\t */\n\tprivate _nodeOrganizationId?: string;\n\n\t/**\n\t * The cache of contexts resolved from verified tokens, undefined when caching is disabled.\n\t * @internal\n\t */\n\tprivate readonly _tokenCache?: LfuCache<IAuthTokenContext>;\n\n\t/**\n\t * How long a cached token context may live, counted from when it was resolved.\n\t * @internal\n\t */\n\tprivate readonly _tokenCacheTtlMs: number;\n\n\t/**\n\t * Create a new instance of AuthHeaderProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: IAuthHeaderProcessorConstructorOptions) {\n\t\tthis._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? \"vault\");\n\t\tthis._userEntityStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.userEntityStorageType ?? \"authentication-user\"\n\t\t);\n\t\tthis._tenantAdminComponent = ComponentFactory.getIfExists<ITenantAdminComponent>(\n\t\t\toptions?.tenantAdminComponentType ?? \"tenant-admin\"\n\t\t);\n\n\t\tthis._signingKeyName = options?.config?.signingKeyName ?? \"auth-signing\";\n\t\tthis._cookieName = options?.config?.cookieName ?? AuthHeaderProcessor.DEFAULT_COOKIE_NAME;\n\t\tthis._includeErrorStack = options?.config?.includeErrorStack ?? false;\n\n\t\tthis._tokenCacheTtlMs =\n\t\t\toptions?.config?.tokenCacheTtlMs ?? AuthHeaderProcessor._DEFAULT_TOKEN_CACHE_TTL_MS;\n\t\tif (this._tokenCacheTtlMs > 0) {\n\t\t\tthis._tokenCache = new LfuCache<IAuthTokenContext>({\n\t\t\t\tcapacity: options?.config?.tokenCacheCapacity,\n\t\t\t\tttiMs: this._tokenCacheTtlMs,\n\t\t\t\tmutexTimeoutMs: options?.config?.tokenCacheMutexTimeoutMs\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn AuthHeaderProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * The service needs to be started when the application is initialized.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns A promise that resolves when the node identity and organization ID have been cached.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.Node);\n\t\tthis._nodeId = contextIds[ContextIdKeys.Node];\n\t\tthis._nodeOrganizationId = contextIds[ContextIdKeys.Organization];\n\t}\n\n\t/**\n\t * Stop the processor and release the token cache.\n\t * @returns A promise that resolves when the processor has stopped.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis._tokenCache?.destroy();\n\t}\n\n\t/**\n\t * Pre process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t * @returns A promise that resolves when the JWT has been verified and the context populated, or an error response set.\n\t */\n\tpublic async pre(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tif (!Is.empty(route) && !(route.skipAuth ?? false)) {\n\t\t\ttry {\n\t\t\t\tif (!Is.stringValue(this._nodeId)) {\n\t\t\t\t\tthrow new GeneralError(AuthHeaderProcessor.CLASS_NAME, \"nodeIdNotSet\");\n\t\t\t\t}\n\n\t\t\t\tconst tokenAndLocation = TokenHelper.extractTokenFromHeaders(\n\t\t\t\t\trequest.headers,\n\t\t\t\t\tthis._cookieName\n\t\t\t\t);\n\n\t\t\t\tconst tokenContext = await this.resolveTokenContext(\n\t\t\t\t\tthis._nodeId,\n\t\t\t\t\ttokenAndLocation?.token,\n\t\t\t\t\tcontextIds\n\t\t\t\t);\n\n\t\t\t\tcontextIds[ContextIdKeys.Tenant] = tokenContext.tenantId;\n\t\t\t\t// In a multi-tenant environment the tenant organization ID is authoritative,\n\t\t\t\t// in a single-tenant environment we fall back to the node organization ID.\n\t\t\t\tcontextIds[ContextIdKeys.Organization] =\n\t\t\t\t\ttokenContext.tenantOrganizationId ?? this._nodeOrganizationId;\n\t\t\t\tcontextIds[ContextIdKeys.User] = tokenContext.userIdentity;\n\t\t\t\tcontextIds[ContextIdKeys.UserOrganization] = tokenContext.userOrganization;\n\n\t\t\t\t// If the tenant has a custom public origin, we set it in the context for downstream processors to use.\n\t\t\t\tif (Is.stringValue(tokenContext.tenantPublicOrigin)) {\n\t\t\t\t\tcontextIds[HttpContextIdKeys.PublicOrigin] = tokenContext.tenantPublicOrigin;\n\t\t\t\t}\n\n\t\t\t\tprocessorState.authToken = tokenAndLocation?.token;\n\t\t\t\tprocessorState.authTokenLocation = tokenAndLocation?.location;\n\t\t\t} catch (err) {\n\t\t\t\tconst error = BaseError.fromError(err);\n\t\t\t\tHttpErrorHelper.buildResponse(\n\t\t\t\t\tresponse,\n\t\t\t\t\terror,\n\t\t\t\t\tHttpStatusCode.unauthorized,\n\t\t\t\t\tthis._includeErrorStack\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Post process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t * @returns A promise that resolves when the Set-Cookie header has been applied to the response if required.\n\t */\n\tpublic async post(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tconst responseAuthOperation = processorState?.authOperation;\n\t\tconst responseAuthToken = processorState?.authToken;\n\n\t\t// We don't populate the cookie if the incoming request was from an authorization header.\n\t\tif (\n\t\t\t!Is.empty(route) &&\n\t\t\tIs.stringValue(responseAuthOperation) &&\n\t\t\tprocessorState.authTokenLocation !== \"authorization\"\n\t\t) {\n\t\t\tif (\n\t\t\t\t(responseAuthOperation === \"login\" || responseAuthOperation === \"refresh\") &&\n\t\t\t\tIs.stringValue(responseAuthToken)\n\t\t\t) {\n\t\t\t\tresponse.headers ??= {};\n\t\t\t\tresponse.headers[HeaderTypes.SetCookie] = CookieHelper.createCookie(\n\t\t\t\t\tthis._cookieName,\n\t\t\t\t\tresponseAuthToken,\n\t\t\t\t\t{\n\t\t\t\t\t\tsecure: true,\n\t\t\t\t\t\thttpOnly: true,\n\t\t\t\t\t\tsameSite: \"None\",\n\t\t\t\t\t\tpath: \"/\"\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t} else if (responseAuthOperation === \"logout\") {\n\t\t\t\tresponse.headers ??= {};\n\t\t\t\tresponse.headers[HeaderTypes.SetCookie] = CookieHelper.deleteCookie(this._cookieName, {\n\t\t\t\t\tsecure: true,\n\t\t\t\t\thttpOnly: true,\n\t\t\t\t\tsameSite: \"None\",\n\t\t\t\t\tpath: \"/\"\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the route independent context for a token, through the cache when one is enabled.\n\t * @param nodeId The node identifier the token must have been issued by.\n\t * @param token The token from the request, if there was one.\n\t * @param contextIds The context IDs of the request.\n\t * @returns The resolved context.\n\t * @internal\n\t */\n\tprivate async resolveTokenContext(\n\t\tnodeId: string,\n\t\ttoken: string | undefined,\n\t\tcontextIds: IContextIds\n\t): Promise<IAuthTokenContext> {\n\t\tif (Is.empty(this._tokenCache) || !Is.stringValue(token)) {\n\t\t\treturn this.verifyToken(nodeId, token, contextIds);\n\t\t}\n\n\t\t// Use hash of token so we don't store the raw token in memory.\n\t\tconst cacheKey = Converter.bytesToHex(Blake2b.sum256(Converter.utf8ToBytes(token)));\n\n\t\tconst tokenContext = await this._tokenCache.getOrSet(\n\t\t\tcacheKey,\n\t\t\tasync () => this.verifyToken(nodeId, token, contextIds),\n\t\t\tDate.now() + this._tokenCacheTtlMs\n\t\t);\n\n\t\t// A token can expire while its context sits in the cache.\n\t\tif (Is.integer(tokenContext.expires) && tokenContext.expires <= Date.now()) {\n\t\t\tthis._tokenCache.delete(cacheKey);\n\t\t\treturn this.verifyToken(nodeId, token, contextIds);\n\t\t}\n\n\t\treturn tokenContext;\n\t}\n\n\t/**\n\t * Verify a token against the vault and resolve the tenant and user it refers to.\n\t * @param nodeId The node identifier the token must have been issued by.\n\t * @param token The token from the request, if there was one.\n\t * @param contextIds The context IDs of the request.\n\t * @returns The resolved context.\n\t * @internal\n\t */\n\tprivate async verifyToken(\n\t\tnodeId: string,\n\t\ttoken: string | undefined,\n\t\tcontextIds: IContextIds\n\t): Promise<IAuthTokenContext> {\n\t\tlet user: AuthenticationUser | undefined;\n\t\tlet tenantId: string | undefined;\n\t\tlet tenantOrganizationId: string | undefined;\n\t\tlet tenantPublicOrigin: string | undefined;\n\n\t\tconst { payload } = await TokenHelper.verify(\n\t\t\tthis._vaultConnector,\n\t\t\tnodeId,\n\t\t\tthis._signingKeyName,\n\t\t\ttoken,\n\t\t\tasync (\n\t\t\t\tsub: string,\n\t\t\t\torg: string,\n\t\t\t\ttid: string | undefined,\n\t\t\t\tpasswordVersion: number | undefined\n\t\t\t) => {\n\t\t\t\tconst validParts = [];\n\n\t\t\t\ttenantId = tid;\n\n\t\t\t\tif (Is.stringValue(tenantId)) {\n\t\t\t\t\tconst tenant = await this._tenantAdminComponent?.get(tenantId);\n\t\t\t\t\tif (tenant?.id === tenantId) {\n\t\t\t\t\t\tvalidParts.push(\"tenant\");\n\t\t\t\t\t\ttenantOrganizationId = tenant.organizationId;\n\t\t\t\t\t\ttenantPublicOrigin = tenant.publicOrigin;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// We use the tenant id from the token, if the user is not in that\n\t\t\t\t// partition then the get will fail\n\t\t\t\tconst contextIdsForUserLookup = {\n\t\t\t\t\t...contextIds,\n\t\t\t\t\t[ContextIdKeys.Tenant]: tid\n\t\t\t\t};\n\n\t\t\t\t// Wrap the user lookup in the request context so partitioned storage uses the correct tenant.\n\t\t\t\tuser = await ContextIdStore.run(contextIdsForUserLookup, async () =>\n\t\t\t\t\tthis._userEntityStorage.get(sub, \"identity\")\n\t\t\t\t);\n\n\t\t\t\tif (user?.identity === sub && (passwordVersion ?? 0) === (user.passwordVersion ?? 0)) {\n\t\t\t\t\tvalidParts.push(\"user\");\n\t\t\t\t}\n\t\t\t\tif (user?.organization === org) {\n\t\t\t\t\tvalidParts.push(\"organization\");\n\t\t\t\t}\n\n\t\t\t\treturn validParts;\n\t\t\t}\n\t\t);\n\n\t\tconst expiresSeconds = Coerce.integer(payload.exp);\n\n\t\treturn {\n\t\t\ttenantId,\n\t\t\ttenantOrganizationId,\n\t\t\ttenantPublicOrigin,\n\t\t\tuserIdentity: user?.identity,\n\t\t\tuserOrganization: user?.organization,\n\t\t\texpires: Is.integer(expiresSeconds) ? expiresSeconds * 1000 : undefined\n\t\t};\n\t}\n}\n"]}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { generateRestRoutesAuthenticationAdmin, tagsAuthenticationAdmin } from "./routes/entityStorageAuthenticationAdminRoutes.js";
|
|
2
|
-
import { generateRestRoutesAuthenticationAudit, tagsAuthenticationAudit } from "./routes/entityStorageAuthenticationAuditRoutes.js";
|
|
3
2
|
import { generateRestRoutesAuthentication, tagsAuthentication } from "./routes/entityStorageAuthenticationRoutes.js";
|
|
4
3
|
/**
|
|
5
|
-
* REST entry points for the authentication
|
|
4
|
+
* REST entry points for the authentication and authentication admin services.
|
|
6
5
|
*/
|
|
7
6
|
export const restEntryPoints = [
|
|
8
7
|
{
|
|
@@ -16,12 +15,6 @@ export const restEntryPoints = [
|
|
|
16
15
|
defaultBaseRoute: "authentication/admin",
|
|
17
16
|
tags: tagsAuthenticationAdmin,
|
|
18
17
|
generateRoutes: generateRestRoutesAuthenticationAdmin
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
name: "authenticationAudit",
|
|
22
|
-
defaultBaseRoute: "authentication/audit",
|
|
23
|
-
tags: tagsAuthenticationAudit,
|
|
24
|
-
generateRoutes: generateRestRoutesAuthenticationAudit
|
|
25
18
|
}
|
|
26
19
|
];
|
|
27
20
|
//# sourceMappingURL=restEntryPoints.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"restEntryPoints.js","
|
|
1
|
+
{"version":3,"file":"restEntryPoints.js","sources":["../../src/restEntryPoints.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,qCAAqC,EACrC,uBAAuB,EACvB,MAAM,oDAAoD,CAAC;AAC5D,OAAO,EACN,gCAAgC,EAChC,kBAAkB,EAClB,MAAM,+CAA+C,CAAC;AAEvD;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAA2B;IACtD;QACC,IAAI,EAAE,gBAAgB;QACtB,gBAAgB,EAAE,gBAAgB;QAClC,IAAI,EAAE,kBAAkB;QACxB,cAAc,EAAE,gCAAgC;KAChD;IACD;QACC,IAAI,EAAE,qBAAqB;QAC3B,gBAAgB,EAAE,sBAAsB;QACxC,IAAI,EAAE,uBAAuB;QAC7B,cAAc,EAAE,qCAAqC;KACrD;CACD,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IRestRouteEntryPoint } from \"@3sixty/api-models\";\nimport {\n\tgenerateRestRoutesAuthenticationAdmin,\n\ttagsAuthenticationAdmin\n} from \"./routes/entityStorageAuthenticationAdminRoutes.js\";\nimport {\n\tgenerateRestRoutesAuthentication,\n\ttagsAuthentication\n} from \"./routes/entityStorageAuthenticationRoutes.js\";\n\n/**\n * REST entry points for the authentication and authentication admin services.\n */\nexport const restEntryPoints: IRestRouteEntryPoint[] = [\n\t{\n\t\tname: \"authentication\",\n\t\tdefaultBaseRoute: \"authentication\",\n\t\ttags: tagsAuthentication,\n\t\tgenerateRoutes: generateRestRoutesAuthentication\n\t},\n\t{\n\t\tname: \"authenticationAdmin\",\n\t\tdefaultBaseRoute: \"authentication/admin\",\n\t\ttags: tagsAuthenticationAdmin,\n\t\tgenerateRoutes: generateRestRoutesAuthenticationAdmin\n\t}\n];\n"]}
|
|
@@ -6,6 +6,18 @@ import { HttpStatusCode } from "@3sixty/web";
|
|
|
6
6
|
* The source used when communicating about these routes.
|
|
7
7
|
*/
|
|
8
8
|
const ROUTES_SOURCE = "authenticationAdminRoutes";
|
|
9
|
+
/**
|
|
10
|
+
* The default authorization for the routes, used to seed authorization rules.
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_AUTHORIZATION_READER = {
|
|
13
|
+
permission: "user-admin:read",
|
|
14
|
+
role: "user-admin"
|
|
15
|
+
};
|
|
16
|
+
const DEFAULT_AUTHORIZATION_WRITER = {
|
|
17
|
+
permission: "user-admin:write",
|
|
18
|
+
role: "user-admin",
|
|
19
|
+
inherits: [DEFAULT_AUTHORIZATION_READER.permission]
|
|
20
|
+
};
|
|
9
21
|
/**
|
|
10
22
|
* The tag to associate with the routes.
|
|
11
23
|
*/
|
|
@@ -40,8 +52,7 @@ export function generateRestRoutesAuthenticationAdmin(baseRouteName, componentNa
|
|
|
40
52
|
email: "user@example.com",
|
|
41
53
|
password: "MyPassword123!",
|
|
42
54
|
userIdentity: "did:example:123456789abcdefghi",
|
|
43
|
-
organizationIdentity: "did:example:123456789abcdefghi"
|
|
44
|
-
scope: ["scope1", "scope2"]
|
|
55
|
+
organizationIdentity: "did:example:123456789abcdefghi"
|
|
45
56
|
}
|
|
46
57
|
}
|
|
47
58
|
}
|
|
@@ -55,7 +66,7 @@ export function generateRestRoutesAuthenticationAdmin(baseRouteName, componentNa
|
|
|
55
66
|
type: "IUnauthorizedResponse"
|
|
56
67
|
}
|
|
57
68
|
],
|
|
58
|
-
|
|
69
|
+
defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER
|
|
59
70
|
};
|
|
60
71
|
const updateUserRoute = {
|
|
61
72
|
operationId: "authenticationAdminUpdateUser",
|
|
@@ -76,8 +87,7 @@ export function generateRestRoutesAuthenticationAdmin(baseRouteName, componentNa
|
|
|
76
87
|
},
|
|
77
88
|
body: {
|
|
78
89
|
userIdentity: "did:example:123456789abcdefghi",
|
|
79
|
-
organizationIdentity: "did:example:123456789abcdefghi"
|
|
80
|
-
scope: ["scope1", "scope2"]
|
|
90
|
+
organizationIdentity: "did:example:123456789abcdefghi"
|
|
81
91
|
}
|
|
82
92
|
}
|
|
83
93
|
}
|
|
@@ -91,7 +101,7 @@ export function generateRestRoutesAuthenticationAdmin(baseRouteName, componentNa
|
|
|
91
101
|
type: "IUnauthorizedResponse"
|
|
92
102
|
}
|
|
93
103
|
],
|
|
94
|
-
|
|
104
|
+
defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER
|
|
95
105
|
};
|
|
96
106
|
const updateUserPasswordRoute = {
|
|
97
107
|
operationId: "authenticationAdminUpdateUserPassword",
|
|
@@ -125,7 +135,7 @@ export function generateRestRoutesAuthenticationAdmin(baseRouteName, componentNa
|
|
|
125
135
|
type: "IUnauthorizedResponse"
|
|
126
136
|
}
|
|
127
137
|
],
|
|
128
|
-
|
|
138
|
+
defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER
|
|
129
139
|
};
|
|
130
140
|
const getUserRoute = {
|
|
131
141
|
operationId: "authenticationAdminGetUser",
|
|
@@ -159,8 +169,7 @@ export function generateRestRoutesAuthenticationAdmin(baseRouteName, componentNa
|
|
|
159
169
|
body: {
|
|
160
170
|
email: "user@example.com",
|
|
161
171
|
userIdentity: "did:example:123456789abcdefghi",
|
|
162
|
-
organizationIdentity: "did:example:123456789abcdefghi"
|
|
163
|
-
scope: ["scope1", "scope2"]
|
|
172
|
+
organizationIdentity: "did:example:123456789abcdefghi"
|
|
164
173
|
}
|
|
165
174
|
}
|
|
166
175
|
}
|
|
@@ -170,7 +179,7 @@ export function generateRestRoutesAuthenticationAdmin(baseRouteName, componentNa
|
|
|
170
179
|
type: "IUnauthorizedResponse"
|
|
171
180
|
}
|
|
172
181
|
],
|
|
173
|
-
|
|
182
|
+
defaultAuthorization: DEFAULT_AUTHORIZATION_READER
|
|
174
183
|
};
|
|
175
184
|
const getByIdentityUserRoute = {
|
|
176
185
|
operationId: "authenticationAdminGetByIdentityUser",
|
|
@@ -204,8 +213,7 @@ export function generateRestRoutesAuthenticationAdmin(baseRouteName, componentNa
|
|
|
204
213
|
body: {
|
|
205
214
|
email: "user@example.com",
|
|
206
215
|
userIdentity: "did:example:123456789abcdefghi",
|
|
207
|
-
organizationIdentity: "did:example:123456789abcdefghi"
|
|
208
|
-
scope: ["scope1", "scope2"]
|
|
216
|
+
organizationIdentity: "did:example:123456789abcdefghi"
|
|
209
217
|
}
|
|
210
218
|
}
|
|
211
219
|
}
|
|
@@ -215,7 +223,7 @@ export function generateRestRoutesAuthenticationAdmin(baseRouteName, componentNa
|
|
|
215
223
|
type: "IUnauthorizedResponse"
|
|
216
224
|
}
|
|
217
225
|
],
|
|
218
|
-
|
|
226
|
+
defaultAuthorization: DEFAULT_AUTHORIZATION_READER
|
|
219
227
|
};
|
|
220
228
|
const removeUserRoute = {
|
|
221
229
|
operationId: "authenticationAdminRemoveUser",
|
|
@@ -246,7 +254,7 @@ export function generateRestRoutesAuthenticationAdmin(baseRouteName, componentNa
|
|
|
246
254
|
type: "IUnauthorizedResponse"
|
|
247
255
|
}
|
|
248
256
|
],
|
|
249
|
-
|
|
257
|
+
defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER
|
|
250
258
|
};
|
|
251
259
|
return [
|
|
252
260
|
createUserRoute,
|