nestjs-ddd-cli 3.2.2 → 3.3.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 +118 -9
- package/ddd.schema.json +11 -1
- package/dist/commands/doctor-enhanced.d.ts +1 -0
- package/dist/commands/doctor-enhanced.js +94 -79
- package/dist/commands/doctor-enhanced.js.map +1 -1
- package/dist/commands/doctor.d.ts +9 -0
- package/dist/commands/doctor.js +68 -21
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/generate-all.js +54 -28
- package/dist/commands/generate-all.js.map +1 -1
- package/dist/commands/generate-controller.d.ts +1 -0
- package/dist/commands/generate-controller.js +71 -0
- package/dist/commands/generate-controller.js.map +1 -0
- package/dist/commands/generate-domain-service.d.ts +1 -1
- package/dist/commands/generate-domain-service.js +6 -4
- package/dist/commands/generate-domain-service.js.map +1 -1
- package/dist/commands/generate-dto.d.ts +4 -0
- package/dist/commands/generate-dto.js +162 -0
- package/dist/commands/generate-dto.js.map +1 -0
- package/dist/commands/generate-entity.js +7 -2
- package/dist/commands/generate-entity.js.map +1 -1
- package/dist/commands/generate-event.js +10 -4
- package/dist/commands/generate-event.js.map +1 -1
- package/dist/commands/generate-migration-deployment.d.ts +15 -0
- package/dist/commands/generate-migration-deployment.js +326 -0
- package/dist/commands/generate-migration-deployment.js.map +1 -0
- package/dist/commands/generate-migration-deployment.spec.d.ts +1 -0
- package/dist/commands/generate-migration-deployment.spec.js +337 -0
- package/dist/commands/generate-migration-deployment.spec.js.map +1 -0
- package/dist/commands/generate-module.js +4 -2
- package/dist/commands/generate-module.js.map +1 -1
- package/dist/commands/generate-query.js +23 -4
- package/dist/commands/generate-query.js.map +1 -1
- package/dist/commands/generate-repository.d.ts +1 -1
- package/dist/commands/generate-repository.js +17 -34
- package/dist/commands/generate-repository.js.map +1 -1
- package/dist/commands/generate-service.js +19 -4
- package/dist/commands/generate-service.js.map +1 -1
- package/dist/commands/generate-usecase.js +12 -31
- package/dist/commands/generate-usecase.js.map +1 -1
- package/dist/commands/generate-value-object.js +3 -14
- package/dist/commands/generate-value-object.js.map +1 -1
- package/dist/commands/init-project.d.ts +3 -0
- package/dist/commands/init-project.js +19 -9
- package/dist/commands/init-project.js.map +1 -1
- package/dist/commands/interactive-scaffold.js +10 -0
- package/dist/commands/interactive-scaffold.js.map +1 -1
- package/dist/commands/migration.d.ts +3 -0
- package/dist/commands/migration.js +83 -48
- package/dist/commands/migration.js.map +1 -1
- package/dist/commands/recipe.d.ts +2 -0
- package/dist/commands/recipe.js +229 -85
- package/dist/commands/recipe.js.map +1 -1
- package/dist/commands/recipes/banklink-connector-contract.recipe.d.ts +1 -0
- package/dist/commands/recipes/banklink-connector-contract.recipe.js +161 -0
- package/dist/commands/recipes/banklink-connector-contract.recipe.js.map +1 -0
- package/dist/commands/recipes/bounded-json-http.recipe.d.ts +2 -0
- package/dist/commands/recipes/bounded-json-http.recipe.js +53 -0
- package/dist/commands/recipes/bounded-json-http.recipe.js.map +1 -0
- package/dist/commands/recipes/business-reference-identifiers.recipe.d.ts +1 -0
- package/dist/commands/recipes/business-reference-identifiers.recipe.js +223 -0
- package/dist/commands/recipes/business-reference-identifiers.recipe.js.map +1 -0
- package/dist/commands/recipes/event-backbone.recipe.d.ts +1 -0
- package/dist/commands/recipes/event-backbone.recipe.js +1118 -0
- package/dist/commands/recipes/event-backbone.recipe.js.map +1 -0
- package/dist/commands/recipes/external-projection-worker.recipe.d.ts +6 -0
- package/dist/commands/recipes/external-projection-worker.recipe.js +211 -0
- package/dist/commands/recipes/external-projection-worker.recipe.js.map +1 -0
- package/dist/commands/recipes/index.d.ts +11 -0
- package/dist/commands/recipes/index.js +11 -0
- package/dist/commands/recipes/index.js.map +1 -1
- package/dist/commands/recipes/jest-cjs-compat.recipe.d.ts +3 -0
- package/dist/commands/recipes/jest-cjs-compat.recipe.js +130 -0
- package/dist/commands/recipes/jest-cjs-compat.recipe.js.map +1 -0
- package/dist/commands/recipes/joi-env.recipe.js +1 -1
- package/dist/commands/recipes/kafka-consumer.recipe.d.ts +1 -0
- package/dist/commands/recipes/kafka-consumer.recipe.js +65 -0
- package/dist/commands/recipes/kafka-consumer.recipe.js.map +1 -0
- package/dist/commands/recipes/oauth2-client-credentials.recipe.d.ts +1 -0
- package/dist/commands/recipes/oauth2-client-credentials.recipe.js +60 -0
- package/dist/commands/recipes/oauth2-client-credentials.recipe.js.map +1 -0
- package/dist/commands/recipes/oauth2-token-introspection.recipe.d.ts +1 -0
- package/dist/commands/recipes/oauth2-token-introspection.recipe.js +67 -0
- package/dist/commands/recipes/oauth2-token-introspection.recipe.js.map +1 -0
- package/dist/commands/recipes/oidc-bff-session.recipe.d.ts +1 -0
- package/dist/commands/recipes/oidc-bff-session.recipe.js +57 -0
- package/dist/commands/recipes/oidc-bff-session.recipe.js.map +1 -0
- package/dist/commands/recipes/oidc-dashboard.recipe.d.ts +1 -0
- package/dist/commands/recipes/oidc-dashboard.recipe.js +463 -0
- package/dist/commands/recipes/oidc-dashboard.recipe.js.map +1 -0
- package/dist/commands/recipes/oidc-resource-server.recipe.d.ts +1 -0
- package/dist/commands/recipes/oidc-resource-server.recipe.js +85 -0
- package/dist/commands/recipes/oidc-resource-server.recipe.js.map +1 -0
- package/dist/commands/recipes/platform-context.recipe.d.ts +1 -0
- package/dist/commands/recipes/platform-context.recipe.js +229 -0
- package/dist/commands/recipes/platform-context.recipe.js.map +1 -0
- package/dist/commands/recipes/platform-parc-authorization.recipe.d.ts +1 -0
- package/dist/commands/recipes/platform-parc-authorization.recipe.js +339 -0
- package/dist/commands/recipes/platform-parc-authorization.recipe.js.map +1 -0
- package/dist/commands/recipes/platform-parc-bound-authorization.recipe.d.ts +1 -0
- package/dist/commands/recipes/platform-parc-bound-authorization.recipe.js +56 -0
- package/dist/commands/recipes/platform-parc-bound-authorization.recipe.js.map +1 -0
- package/dist/commands/recipes/platform-service-access-request-context.recipe.d.ts +1 -0
- package/dist/commands/recipes/platform-service-access-request-context.recipe.js +103 -0
- package/dist/commands/recipes/platform-service-access-request-context.recipe.js.map +1 -0
- package/dist/commands/recipes/platform-service-runtime.recipe.d.ts +1 -0
- package/dist/commands/recipes/platform-service-runtime.recipe.js +415 -0
- package/dist/commands/recipes/platform-service-runtime.recipe.js.map +1 -0
- package/dist/index.js +77 -9
- package/dist/index.js.map +1 -1
- package/dist/templates/ai-context/CLAUDE.md.hbs +39 -3
- package/dist/templates/ai-context/conventions.md.hbs +13 -8
- package/dist/templates/command/create-command.hbs +8 -3
- package/dist/templates/command/delete-command.hbs +1 -1
- package/dist/templates/command/update-command.hbs +1 -1
- package/dist/templates/controller/application-controller.hbs +7 -0
- package/dist/templates/controller/controller.hbs +6 -0
- package/dist/templates/dto/create-dto.hbs +17 -5
- package/dist/templates/dto/filter-dto.hbs +1 -1
- package/dist/templates/dto/response-dto.hbs +1 -1
- package/dist/templates/entity/entity.hbs +4 -0
- package/dist/templates/mapper/mapper.hbs +5 -1
- package/dist/templates/orm-entity/orm-entity.hbs +9 -1
- package/dist/templates/prisma/prisma-mapper.hbs +6 -0
- package/dist/templates/prisma/prisma-repository.hbs +57 -5
- package/dist/templates/prisma/prisma-schema.hbs +2 -0
- package/dist/templates/query/get-all.query.hbs +10 -2
- package/dist/templates/recipes/bounded-json-http/README.md.hbs +36 -0
- package/dist/templates/recipes/external-projection-worker/README.md.hbs +157 -0
- package/dist/templates/recipes/external-projection-worker/canonical-json.ts.hbs +123 -0
- package/dist/templates/recipes/external-projection-worker/enqueuer.ts.hbs +167 -0
- package/dist/templates/recipes/external-projection-worker/index.ts.hbs +6 -0
- package/dist/templates/recipes/external-projection-worker/migration.ts.hbs +88 -0
- package/dist/templates/recipes/external-projection-worker/module.ts.hbs +36 -0
- package/dist/templates/recipes/external-projection-worker/store.ts.hbs +354 -0
- package/dist/templates/recipes/external-projection-worker/types.ts.hbs +134 -0
- package/dist/templates/recipes/external-projection-worker/worker.ts.hbs +296 -0
- package/dist/templates/recipes/kafka-consumer/README.md.hbs +50 -0
- package/dist/templates/recipes/kafka-consumer/kafka-consumer.ts.hbs +135 -0
- package/dist/templates/recipes/kafka-consumer/migration.ts.hbs +17 -0
- package/dist/templates/recipes/oauth2-client-credentials/README.md.hbs +40 -0
- package/dist/templates/recipes/oauth2-client-credentials/client.spec.ts.hbs +234 -0
- package/dist/templates/recipes/oauth2-client-credentials/client.ts.hbs +210 -0
- package/dist/templates/recipes/oauth2-client-credentials/index.ts.hbs +2 -0
- package/dist/templates/recipes/oauth2-client-credentials/module.ts.hbs +58 -0
- package/dist/templates/recipes/oauth2-token-introspection/README.md.hbs +158 -0
- package/dist/templates/recipes/oauth2-token-introspection/client.spec.ts.hbs +350 -0
- package/dist/templates/recipes/oauth2-token-introspection/client.ts.hbs +241 -0
- package/dist/templates/recipes/oauth2-token-introspection/http-client.spec.ts.hbs +411 -0
- package/dist/templates/recipes/oauth2-token-introspection/http-client.ts.hbs +343 -0
- package/dist/templates/recipes/oauth2-token-introspection/index.ts.hbs +3 -0
- package/dist/templates/recipes/oauth2-token-introspection/module.ts.hbs +58 -0
- package/dist/templates/recipes/oidc-bff-session/README.md.hbs +96 -0
- package/dist/templates/recipes/oidc-bff-session/client.test.ts.hbs +663 -0
- package/dist/templates/recipes/oidc-bff-session/client.ts.hbs +635 -0
- package/dist/templates/recipes/oidc-bff-session/index.ts.hbs +3 -0
- package/dist/templates/recipes/oidc-bff-session/store.integration.test.ts.hbs +480 -0
- package/dist/templates/recipes/oidc-bff-session/store.test.ts.hbs +87 -0
- package/dist/templates/recipes/oidc-bff-session/store.ts.hbs +549 -0
- package/dist/templates/recipes/oidc-bff-session/valkey.test.ts.hbs +158 -0
- package/dist/templates/recipes/oidc-bff-session/valkey.ts.hbs +148 -0
- package/dist/templates/recipes/oidc-resource-server/README.md.hbs +74 -0
- package/dist/templates/recipes/oidc-resource-server/index.ts.hbs +2 -0
- package/dist/templates/recipes/oidc-resource-server/module.ts.hbs +51 -0
- package/dist/templates/recipes/oidc-resource-server/verifier.spec.ts.hbs +186 -0
- package/dist/templates/recipes/oidc-resource-server/verifier.ts.hbs +440 -0
- package/dist/templates/recipes/platform-parc-bound-authorization/README.md.hbs +92 -0
- package/dist/templates/recipes/platform-parc-bound-authorization/client.ts.hbs +142 -0
- package/dist/templates/recipes/platform-service-access-request-context/docs.md.hbs +61 -0
- package/dist/templates/recipes/platform-service-access-request-context/env.example.hbs +7 -0
- package/dist/templates/recipes/platform-service-access-request-context/index.ts.hbs +7 -0
- package/dist/templates/recipes/platform-service-access-request-context/request-context-headers.ts.hbs +61 -0
- package/dist/templates/recipes/platform-service-access-request-context/request-context-introspection.parser.ts.hbs +132 -0
- package/dist/templates/recipes/platform-service-access-request-context/request-context-introspection.types.ts.hbs +40 -0
- package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.client.ts.hbs +45 -0
- package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.config.ts.hbs +142 -0
- package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.guard.ts.hbs +77 -0
- package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.module.ts.hbs +17 -0
- package/dist/templates/repository/repository.hbs +42 -4
- package/dist/templates/resolver/graphql-input.hbs +23 -6
- package/dist/templates/resolver/resolver.hbs +4 -0
- package/dist/templates/service/domain-service.hbs +2 -2
- package/dist/templates/test/controller.spec.hbs +5 -1
- package/dist/templates/test/prisma-repository.spec.hbs +192 -0
- package/dist/templates/test/repository.spec.hbs +40 -1
- package/dist/templates/test/usecase.spec.hbs +23 -4
- package/dist/templates/usecase/application-usecase.hbs +17 -0
- package/dist/templates/usecase/create-usecase.hbs +34 -3
- package/dist/templates/usecase/update-usecase.hbs +15 -1
- package/dist/utils/config.utils.d.ts +2 -0
- package/dist/utils/config.utils.js +14 -2
- package/dist/utils/config.utils.js.map +1 -1
- package/dist/utils/dependency.utils.d.ts +16 -2
- package/dist/utils/dependency.utils.js +100 -12
- package/dist/utils/dependency.utils.js.map +1 -1
- package/dist/utils/field.utils.d.ts +6 -0
- package/dist/utils/field.utils.js +50 -8
- package/dist/utils/field.utils.js.map +1 -1
- package/dist/utils/field.utils.spec.d.ts +1 -0
- package/dist/utils/field.utils.spec.js +38 -0
- package/dist/utils/field.utils.spec.js.map +1 -0
- package/dist/utils/file.utils.d.ts +23 -1
- package/dist/utils/file.utils.js +37 -1
- package/dist/utils/file.utils.js.map +1 -1
- package/dist/utils/naming.utils.js +5 -4
- package/dist/utils/naming.utils.js.map +1 -1
- package/dist/utils/static-recipe.utils.d.ts +4 -0
- package/dist/utils/static-recipe.utils.js +148 -0
- package/dist/utils/static-recipe.utils.js.map +1 -0
- package/package.json +12 -5
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.applyOAuth2TokenIntrospectionRecipe = applyOAuth2TokenIntrospectionRecipe;
|
|
37
|
+
const path = __importStar(require("node:path"));
|
|
38
|
+
const static_recipe_utils_1 = require("../../utils/static-recipe.utils");
|
|
39
|
+
const files = [
|
|
40
|
+
[
|
|
41
|
+
'client.ts.hbs',
|
|
42
|
+
'src/shared/auth/oauth2-token-introspection/oauth2-token-introspection.client.ts',
|
|
43
|
+
],
|
|
44
|
+
[
|
|
45
|
+
'client.spec.ts.hbs',
|
|
46
|
+
'src/shared/auth/oauth2-token-introspection/oauth2-token-introspection.client.spec.ts',
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
'module.ts.hbs',
|
|
50
|
+
'src/shared/auth/oauth2-token-introspection/oauth2-token-introspection.module.ts',
|
|
51
|
+
],
|
|
52
|
+
['index.ts.hbs', 'src/shared/auth/oauth2-token-introspection/index.ts'],
|
|
53
|
+
['http-client.ts.hbs', 'src/shared/auth/oauth2-token-introspection/bounded-json-http.client.ts'],
|
|
54
|
+
[
|
|
55
|
+
'http-client.spec.ts.hbs',
|
|
56
|
+
'src/shared/auth/oauth2-token-introspection/bounded-json-http.client.spec.ts',
|
|
57
|
+
],
|
|
58
|
+
['README.md.hbs', 'docs/auth/oauth2-token-introspection.md'],
|
|
59
|
+
];
|
|
60
|
+
async function applyOAuth2TokenIntrospectionRecipe(basePath, dryRun = false) {
|
|
61
|
+
const templateRoot = path.join(__dirname, '../../templates/recipes/oauth2-token-introspection');
|
|
62
|
+
await (0, static_recipe_utils_1.applyStaticRecipePlan)(basePath, files.map(([source, destination]) => ({
|
|
63
|
+
template: path.join(templateRoot, source),
|
|
64
|
+
destination,
|
|
65
|
+
})), dryRun);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=oauth2-token-introspection.recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth2-token-introspection.recipe.js","sourceRoot":"","sources":["../../../src/commands/recipes/oauth2-token-introspection.recipe.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,kFAaC;AAtCD,gDAAkC;AAClC,yEAAwE;AAExE,MAAM,KAAK,GAAG;IACZ;QACE,eAAe;QACf,iFAAiF;KAClF;IACD;QACE,oBAAoB;QACpB,sFAAsF;KACvF;IACD;QACE,eAAe;QACf,iFAAiF;KAClF;IACD,CAAC,cAAc,EAAE,qDAAqD,CAAC;IACvE,CAAC,oBAAoB,EAAE,wEAAwE,CAAC;IAChG;QACE,yBAAyB;QACzB,6EAA6E;KAC9E;IACD,CAAC,eAAe,EAAE,yCAAyC,CAAC;CACpD,CAAC;AAEJ,KAAK,UAAU,mCAAmC,CACvD,QAAgB,EAChB,MAAM,GAAG,KAAK;IAEd,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,oDAAoD,CAAC,CAAC;IAChG,MAAM,IAAA,2CAAqB,EACzB,QAAQ,EACR,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;QACpC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC;QACzC,WAAW;KACZ,CAAC,CAAC,EACH,MAAM,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function applyOidcBffSessionRecipe(basePath: string, dryRun?: boolean): Promise<void>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.applyOidcBffSessionRecipe = applyOidcBffSessionRecipe;
|
|
37
|
+
const path = __importStar(require("node:path"));
|
|
38
|
+
const static_recipe_utils_1 = require("../../utils/static-recipe.utils");
|
|
39
|
+
const files = [
|
|
40
|
+
['client.ts.hbs', 'src/shared/oidc-bff/oidc-bff.client.ts'],
|
|
41
|
+
['client.test.ts.hbs', 'src/shared/oidc-bff/oidc-bff.client.test.ts'],
|
|
42
|
+
['store.ts.hbs', 'src/shared/oidc-bff/opaque-session-store.ts'],
|
|
43
|
+
['store.test.ts.hbs', 'src/shared/oidc-bff/opaque-session-store.test.ts'],
|
|
44
|
+
['store.integration.test.ts.hbs', 'src/shared/oidc-bff/opaque-session-store.integration.test.ts'],
|
|
45
|
+
['valkey.ts.hbs', 'src/shared/oidc-bff/valkey-session-store.ts'],
|
|
46
|
+
['valkey.test.ts.hbs', 'src/shared/oidc-bff/valkey-session-store.test.ts'],
|
|
47
|
+
['index.ts.hbs', 'src/shared/oidc-bff/index.ts'],
|
|
48
|
+
['README.md.hbs', 'docs/auth/oidc-bff-session.md'],
|
|
49
|
+
];
|
|
50
|
+
async function applyOidcBffSessionRecipe(basePath, dryRun = false) {
|
|
51
|
+
const templateRoot = path.join(__dirname, '../../templates/recipes/oidc-bff-session');
|
|
52
|
+
await (0, static_recipe_utils_1.applyStaticRecipePlan)(basePath, files.map(([source, destination]) => ({
|
|
53
|
+
template: path.join(templateRoot, source),
|
|
54
|
+
destination,
|
|
55
|
+
})), dryRun);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=oidc-bff-session.recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-bff-session.recipe.js","sourceRoot":"","sources":["../../../src/commands/recipes/oidc-bff-session.recipe.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,8DAUC;AAzBD,gDAAkC;AAClC,yEAAwE;AAExE,MAAM,KAAK,GAAG;IACZ,CAAC,eAAe,EAAE,wCAAwC,CAAC;IAC3D,CAAC,oBAAoB,EAAE,6CAA6C,CAAC;IACrE,CAAC,cAAc,EAAE,6CAA6C,CAAC;IAC/D,CAAC,mBAAmB,EAAE,kDAAkD,CAAC;IACzE,CAAC,+BAA+B,EAAE,8DAA8D,CAAC;IACjG,CAAC,eAAe,EAAE,6CAA6C,CAAC;IAChE,CAAC,oBAAoB,EAAE,kDAAkD,CAAC;IAC1E,CAAC,cAAc,EAAE,8BAA8B,CAAC;IAChD,CAAC,eAAe,EAAE,+BAA+B,CAAC;CAC1C,CAAC;AAEJ,KAAK,UAAU,yBAAyB,CAAC,QAAgB,EAAE,MAAM,GAAG,KAAK;IAC9E,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,0CAA0C,CAAC,CAAC;IACtF,MAAM,IAAA,2CAAqB,EACzB,QAAQ,EACR,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;QACpC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC;QACzC,WAAW;KACZ,CAAC,CAAC,EACH,MAAM,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function applyOidcDashboardRecipe(basePath: string): Promise<void>;
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.applyOidcDashboardRecipe = applyOidcDashboardRecipe;
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const file_utils_1 = require("../../utils/file.utils");
|
|
43
|
+
const platform_context_recipe_1 = require("./platform-context.recipe");
|
|
44
|
+
async function applyOidcDashboardRecipe(basePath) {
|
|
45
|
+
await (0, platform_context_recipe_1.applyPlatformContextRecipe)(basePath);
|
|
46
|
+
const authPath = path.join(basePath, 'src/shared/auth/oidc-dashboard');
|
|
47
|
+
const docsPath = path.join(basePath, 'docs/auth');
|
|
48
|
+
await (0, file_utils_1.ensureDir)(authPath);
|
|
49
|
+
await (0, file_utils_1.ensureDir)(docsPath);
|
|
50
|
+
await (0, file_utils_1.writeFile)(path.join(authPath, 'oidc-dashboard.config.ts'), configContent);
|
|
51
|
+
await (0, file_utils_1.writeFile)(path.join(authPath, 'oidc-principal.ts'), principalContent);
|
|
52
|
+
await (0, file_utils_1.writeFile)(path.join(authPath, 'oidc-access-mapping.ts'), mappingContent);
|
|
53
|
+
await (0, file_utils_1.writeFile)(path.join(authPath, 'oidc-token-verifier.service.ts'), verifierContent);
|
|
54
|
+
await (0, file_utils_1.writeFile)(path.join(authPath, 'canonical-platform-principal.resolver.ts'), resolverContent);
|
|
55
|
+
await (0, file_utils_1.writeFile)(path.join(authPath, 'oidc-dashboard.guard.ts'), guardContent);
|
|
56
|
+
await (0, file_utils_1.writeFile)(path.join(authPath, 'index.ts'), indexContent);
|
|
57
|
+
await (0, file_utils_1.writeFile)(path.join(docsPath, 'oidc-dashboard.md'), docsContent);
|
|
58
|
+
const envExamplePath = path.join(basePath, '.env.example');
|
|
59
|
+
const fallbackExamplePath = path.join(basePath, '.env.oidc-dashboard.example');
|
|
60
|
+
const targetExamplePath = (await (0, file_utils_1.fileExists)(envExamplePath))
|
|
61
|
+
? fallbackExamplePath
|
|
62
|
+
: envExamplePath;
|
|
63
|
+
await (0, file_utils_1.writeFile)(targetExamplePath, envExampleContent);
|
|
64
|
+
console.log(chalk_1.default.green(' ✓ Platform request context contract'));
|
|
65
|
+
console.log(chalk_1.default.green(' ✓ OIDC dashboard config contract'));
|
|
66
|
+
console.log(chalk_1.default.green(' ✓ Canonical platform principal resolver hook'));
|
|
67
|
+
console.log(chalk_1.default.green(' ✓ Role/group mapping hook'));
|
|
68
|
+
console.log(chalk_1.default.green(' ✓ JWKS-backed bearer token verifier'));
|
|
69
|
+
console.log(chalk_1.default.green(' ✓ NestJS dashboard/API guard with platform context'));
|
|
70
|
+
console.log(chalk_1.default.green(' ✓ Laravel/Filament and Next/Node integration notes'));
|
|
71
|
+
console.log(chalk_1.default.yellow(' Keep dashboard RBAC local or PARC-backed; OIDC proves identity, not product authorization.'));
|
|
72
|
+
}
|
|
73
|
+
const configContent = `export interface OidcDashboardConfig {
|
|
74
|
+
issuerUrl: string;
|
|
75
|
+
clientId: string;
|
|
76
|
+
clientSecret?: string;
|
|
77
|
+
callbackUrl: string;
|
|
78
|
+
postLogoutRedirectUrl?: string;
|
|
79
|
+
scopes: string[];
|
|
80
|
+
adminEmails: string[];
|
|
81
|
+
allowedEmailDomains: string[];
|
|
82
|
+
roleClaim: string;
|
|
83
|
+
groupClaim: string;
|
|
84
|
+
audience?: string;
|
|
85
|
+
jwksUrl?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function listFromEnv(value: string | undefined, fallback: string[] = []): string[] {
|
|
89
|
+
if (!value) {
|
|
90
|
+
return fallback;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return value
|
|
94
|
+
.split(",")
|
|
95
|
+
.map((item) => item.trim())
|
|
96
|
+
.filter(Boolean);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function oidcDashboardConfigFromEnv(): OidcDashboardConfig {
|
|
100
|
+
const issuerUrl = process.env.OIDC_ISSUER_URL;
|
|
101
|
+
const clientId = process.env.OIDC_CLIENT_ID;
|
|
102
|
+
const callbackUrl = process.env.OIDC_CALLBACK_URL;
|
|
103
|
+
|
|
104
|
+
if (!issuerUrl || !clientId || !callbackUrl) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
"OIDC_ISSUER_URL, OIDC_CLIENT_ID, and OIDC_CALLBACK_URL are required for dashboard SSO",
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
issuerUrl: issuerUrl.replace(/\\/+$/, ""),
|
|
112
|
+
clientId,
|
|
113
|
+
clientSecret: process.env.OIDC_CLIENT_SECRET,
|
|
114
|
+
callbackUrl,
|
|
115
|
+
postLogoutRedirectUrl: process.env.OIDC_POST_LOGOUT_REDIRECT_URL,
|
|
116
|
+
scopes: listFromEnv(process.env.OIDC_SCOPES, ["openid", "profile", "email"]),
|
|
117
|
+
adminEmails: listFromEnv(process.env.OIDC_ADMIN_EMAILS).map((email) =>
|
|
118
|
+
email.toLowerCase(),
|
|
119
|
+
),
|
|
120
|
+
allowedEmailDomains: listFromEnv(process.env.OIDC_ALLOWED_EMAIL_DOMAINS).map(
|
|
121
|
+
(domain) => domain.toLowerCase(),
|
|
122
|
+
),
|
|
123
|
+
roleClaim: process.env.OIDC_ROLE_CLAIM ?? "roles",
|
|
124
|
+
groupClaim: process.env.OIDC_GROUP_CLAIM ?? "groups",
|
|
125
|
+
audience: process.env.OIDC_AUDIENCE ?? clientId,
|
|
126
|
+
jwksUrl: process.env.OIDC_JWKS_URL || undefined,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
`;
|
|
130
|
+
const principalContent = `export interface OidcDashboardPrincipal {
|
|
131
|
+
subject: string;
|
|
132
|
+
email?: string;
|
|
133
|
+
emailVerified?: boolean;
|
|
134
|
+
name?: string;
|
|
135
|
+
roles: string[];
|
|
136
|
+
groups: string[];
|
|
137
|
+
claims: Record<string, unknown>;
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
const mappingContent = `export interface OidcDashboardAccessMappingInput {
|
|
141
|
+
subject: string;
|
|
142
|
+
email?: string;
|
|
143
|
+
claims: Record<string, unknown>;
|
|
144
|
+
roleClaim: string;
|
|
145
|
+
groupClaim: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface OidcDashboardAccessMapping {
|
|
149
|
+
roles: string[];
|
|
150
|
+
groups: string[];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export type OidcDashboardAccessMappingHook = (
|
|
154
|
+
input: OidcDashboardAccessMappingInput,
|
|
155
|
+
) => OidcDashboardAccessMapping | Promise<OidcDashboardAccessMapping>;
|
|
156
|
+
|
|
157
|
+
export const defaultOidcDashboardAccessMapping: OidcDashboardAccessMappingHook = (
|
|
158
|
+
input,
|
|
159
|
+
) => ({
|
|
160
|
+
roles: oidcClaimValues(input.claims, input.roleClaim),
|
|
161
|
+
groups: oidcClaimValues(input.claims, input.groupClaim),
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
export function oidcClaimValues(
|
|
165
|
+
claims: Record<string, unknown>,
|
|
166
|
+
claimName: string,
|
|
167
|
+
): string[] {
|
|
168
|
+
const value = claims[claimName];
|
|
169
|
+
return uniqueStringList(stringListFromClaimValue(value));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function stringListFromClaimValue(value: unknown): string[] {
|
|
173
|
+
if (Array.isArray(value)) {
|
|
174
|
+
return value.filter((item): item is string => typeof item === "string");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (typeof value === "string") {
|
|
178
|
+
return value.split(/[\\s,]+/);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (value && typeof value === "object") {
|
|
182
|
+
return Object.entries(value as Record<string, unknown>)
|
|
183
|
+
.filter(([, nestedValue]) => Boolean(nestedValue))
|
|
184
|
+
.map(([key]) => key);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function uniqueStringList(values: string[]): string[] {
|
|
191
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
192
|
+
}
|
|
193
|
+
`;
|
|
194
|
+
const verifierContent = `import { Injectable, UnauthorizedException } from "@nestjs/common";
|
|
195
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
196
|
+
import { defaultOidcDashboardAccessMapping } from "./oidc-access-mapping";
|
|
197
|
+
import {
|
|
198
|
+
OidcDashboardConfig,
|
|
199
|
+
oidcDashboardConfigFromEnv,
|
|
200
|
+
} from "./oidc-dashboard.config";
|
|
201
|
+
import { OidcDashboardPrincipal } from "./oidc-principal";
|
|
202
|
+
|
|
203
|
+
@Injectable()
|
|
204
|
+
export class OidcTokenVerifierService {
|
|
205
|
+
private readonly config: OidcDashboardConfig;
|
|
206
|
+
private readonly jwks: ReturnType<typeof createRemoteJWKSet>;
|
|
207
|
+
|
|
208
|
+
constructor() {
|
|
209
|
+
this.config = oidcDashboardConfigFromEnv();
|
|
210
|
+
this.jwks = createRemoteJWKSet(
|
|
211
|
+
new URL(this.config.jwksUrl ?? this.config.issuerUrl + "/oauth/v2/keys"),
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async verifyBearerToken(token: string): Promise<OidcDashboardPrincipal> {
|
|
216
|
+
try {
|
|
217
|
+
const { payload } = await jwtVerify(token, this.jwks, {
|
|
218
|
+
issuer: this.config.issuerUrl,
|
|
219
|
+
audience: this.config.audience,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const subject = String(payload.sub);
|
|
223
|
+
const email = typeof payload.email === "string" ? payload.email : undefined;
|
|
224
|
+
const claims = payload as Record<string, unknown>;
|
|
225
|
+
const access = await defaultOidcDashboardAccessMapping({
|
|
226
|
+
subject,
|
|
227
|
+
email,
|
|
228
|
+
claims,
|
|
229
|
+
roleClaim: this.config.roleClaim,
|
|
230
|
+
groupClaim: this.config.groupClaim,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
if (email && !this.emailAllowed(email)) {
|
|
234
|
+
throw new UnauthorizedException("OIDC email domain is not allowed");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
subject,
|
|
239
|
+
email,
|
|
240
|
+
emailVerified:
|
|
241
|
+
typeof payload.email_verified === "boolean"
|
|
242
|
+
? payload.email_verified
|
|
243
|
+
: undefined,
|
|
244
|
+
name: typeof payload.name === "string" ? payload.name : undefined,
|
|
245
|
+
roles: access.roles,
|
|
246
|
+
groups: access.groups,
|
|
247
|
+
claims,
|
|
248
|
+
};
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (error instanceof UnauthorizedException) {
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
throw new UnauthorizedException("Invalid OIDC bearer token");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private emailAllowed(email: string): boolean {
|
|
258
|
+
if (this.config.adminEmails.includes(email.toLowerCase())) {
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (this.config.allowedEmailDomains.length === 0) {
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const domain = email.split("@").pop()?.toLowerCase();
|
|
267
|
+
return Boolean(domain && this.config.allowedEmailDomains.includes(domain));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
`;
|
|
271
|
+
const resolverContent = `import { UnauthorizedException } from "@nestjs/common";
|
|
272
|
+
import { VerifiedPlatformPrincipal } from "../../platform-context/platform-context.types";
|
|
273
|
+
import { OidcDashboardPrincipal } from "./oidc-principal";
|
|
274
|
+
|
|
275
|
+
export const CANONICAL_PLATFORM_PRINCIPAL_RESOLVER = Symbol(
|
|
276
|
+
"CANONICAL_PLATFORM_PRINCIPAL_RESOLVER",
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
export interface CanonicalPlatformPrincipalResolver {
|
|
280
|
+
resolve(
|
|
281
|
+
principal: OidcDashboardPrincipal,
|
|
282
|
+
): VerifiedPlatformPrincipal | Promise<VerifiedPlatformPrincipal>;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export const defaultCanonicalPlatformPrincipalResolver: CanonicalPlatformPrincipalResolver =
|
|
286
|
+
{
|
|
287
|
+
resolve() {
|
|
288
|
+
throw new UnauthorizedException(
|
|
289
|
+
"Canonical platform principal mapping is not configured",
|
|
290
|
+
);
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
`;
|
|
294
|
+
const guardContent = `import {
|
|
295
|
+
CanActivate,
|
|
296
|
+
ExecutionContext,
|
|
297
|
+
Inject,
|
|
298
|
+
Injectable,
|
|
299
|
+
UnauthorizedException,
|
|
300
|
+
} from "@nestjs/common";
|
|
301
|
+
import { Request } from "express";
|
|
302
|
+
import { createPlatformRequestContext } from "../../platform-context/platform-context.factory";
|
|
303
|
+
import { PlatformRequestContext } from "../../platform-context/platform-context.types";
|
|
304
|
+
import {
|
|
305
|
+
CANONICAL_PLATFORM_PRINCIPAL_RESOLVER,
|
|
306
|
+
CanonicalPlatformPrincipalResolver,
|
|
307
|
+
} from "./canonical-platform-principal.resolver";
|
|
308
|
+
import { OidcTokenVerifierService } from "./oidc-token-verifier.service";
|
|
309
|
+
|
|
310
|
+
export interface RequestWithOidcPrincipal extends Request {
|
|
311
|
+
oidcPrincipal?: Awaited<
|
|
312
|
+
ReturnType<OidcTokenVerifierService["verifyBearerToken"]>
|
|
313
|
+
>;
|
|
314
|
+
platformContext?: PlatformRequestContext;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
@Injectable()
|
|
318
|
+
export class OidcDashboardGuard implements CanActivate {
|
|
319
|
+
constructor(
|
|
320
|
+
private readonly verifier: OidcTokenVerifierService,
|
|
321
|
+
@Inject(CANONICAL_PLATFORM_PRINCIPAL_RESOLVER)
|
|
322
|
+
private readonly principalResolver: CanonicalPlatformPrincipalResolver,
|
|
323
|
+
) {}
|
|
324
|
+
|
|
325
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
326
|
+
const request = context.switchToHttp().getRequest<RequestWithOidcPrincipal>();
|
|
327
|
+
const token = this.extractBearerToken(request.headers.authorization);
|
|
328
|
+
|
|
329
|
+
const oidcPrincipal = await this.verifier.verifyBearerToken(token);
|
|
330
|
+
request.oidcPrincipal = oidcPrincipal;
|
|
331
|
+
|
|
332
|
+
const verifiedPrincipal = await this.principalResolver.resolve(oidcPrincipal);
|
|
333
|
+
request.platformContext = createPlatformRequestContext({
|
|
334
|
+
principal: verifiedPrincipal,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private extractBearerToken(authorization?: string): string {
|
|
341
|
+
const [scheme, token] = authorization?.split(" ") ?? [];
|
|
342
|
+
if (scheme !== "Bearer" || !token) {
|
|
343
|
+
throw new UnauthorizedException("Bearer token is required");
|
|
344
|
+
}
|
|
345
|
+
return token;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
`;
|
|
349
|
+
const indexContent = `export * from "./oidc-dashboard.config";
|
|
350
|
+
export * from "./oidc-principal";
|
|
351
|
+
export * from "./oidc-access-mapping";
|
|
352
|
+
export * from "./oidc-token-verifier.service";
|
|
353
|
+
export * from "./canonical-platform-principal.resolver";
|
|
354
|
+
export * from "./oidc-dashboard.guard";
|
|
355
|
+
`;
|
|
356
|
+
const docsContent = `# OIDC Dashboard Integration
|
|
357
|
+
|
|
358
|
+
Use this recipe for internal dashboards, operator portals, and admin APIs that
|
|
359
|
+
delegate authentication to an OIDC broker such as ZITADEL.
|
|
360
|
+
|
|
361
|
+
## Ownership Boundary
|
|
362
|
+
|
|
363
|
+
- OIDC authenticates the human or machine session.
|
|
364
|
+
- The service maps the OIDC subject and email to a local operator record.
|
|
365
|
+
- Product authorization stays local or PARC-backed. Do not grant access merely
|
|
366
|
+
because a user can authenticate with the broker.
|
|
367
|
+
- Identity Manager remains the canonical identity profile service. VerifyHQ
|
|
368
|
+
remains verification evidence. Service Access remains service/client
|
|
369
|
+
enrollment and discovery.
|
|
370
|
+
|
|
371
|
+
## NestJS API/Dashboard Pattern
|
|
372
|
+
|
|
373
|
+
1. Configure env values from \`.env.oidc-dashboard.example\`.
|
|
374
|
+
2. Register \`OidcTokenVerifierService\` as a provider.
|
|
375
|
+
3. Provide \`CANONICAL_PLATFORM_PRINCIPAL_RESOLVER\` with an implementation that
|
|
376
|
+
maps the verified OIDC principal to \`VerifiedPlatformPrincipal\` through
|
|
377
|
+
Identity Manager and Business Entity. Do not set \`actor.subjectId\` to the
|
|
378
|
+
OIDC \`sub\` automatically; canonical subjects come from platform identity
|
|
379
|
+
services after broker verification.
|
|
380
|
+
4. Apply \`OidcDashboardGuard\` to dashboard/admin API controllers. The guard
|
|
381
|
+
verifies the bearer token, resolves the canonical principal, builds
|
|
382
|
+
\`platformContext\` with \`createPlatformRequestContext\`, and attaches it to
|
|
383
|
+
the request. Do not read actor, business, or application identity from custom
|
|
384
|
+
headers or query parameters.
|
|
385
|
+
5. Before privileged mutations, require both Service Access capability grants and
|
|
386
|
+
PARC action checks. Record Service Access and PARC decision references in the owner
|
|
387
|
+
audit trail.
|
|
388
|
+
6. On first successful login, map broker identity to your local operator record
|
|
389
|
+
or ask PARC for roles/permissions.
|
|
390
|
+
7. Keep the OIDC client secret in Vault, External Secrets, or the deployment
|
|
391
|
+
secret store. Never commit it.
|
|
392
|
+
8. Set \`OIDC_JWKS_URL\` only when the broker does not expose keys at the
|
|
393
|
+
default ZITADEL-compatible \`/oauth/v2/keys\` path.
|
|
394
|
+
|
|
395
|
+
## Environment Contract
|
|
396
|
+
|
|
397
|
+
- \`OIDC_ISSUER_URL\`: OIDC issuer URL, for example \`https://auth.example.com\`.
|
|
398
|
+
- \`OIDC_CLIENT_ID\`: dashboard/application client ID.
|
|
399
|
+
- \`OIDC_CLIENT_SECRET\`: secret loaded from the environment secret store.
|
|
400
|
+
- \`OIDC_CALLBACK_URL\`: exact redirect URI registered on the OIDC client.
|
|
401
|
+
- \`OIDC_SCOPES\`: comma-separated scopes, usually \`openid,profile,email\`.
|
|
402
|
+
- \`OIDC_ALLOWED_EMAIL_DOMAINS\`: comma-separated allow-list for staff/operator
|
|
403
|
+
domains. Leave empty only when the dashboard has a stronger local allow-list.
|
|
404
|
+
- \`OIDC_ROLE_CLAIM\`: token claim used for roles. Defaults to \`roles\`.
|
|
405
|
+
- \`OIDC_GROUP_CLAIM\`: token claim used for groups. Defaults to \`groups\`.
|
|
406
|
+
|
|
407
|
+
## Role And Group Mapping Hook
|
|
408
|
+
|
|
409
|
+
\`oidc-access-mapping.ts\` is intentionally framework-neutral. Use
|
|
410
|
+
\`defaultOidcDashboardAccessMapping\` as the safe default, then wrap or replace
|
|
411
|
+
it when a dashboard needs to map broker roles/groups to local roles, tenant
|
|
412
|
+
access, or PARC permissions. Keep this mapping after token verification and
|
|
413
|
+
before local session creation.
|
|
414
|
+
|
|
415
|
+
## Laravel/Filament Pattern
|
|
416
|
+
|
|
417
|
+
Laravel dashboards should use Socialite with an OpenID Connect provider.
|
|
418
|
+
Filament panels can either:
|
|
419
|
+
|
|
420
|
+
- use a Filament Socialite plugin when the panel wants provider buttons and
|
|
421
|
+
allow-list callbacks; or
|
|
422
|
+
- use a custom Socialite controller when tenant/PARC assignment is more complex.
|
|
423
|
+
|
|
424
|
+
The same rule applies: Socialite proves who logged in; the dashboard still needs
|
|
425
|
+
local/PARC authorization before showing resources.
|
|
426
|
+
|
|
427
|
+
## Next.js/Node Pattern
|
|
428
|
+
|
|
429
|
+
Next.js dashboards should use Auth.js/NextAuth or \`openid-client\` with the
|
|
430
|
+
same issuer, client, callback URL, and scopes from the environment contract. In
|
|
431
|
+
the sign-in callback, enforce \`OIDC_ALLOWED_EMAIL_DOMAINS\`, then call the same
|
|
432
|
+
role/group mapping hook before writing the local session. Plain Node services
|
|
433
|
+
that receive bearer tokens can reuse the generated \`jose\` verifier pattern and
|
|
434
|
+
map roles/groups before applying route permissions.
|
|
435
|
+
|
|
436
|
+
## Required Redirect Shape
|
|
437
|
+
|
|
438
|
+
Create one OIDC application per dashboard/environment. The redirect URI must
|
|
439
|
+
match the app exactly, for example:
|
|
440
|
+
|
|
441
|
+
\`\`\`text
|
|
442
|
+
https://dashboard.example.com/auth/oidc/callback
|
|
443
|
+
\`\`\`
|
|
444
|
+
|
|
445
|
+
For private/VPN-only staging surfaces, HTTP can be acceptable inside the private
|
|
446
|
+
network when the edge already terminates transport securely elsewhere, but
|
|
447
|
+
production dashboards should use HTTPS.
|
|
448
|
+
`;
|
|
449
|
+
const envExampleContent = `# OIDC dashboard SSO
|
|
450
|
+
OIDC_ISSUER_URL=https://auth.example.com
|
|
451
|
+
OIDC_CLIENT_ID=replace-me
|
|
452
|
+
OIDC_CLIENT_SECRET=replace-me
|
|
453
|
+
OIDC_CALLBACK_URL=https://dashboard.example.com/auth/oidc/callback
|
|
454
|
+
OIDC_POST_LOGOUT_REDIRECT_URL=https://dashboard.example.com
|
|
455
|
+
OIDC_SCOPES=openid,profile,email
|
|
456
|
+
OIDC_AUDIENCE=replace-me
|
|
457
|
+
OIDC_JWKS_URL=
|
|
458
|
+
OIDC_ROLE_CLAIM=roles
|
|
459
|
+
OIDC_GROUP_CLAIM=groups
|
|
460
|
+
OIDC_ADMIN_EMAILS=admin@example.com
|
|
461
|
+
OIDC_ALLOWED_EMAIL_DOMAINS=example.com
|
|
462
|
+
`;
|
|
463
|
+
//# sourceMappingURL=oidc-dashboard.recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-dashboard.recipe.js","sourceRoot":"","sources":["../../../src/commands/recipes/oidc-dashboard.recipe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function applyOidcResourceServerRecipe(basePath: string, dryRun?: boolean): Promise<void>;
|