@marxbiotech/signet-integration 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/dist/bearer-challenge.d.ts +27 -0
  4. package/dist/bearer-challenge.filter.d.ts +13 -0
  5. package/dist/bearer-challenge.filter.js +76 -0
  6. package/dist/bearer-challenge.filter.js.map +1 -0
  7. package/dist/bearer-challenge.js +120 -0
  8. package/dist/bearer-challenge.js.map +1 -0
  9. package/dist/config.d.ts +23 -0
  10. package/dist/config.js +56 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/deployment-profile.d.ts +25 -0
  13. package/dist/deployment-profile.js +103 -0
  14. package/dist/deployment-profile.js.map +1 -0
  15. package/dist/guard.d.ts +18 -0
  16. package/dist/guard.js +210 -0
  17. package/dist/guard.js.map +1 -0
  18. package/dist/index.d.ts +15 -0
  19. package/dist/index.js +50 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/jwt-verifier.d.ts +36 -0
  22. package/dist/jwt-verifier.js +167 -0
  23. package/dist/jwt-verifier.js.map +1 -0
  24. package/dist/log-sanitise.d.ts +3 -0
  25. package/dist/log-sanitise.js +23 -0
  26. package/dist/log-sanitise.js.map +1 -0
  27. package/dist/module.d.ts +12 -0
  28. package/dist/module.js +61 -0
  29. package/dist/module.js.map +1 -0
  30. package/dist/options.d.ts +31 -0
  31. package/dist/options.js +9 -0
  32. package/dist/options.js.map +1 -0
  33. package/dist/principal-resolver.d.ts +19 -0
  34. package/dist/principal-resolver.js +25 -0
  35. package/dist/principal-resolver.js.map +1 -0
  36. package/dist/principal.decorator.d.ts +2 -0
  37. package/dist/principal.decorator.js +28 -0
  38. package/dist/principal.decorator.js.map +1 -0
  39. package/dist/protected-resource.controller.d.ts +11 -0
  40. package/dist/protected-resource.controller.js +130 -0
  41. package/dist/protected-resource.controller.js.map +1 -0
  42. package/dist/public.decorator.d.ts +2 -0
  43. package/dist/public.decorator.js +11 -0
  44. package/dist/public.decorator.js.map +1 -0
  45. package/dist/scope-vocabulary.d.ts +17 -0
  46. package/dist/scope-vocabulary.js +52 -0
  47. package/dist/scope-vocabulary.js.map +1 -0
  48. package/dist/strategy.d.ts +16 -0
  49. package/dist/strategy.js +89 -0
  50. package/dist/strategy.js.map +1 -0
  51. package/dist/testing/fixture.d.ts +8 -0
  52. package/dist/testing/fixture.js +38 -0
  53. package/dist/testing/fixture.js.map +1 -0
  54. package/dist/testing/index.d.ts +3 -0
  55. package/dist/testing/index.js +11 -0
  56. package/dist/testing/index.js.map +1 -0
  57. package/dist/testing/jwks-server.d.ts +10 -0
  58. package/dist/testing/jwks-server.js +33 -0
  59. package/dist/testing/jwks-server.js.map +1 -0
  60. package/dist/testing/signet-token.d.ts +18 -0
  61. package/dist/testing/signet-token.js +31 -0
  62. package/dist/testing/signet-token.js.map +1 -0
  63. package/dist/validate-options.d.ts +8 -0
  64. package/dist/validate-options.js +88 -0
  65. package/dist/validate-options.js.map +1 -0
  66. package/package.json +93 -0
@@ -0,0 +1,18 @@
1
+ import { type JSONWebKeySet, type JWTPayload } from 'jose';
2
+ import { type JwksServer } from './jwks-server';
3
+ export interface TestIssuer {
4
+ readonly close: () => Promise<void>;
5
+ readonly jwks: JSONWebKeySet;
6
+ readonly keys: JwksServer;
7
+ readonly sign: (payload: JWTPayload, header?: {
8
+ typ?: string;
9
+ }) => Promise<string>;
10
+ readonly signSignetToken: (input: {
11
+ audience: string;
12
+ clientId: string;
13
+ issuer: string;
14
+ scopes: readonly string[];
15
+ subject: string;
16
+ }) => Promise<string>;
17
+ }
18
+ export declare function startTestIssuer(): Promise<TestIssuer>;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startTestIssuer = startTestIssuer;
4
+ const jose_1 = require("jose");
5
+ const jwks_server_1 = require("./jwks-server");
6
+ async function startTestIssuer() {
7
+ const pair = await (0, jose_1.generateKeyPair)('ES256');
8
+ const signingKey = pair.privateKey;
9
+ const jwks = {
10
+ keys: [{ ...(await (0, jose_1.exportJWK)(pair.publicKey)), alg: 'ES256', kid: 'k1' }],
11
+ };
12
+ const keys = await (0, jwks_server_1.startJwksServer)(jwks);
13
+ const sign = (payload, header = {}) => new jose_1.SignJWT(payload)
14
+ .setProtectedHeader({ alg: 'ES256', kid: 'k1', ...header })
15
+ .setExpirationTime('5m')
16
+ .sign(signingKey);
17
+ return {
18
+ close: () => keys.close(),
19
+ jwks,
20
+ keys,
21
+ sign,
22
+ signSignetToken: ({ audience, clientId, issuer, scopes, subject }) => sign({
23
+ aud: [audience],
24
+ client_id: clientId,
25
+ iss: issuer,
26
+ scp: [...scopes],
27
+ sub: subject,
28
+ }),
29
+ };
30
+ }
31
+ //# sourceMappingURL=signet-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signet-token.js","sourceRoot":"","sources":["../../src/testing/signet-token.ts"],"names":[],"mappings":";;AAgCA,0CA0BC;AA1DD,+BAOc;AAEd,+CAAiE;AAuB1D,KAAK,UAAU,eAAe;IACnC,MAAM,IAAI,GAAG,MAAM,IAAA,sBAAe,EAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAY,IAAI,CAAC,UAAU,CAAC;IAC5C,MAAM,IAAI,GAAkB;QAC1B,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,IAAA,gBAAS,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;KAC1E,CAAC;IACF,MAAM,IAAI,GAAG,MAAM,IAAA,6BAAe,EAAC,IAAI,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,CAAC,OAAmB,EAAE,SAA2B,EAAE,EAAE,EAAE,CAClE,IAAI,cAAO,CAAC,OAAO,CAAC;SACjB,kBAAkB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;SAC1D,iBAAiB,CAAC,IAAI,CAAC;SACvB,IAAI,CAAC,UAAU,CAAC,CAAC;IACtB,OAAO;QACL,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE;QACzB,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,eAAe,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,CACnE,IAAI,CAAC;YACH,GAAG,EAAE,CAAC,QAAQ,CAAC;YACf,SAAS,EAAE,QAAQ;YACnB,GAAG,EAAE,MAAM;YACX,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC;YAChB,GAAG,EAAE,OAAO;SACb,CAAC;KACL,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type SignetIntegrationOptions } from './options';
2
+ /**
3
+ * Checks the invariants the mechanism relies on and the comments state, once,
4
+ * at construction (`SignetIntegrationModule.forRoot`). Every rule here is one
5
+ * a second consumer could break by mistake without any type error; each
6
+ * throws with the field named. Returns the options for chaining.
7
+ */
8
+ export declare function validateSignetIntegrationOptions<E extends string>(options: SignetIntegrationOptions<E>): SignetIntegrationOptions<E>;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateSignetIntegrationOptions = validateSignetIntegrationOptions;
4
+ const options_1 = require("./options");
5
+ /**
6
+ * Request properties Express and Nest already use; a principal written there
7
+ * would overwrite them.
8
+ */
9
+ const RESERVED_REQUEST_KEYS = new Set([
10
+ 'app',
11
+ 'body',
12
+ 'cookies',
13
+ 'headers',
14
+ 'hostname',
15
+ 'ip',
16
+ 'ips',
17
+ 'method',
18
+ 'originalUrl',
19
+ 'params',
20
+ 'path',
21
+ 'protocol',
22
+ 'query',
23
+ 'rawHeaders',
24
+ 'res',
25
+ 'route',
26
+ 'signedCookies',
27
+ 'url',
28
+ ]);
29
+ /**
30
+ * Characters that would break out of the quoted value in a
31
+ * `WWW-Authenticate` parameter (RFC 7235 quoted-string).
32
+ */
33
+ const QUOTE_BREAKERS = /["\\\r\n]/;
34
+ /**
35
+ * Checks the invariants the mechanism relies on and the comments state, once,
36
+ * at construction (`SignetIntegrationModule.forRoot`). Every rule here is one
37
+ * a second consumer could break by mistake without any type error; each
38
+ * throws with the field named. Returns the options for chaining.
39
+ */
40
+ function validateSignetIntegrationOptions(options) {
41
+ const fail = (reason) => {
42
+ throw new Error(`invalid SignetIntegrationOptions: ${reason}`);
43
+ };
44
+ if (options.admissionScope === '' ||
45
+ QUOTE_BREAKERS.test(options.admissionScope)) {
46
+ fail('admissionScope must be non-empty and contain no quote, backslash or newline');
47
+ }
48
+ if (options.realm === '' || QUOTE_BREAKERS.test(options.realm)) {
49
+ fail('realm must be non-empty and contain no quote, backslash or newline');
50
+ }
51
+ if (!options.scopesSupported.includes(options.admissionScope)) {
52
+ fail(`scopesSupported must include the admission scope ${options.admissionScope}`);
53
+ }
54
+ if (RESERVED_REQUEST_KEYS.has(options.requestPrincipalKey)) {
55
+ fail(`requestPrincipalKey ${options.requestPrincipalKey} is a request property Express or Nest already uses`);
56
+ }
57
+ const profiles = Object.entries(options.deployedProfiles);
58
+ if (profiles.length === 0)
59
+ fail('deployedProfiles must name at least one environment');
60
+ if (profiles.some(([e]) => e === options_1.DEVELOPMENT_ENVIRONMENT)) {
61
+ fail(`deployedProfiles must not contain ${options_1.DEVELOPMENT_ENVIRONMENT}; the resolver stamps it`);
62
+ }
63
+ const namespaces = new Set(profiles.map(([, p]) => p.namespace));
64
+ if (namespaces.size !== profiles.length)
65
+ fail('deployedProfiles namespaces must be unique');
66
+ const hosts = new Set(profiles.map(([, p]) => p.host));
67
+ if (hosts.size !== profiles.length)
68
+ fail('deployedProfiles hosts must be unique');
69
+ // Every canonical resource -- deployed and development -- must carry the
70
+ // same path with no query or fragment: the metadata route is mounted once,
71
+ // on that path, and the challenge points at it.
72
+ const resourcePath = (resource, label) => {
73
+ const url = new URL(resource);
74
+ if (url.search !== '' || url.hash !== '') {
75
+ fail(`${label} must not carry a query or fragment: ${resource}`);
76
+ }
77
+ return url.pathname;
78
+ };
79
+ const developmentPath = resourcePath(options.developmentProfile.canonicalResource, 'developmentProfile.canonicalResource');
80
+ for (const [environment, { host }] of profiles) {
81
+ const path = resourcePath(options.canonicalResourceFor(host), `canonicalResourceFor(${host})`);
82
+ if (path !== developmentPath) {
83
+ fail(`canonicalResourceFor(${host}) has path ${path} but developmentProfile has ${developmentPath} (environment ${environment}); every profile must share one resource path`);
84
+ }
85
+ }
86
+ return options;
87
+ }
88
+ //# sourceMappingURL=validate-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-options.js","sourceRoot":"","sources":["../src/validate-options.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "@marxbiotech/signet-integration",
3
+ "version": "0.1.0",
4
+ "description": "Signet (OAuth 2.1 / OIDC) resource-server integration for NestJS: Bearer verification, RFC 6750 challenges, RFC 9728 metadata, deployment profile.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/marxbiotech/ordersync-marxbio-tech.git",
9
+ "directory": "packages/signet-integration"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public",
13
+ "registry": "https://registry.npmjs.org"
14
+ },
15
+ "main": "dist/index.js",
16
+ "types": "dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./testing": {
23
+ "types": "./dist/testing/index.d.ts",
24
+ "default": "./dist/testing/index.js"
25
+ }
26
+ },
27
+ "typesVersions": {
28
+ "*": {
29
+ "testing": [
30
+ "dist/testing/index.d.ts"
31
+ ]
32
+ }
33
+ },
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.build.json",
39
+ "prepublishOnly": "npm run build",
40
+ "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
41
+ "lint": "eslint \"src/**/*.ts\" --max-warnings=0",
42
+ "test": "TZ=UTC jest",
43
+ "test:cov": "TZ=UTC jest --coverage --runInBand",
44
+ "typecheck": "tsc --noEmit"
45
+ },
46
+ "peerDependencies": {
47
+ "@nestjs/common": "^10.0.0 || ^11.0.0",
48
+ "@nestjs/config": "^3.0.0 || ^4.0.0",
49
+ "@nestjs/core": "^10.0.0 || ^11.0.0",
50
+ "@nestjs/passport": "^10.0.0 || ^11.0.0",
51
+ "@nestjs/swagger": "^8.0.0 || ^11.0.0",
52
+ "@nestjs/throttler": "^6.0.0",
53
+ "@types/express": "^4.17.0 || ^5.0.0",
54
+ "jose": "^5.10.0",
55
+ "passport-custom": "^1.1.1",
56
+ "reflect-metadata": "^0.2.0"
57
+ },
58
+ "peerDependenciesMeta": {
59
+ "@types/express": {
60
+ "optional": true
61
+ }
62
+ },
63
+ "devDependencies": {
64
+ "@nestjs/common": "^10.4.15",
65
+ "@nestjs/config": "^3.3.0",
66
+ "@nestjs/core": "^10.4.15",
67
+ "@nestjs/passport": "^10.0.3",
68
+ "@nestjs/platform-express": "^10.4.15",
69
+ "@nestjs/swagger": "^8.1.1",
70
+ "@nestjs/testing": "^10.4.15",
71
+ "@nestjs/throttler": "^6.5.0",
72
+ "@types/express": "^5.0.0",
73
+ "@types/jest": "^29.5.14",
74
+ "@types/node": "^22.10.7",
75
+ "@types/passport": "^1.0.16",
76
+ "@typescript-eslint/eslint-plugin": "^8.20.0",
77
+ "@typescript-eslint/parser": "^8.20.0",
78
+ "eslint": "^8.57.1",
79
+ "eslint-config-prettier": "^10.0.1",
80
+ "eslint-plugin-prettier": "^5.2.3",
81
+ "eslint-plugin-simple-import-sort": "^12.1.1",
82
+ "express": "^5.0.0",
83
+ "jest": "^29.7.0",
84
+ "jose": "5.10.0",
85
+ "passport": "^0.7.0",
86
+ "passport-custom": "^1.1.1",
87
+ "prettier": "^3.4.2",
88
+ "reflect-metadata": "^0.2.2",
89
+ "rxjs": "^7.8.1",
90
+ "ts-jest": "^29.2.5",
91
+ "typescript": "^5.7.3"
92
+ }
93
+ }