@decaf-ts/integrations 0.2.0 → 0.2.2

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 (49) hide show
  1. package/dist/integrations.cjs +1 -1
  2. package/dist/integrations.cjs.map +1 -1
  3. package/dist/integrations.js +1 -1
  4. package/dist/integrations.js.map +1 -1
  5. package/lib/cjs/index.cjs +3 -3
  6. package/lib/cjs/nest/authService.cjs +53 -2
  7. package/lib/cjs/nest/authService.cjs.map +1 -1
  8. package/lib/cjs/nest/index.cjs +2 -0
  9. package/lib/cjs/nest/index.cjs.map +1 -1
  10. package/lib/cjs/nest/keycloakAuthHandler.cjs +49 -23
  11. package/lib/cjs/nest/keycloakAuthHandler.cjs.map +1 -1
  12. package/lib/cjs/nest/keycloakModule.cjs +5 -3
  13. package/lib/cjs/nest/keycloakModule.cjs.map +1 -1
  14. package/lib/cjs/nest/logging.cjs +41 -0
  15. package/lib/cjs/nest/logging.cjs.map +1 -0
  16. package/lib/cjs/nest/utils.cjs +24 -0
  17. package/lib/cjs/nest/utils.cjs.map +1 -1
  18. package/lib/cjs/utils.cjs +1 -1
  19. package/lib/esm/index.js +3 -3
  20. package/lib/esm/nest/authService.js +54 -3
  21. package/lib/esm/nest/authService.js.map +1 -1
  22. package/lib/esm/nest/index.js +2 -0
  23. package/lib/esm/nest/index.js.map +1 -1
  24. package/lib/esm/nest/keycloakAuthHandler.js +49 -23
  25. package/lib/esm/nest/keycloakAuthHandler.js.map +1 -1
  26. package/lib/esm/nest/keycloakModule.js +5 -3
  27. package/lib/esm/nest/keycloakModule.js.map +1 -1
  28. package/lib/esm/nest/logging.js +38 -0
  29. package/lib/esm/nest/logging.js.map +1 -0
  30. package/lib/esm/nest/utils.js +23 -0
  31. package/lib/esm/nest/utils.js.map +1 -1
  32. package/lib/esm/utils.js +1 -1
  33. package/lib/types/index.d.cts +2 -2
  34. package/lib/types/index.d.mts +2 -2
  35. package/lib/types/nest/authService.d.cts +55 -2
  36. package/lib/types/nest/authService.d.mts +55 -2
  37. package/lib/types/nest/index.d.cts +2 -0
  38. package/lib/types/nest/index.d.mts +2 -0
  39. package/lib/types/nest/keycloakAuthHandler.d.cts +51 -5
  40. package/lib/types/nest/keycloakAuthHandler.d.mts +51 -5
  41. package/lib/types/nest/keycloakModule.d.cts +3 -3
  42. package/lib/types/nest/keycloakModule.d.mts +3 -3
  43. package/lib/types/nest/logging.d.cts +1 -0
  44. package/lib/types/nest/logging.d.mts +1 -0
  45. package/lib/types/nest/types.d.cts +23 -18
  46. package/lib/types/nest/types.d.mts +23 -18
  47. package/lib/types/nest/utils.d.cts +9 -0
  48. package/lib/types/nest/utils.d.mts +9 -0
  49. package/package.json +32 -3
@@ -1,2 +1,2 @@
1
- var e,o;e=this,o=(e,o,r,i)=>{"use strict";e.COMMIT="96f5884",e.FULL_VERSION="0.1.4-96f5884",e.PACKAGE_NAME="@decaf-ts/integrations",e.VERSION="0.1.4",e.execWithLogging=async(e,n,t,s=r.LogLevel.info)=>(t.debug("Executing: "+e),new Promise((r,d)=>{const c=o.exec(e,n),a=[],l=[];c.stdout?.on("data",e=>{a.push(e),t[s](e.trim())}),c.stderr?.on("data",e=>{l.push(e),t.error(e.trim())}),c.on("close",e=>{0===e?(t.silly("Command completed successfully"),r({stdout:a.join("\n"),stderr:l.join("\n")})):(t.critical(`Command failed with code ${e}:\n ${l.join("\n")}`),d(new i.InternalError(`Command failed with code ${e}: ${l.join("\n")}`)))}),c.on("error",e=>{t.critical("Command execution error",e),d(e)})}))},"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("child_process"),require("@decaf-ts/logging"),require("@decaf-ts/db-decorators")):"function"==typeof define&&define.amd?define(["exports","child_process","@decaf-ts/logging","@decaf-ts/db-decorators"],o):o((e="undefined"!=typeof globalThis?globalThis:e||self).integrations={},e.childProcess,e.decafTsLogging,e.decafTsDbDecorators);
1
+ var e,o;e=this,o=(e,o,i,n)=>{"use strict";e.COMMIT="4990f39",e.FULL_VERSION="0.2.1-4990f39",e.PACKAGE_NAME="@decaf-ts/integrations",e.VERSION="0.2.1",e.execWithLogging=async(e,t,r,d=i.LogLevel.info)=>(r.debug("Executing: "+e),new Promise((i,s)=>{const c=o.exec(e,t),a=[],l=[];c.stdout?.on("data",e=>{a.push(e),r[d](e.trim())}),c.stderr?.on("data",e=>{l.push(e),r.debug(e.trim())}),c.on("close",e=>{0===e?(r.silly("Command completed successfully"),i({stdout:a.join("\n"),stderr:l.join("\n")})):(r.critical(`Command failed with code ${e}:\n ${l.join("\n")}`),s(new n.InternalError(`Command failed with code ${e}: ${l.join("\n")}`)))}),c.on("error",e=>{r.critical("Command execution error",e),s(e)})}))},"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("child_process"),require("@decaf-ts/logging"),require("@decaf-ts/db-decorators")):"function"==typeof define&&define.amd?define(["exports","child_process","@decaf-ts/logging","@decaf-ts/db-decorators"],o):o((e="undefined"!=typeof globalThis?globalThis:e||self).integrations={},e.childProcess,e.decafTsLogging,e.decafTsDbDecorators);
2
2
  //# sourceMappingURL=integrations.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"integrations.cjs","sources":["../src/index.ts","../src/utils.ts"],"sourcesContent":[null,null],"names":["async","command","options","log","level","LogLevel","info","debug","Promise","resolve","reject","child","exec","stdout","stderr","on","data","push","trim","error","code","silly","join","critical","InternalError","err"],"mappings":"mDASsB,4BAEM,kCAEQ,wBANL,gCCExBA,MACLC,EACAC,EACAC,EACAC,EAAkBC,EAAAA,SAASC,QAE3BH,EAAII,MAAM,cAAcN,GAEjB,IAAIO,QAAQ,CAACC,EAASC,KAC3B,MAAMC,EAAQC,EAAAA,KAAKX,EAASC,GAEtBW,EAAmB,GACnBC,EAAmB,GAEzBH,EAAME,QAAQE,GAAG,OAASC,IACxBH,EAAOI,KAAKD,GACZb,EAAIC,GAAOY,EAAKE,UAGlBP,EAAMG,QAAQC,GAAG,OAASC,IACxBF,EAAOG,KAAKD,GACZb,EAAIgB,MAAMH,EAAKE,UAGjBP,EAAMI,GAAG,QAAUK,IACJ,IAATA,GACFjB,EAAIkB,MAAM,kCACVZ,EAAQ,CAAEI,OAAQA,EAAOS,KAAK,MAAOR,OAAQA,EAAOQ,KAAK,UAEzDnB,EAAIoB,SACF,4BAA4BH,QAAWN,EAAOQ,KAAK,SAErDZ,EACE,IAAIc,EAAAA,cACF,4BAA4BJ,MAASN,EAAOQ,KAAK,aAMzDX,EAAMI,GAAG,QAAUU,IACjBtB,EAAIoB,SAAS,0BAA2BE,GACxCf,EAAOe"}
1
+ {"version":3,"file":"integrations.cjs","sources":["../src/index.ts","../src/utils.ts"],"sourcesContent":[null,null],"names":["async","command","options","log","level","LogLevel","info","debug","Promise","resolve","reject","child","exec","stdout","stderr","on","data","push","trim","code","silly","join","critical","InternalError","err"],"mappings":"mDASsB,4BAEM,kCAEQ,wBANL,gCCExBA,MACLC,EACAC,EACAC,EACAC,EAAkBC,EAAAA,SAASC,QAE3BH,EAAII,MAAM,cAAcN,GAEjB,IAAIO,QAAQ,CAACC,EAASC,KAC3B,MAAMC,EAAQC,EAAAA,KAAKX,EAASC,GAEtBW,EAAmB,GACnBC,EAAmB,GAEzBH,EAAME,QAAQE,GAAG,OAASC,IACxBH,EAAOI,KAAKD,GACZb,EAAIC,GAAOY,EAAKE,UAGlBP,EAAMG,QAAQC,GAAG,OAASC,IACxBF,EAAOG,KAAKD,GACZb,EAAII,MAAMS,EAAKE,UAGjBP,EAAMI,GAAG,QAAUI,IACJ,IAATA,GACFhB,EAAIiB,MAAM,kCACVX,EAAQ,CAAEI,OAAQA,EAAOQ,KAAK,MAAOP,OAAQA,EAAOO,KAAK,UAEzDlB,EAAImB,SACF,4BAA4BH,QAAWL,EAAOO,KAAK,SAErDX,EACE,IAAIa,EAAAA,cACF,4BAA4BJ,MAASL,EAAOO,KAAK,aAMzDV,EAAMI,GAAG,QAAUS,IACjBrB,EAAImB,SAAS,0BAA2BE,GACxCd,EAAOc"}
@@ -1,2 +1,2 @@
1
- import{exec as o}from"child_process";import{LogLevel as r}from"@decaf-ts/logging";import{InternalError as n}from"@decaf-ts/db-decorators";async function t(t,e,i,c=r.info){return i.debug("Executing: "+t),new Promise((r,d)=>{const s=o(t,e),m=[],a=[];s.stdout?.on("data",o=>{m.push(o),i[c](o.trim())}),s.stderr?.on("data",o=>{a.push(o),i.error(o.trim())}),s.on("close",o=>{0===o?(i.silly("Command completed successfully"),r({stdout:m.join("\n"),stderr:a.join("\n")})):(i.critical(`Command failed with code ${o}:\n ${a.join("\n")}`),d(new n(`Command failed with code ${o}: ${a.join("\n")}`)))}),s.on("error",o=>{i.critical("Command execution error",o),d(o)})})}const e="##VERSION##",i="##COMMIT##",c="##FULL_VERSION##",d="##PACKAGE##";export{i as COMMIT,c as FULL_VERSION,d as PACKAGE_NAME,e as VERSION,t as execWithLogging};
1
+ import{exec as o}from"child_process";import{LogLevel as n}from"@decaf-ts/logging";import{InternalError as t}from"@decaf-ts/db-decorators";async function r(r,e,i,d=n.info){return i.debug("Executing: "+r),new Promise((n,c)=>{const s=o(r,e),m=[],a=[];s.stdout?.on("data",o=>{m.push(o),i[d](o.trim())}),s.stderr?.on("data",o=>{a.push(o),i.debug(o.trim())}),s.on("close",o=>{0===o?(i.silly("Command completed successfully"),n({stdout:m.join("\n"),stderr:a.join("\n")})):(i.critical(`Command failed with code ${o}:\n ${a.join("\n")}`),c(new t(`Command failed with code ${o}: ${a.join("\n")}`)))}),s.on("error",o=>{i.critical("Command execution error",o),c(o)})})}const e="##VERSION##",i="##COMMIT##",d="##FULL_VERSION##",c="##PACKAGE##";export{i as COMMIT,d as FULL_VERSION,c as PACKAGE_NAME,e as VERSION,r as execWithLogging};
2
2
  //# sourceMappingURL=integrations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"integrations.js","sources":["../src/utils.ts","../src/index.ts"],"sourcesContent":[null,null],"names":["async","execWithLogging","command","options","log","level","LogLevel","info","debug","Promise","resolve","reject","child","exec","stdout","stderr","on","data","push","trim","error","code","silly","join","critical","InternalError","err","VERSION","COMMIT","FULL_VERSION","PACKAGE_NAME"],"mappings":"0IASOA,eAAeC,EACpBC,EACAC,EACAC,EACAC,EAAkBC,EAASC,MAI3B,OAFAH,EAAII,MAAM,cAAcN,GAEjB,IAAIO,QAAQ,CAACC,EAASC,KAC3B,MAAMC,EAAQC,EAAKX,EAASC,GAEtBW,EAAmB,GACnBC,EAAmB,GAEzBH,EAAME,QAAQE,GAAG,OAASC,IACxBH,EAAOI,KAAKD,GACZb,EAAIC,GAAOY,EAAKE,UAGlBP,EAAMG,QAAQC,GAAG,OAASC,IACxBF,EAAOG,KAAKD,GACZb,EAAIgB,MAAMH,EAAKE,UAGjBP,EAAMI,GAAG,QAAUK,IACJ,IAATA,GACFjB,EAAIkB,MAAM,kCACVZ,EAAQ,CAAEI,OAAQA,EAAOS,KAAK,MAAOR,OAAQA,EAAOQ,KAAK,UAEzDnB,EAAIoB,SACF,4BAA4BH,QAAWN,EAAOQ,KAAK,SAErDZ,EACE,IAAIc,EACF,4BAA4BJ,MAASN,EAAOQ,KAAK,aAMzDX,EAAMI,GAAG,QAAUU,IACjBtB,EAAIoB,SAAS,0BAA2BE,GACxCf,EAAOe,MAGb,CC/CO,MAAMC,EAAkB,cAElBC,EAAS,aAETC,EAAe,mBAEfC,EAAuB"}
1
+ {"version":3,"file":"integrations.js","sources":["../src/utils.ts","../src/index.ts"],"sourcesContent":[null,null],"names":["async","execWithLogging","command","options","log","level","LogLevel","info","debug","Promise","resolve","reject","child","exec","stdout","stderr","on","data","push","trim","code","silly","join","critical","InternalError","err","VERSION","COMMIT","FULL_VERSION","PACKAGE_NAME"],"mappings":"0IASOA,eAAeC,EACpBC,EACAC,EACAC,EACAC,EAAkBC,EAASC,MAI3B,OAFAH,EAAII,MAAM,cAAcN,GAEjB,IAAIO,QAAQ,CAACC,EAASC,KAC3B,MAAMC,EAAQC,EAAKX,EAASC,GAEtBW,EAAmB,GACnBC,EAAmB,GAEzBH,EAAME,QAAQE,GAAG,OAASC,IACxBH,EAAOI,KAAKD,GACZb,EAAIC,GAAOY,EAAKE,UAGlBP,EAAMG,QAAQC,GAAG,OAASC,IACxBF,EAAOG,KAAKD,GACZb,EAAII,MAAMS,EAAKE,UAGjBP,EAAMI,GAAG,QAAUI,IACJ,IAATA,GACFhB,EAAIiB,MAAM,kCACVX,EAAQ,CAAEI,OAAQA,EAAOQ,KAAK,MAAOP,OAAQA,EAAOO,KAAK,UAEzDlB,EAAImB,SACF,4BAA4BH,QAAWL,EAAOO,KAAK,SAErDX,EACE,IAAIa,EACF,4BAA4BJ,MAASL,EAAOO,KAAK,aAMzDV,EAAMI,GAAG,QAAUS,IACjBrB,EAAImB,SAAS,0BAA2BE,GACxCd,EAAOc,MAGb,CC/CO,MAAMC,EAAkB,cAElBC,EAAS,aAETC,EAAe,mBAEfC,EAAuB"}
package/lib/cjs/index.cjs CHANGED
@@ -21,9 +21,9 @@ exports.PACKAGE_NAME = exports.FULL_VERSION = exports.COMMIT = exports.VERSION =
21
21
  * @description Exposes the package utilities and build-time version metadata for `@decaf-ts/integrations`.
22
22
  */
23
23
  __exportStar(require("./utils.cjs"), exports);
24
- exports.VERSION = "0.1.4";
25
- exports.COMMIT = "96f5884";
26
- exports.FULL_VERSION = "0.1.4-96f5884";
24
+ exports.VERSION = "0.2.1";
25
+ exports.COMMIT = "4990f39";
26
+ exports.FULL_VERSION = "0.2.1-4990f39";
27
27
  exports.PACKAGE_NAME = "@decaf-ts/integrations";
28
28
  //# sourceMappingURL=index.js.map
29
29
  //# sourceMappingURL=index.cjs.map
@@ -5,15 +5,28 @@ exports.AuthService = void 0;
5
5
  * @module integrations/nest/authService
6
6
  * @summary Nest-compatible authentication service helpers.
7
7
  * @description Request-context-aware auth helpers for Nest applications using Decaf integration patterns.
8
+ *
9
+ * Supports two modes:
10
+ * - **Verify mode** (default when `verifyUrl` is set): validates the JWT signature
11
+ * against the Keycloak JWKS endpoint using `jose.jwtVerify`.
12
+ * - **Decode-only mode** (when `verifyToken` is false / `verifyUrl` is empty):
13
+ * decodes the JWT without signature verification (dev/local mode).
8
14
  */
9
15
  const core_1 = require("@decaf-ts/core");
16
+ const jose_1 = require("jose");
10
17
  const utils_js_1 = require("./utils.cjs");
11
18
  class AuthService {
19
+ constructor(options = {}) {
20
+ this.verifyToken = !!options.verifyToken && !!options.verifyUrl;
21
+ this.verifyUrl = options.verifyUrl ?? "";
22
+ this.clockToleranceSeconds = options.clockToleranceSeconds ?? 5;
23
+ this.excludedClients = options.excludedClients ?? ["account"];
24
+ }
12
25
  getTokenPayload(jwt) {
13
26
  return (0, utils_js_1.getTokenPayload)(jwt);
14
27
  }
15
28
  getRoles(jwt) {
16
- return (0, utils_js_1.extractKeycloakRoles)(this.getTokenPayload(jwt));
29
+ return this.extractKeycloakRoles(this.getTokenPayload(jwt));
17
30
  }
18
31
  getUser(jwt) {
19
32
  return (0, utils_js_1.getUser)(jwt);
@@ -21,16 +34,54 @@ class AuthService {
21
34
  extractKeycloakRoles(payload) {
22
35
  return (0, utils_js_1.extractKeycloakRoles)(payload);
23
36
  }
37
+ /**
38
+ * Extracts client roles from the `resource_access` claim, excluding
39
+ * the configured `excludedClients` (default: `["account"]`).
40
+ */
41
+ extractClientRoles(payload) {
42
+ return (0, utils_js_1.getClientRoles)(payload, this.excludedClients);
43
+ }
24
44
  getRealmFromIssuer(jwt) {
25
45
  return (0, utils_js_1.getRealmFromIssuer)(jwt);
26
46
  }
27
- assertValidToken(jwt) {
47
+ /**
48
+ * Validates a JWT:
49
+ * - In **verify mode**: verifies the signature against the Keycloak JWKS
50
+ * endpoint using `jose.jwtVerify`, then returns the verified payload.
51
+ * - In **decode-only mode**: decodes the JWT without verification and
52
+ * returns the payload (or throws if unparseable).
53
+ *
54
+ * @throws {AuthorizationError} when the token is invalid, expired, or
55
+ * signature verification fails.
56
+ */
57
+ async assertValidToken(jwt) {
58
+ if (this.verifyToken) {
59
+ return this.verifyAgainstJwks(jwt);
60
+ }
28
61
  const payload = this.getTokenPayload(jwt);
29
62
  if (!payload) {
30
63
  throw new core_1.AuthorizationError("Invalid token");
31
64
  }
32
65
  return payload;
33
66
  }
67
+ /**
68
+ * Verifies the JWT signature against the Keycloak JWKS endpoint.
69
+ * Lazily creates the JWKS keystore on first call.
70
+ */
71
+ async verifyAgainstJwks(jwt) {
72
+ if (!this.jwks) {
73
+ this.jwks = (0, jose_1.createRemoteJWKSet)(new URL(this.verifyUrl));
74
+ }
75
+ try {
76
+ const { payload } = await (0, jose_1.jwtVerify)(jwt, this.jwks, {
77
+ clockTolerance: `${this.clockToleranceSeconds}s`,
78
+ });
79
+ return payload;
80
+ }
81
+ catch (e) {
82
+ throw new core_1.AuthorizationError("Invalid token: " + (e?.message ?? e));
83
+ }
84
+ }
34
85
  }
35
86
  exports.AuthService = AuthService;
36
87
  //# sourceMappingURL=authService.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nest/authService.cjs","sourceRoot":"","sources":["nest/authService.js"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,yCAAoD;AACpD,yCAAgG;AAChG,MAAa,WAAW;IACpB,eAAe,CAAC,GAAG;QACf,OAAO,IAAA,0BAAe,EAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IACD,QAAQ,CAAC,GAAG;QACR,OAAO,IAAA,+BAAoB,EAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,CAAC,GAAG;QACP,OAAO,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IACD,oBAAoB,CAAC,OAAO;QACxB,OAAO,IAAA,+BAAoB,EAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IACD,kBAAkB,CAAC,GAAG;QAClB,OAAO,IAAA,6BAAkB,EAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IACD,gBAAgB,CAAC,GAAG;QAChB,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,yBAAkB,CAAC,eAAe,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;CACJ;AAvBD,kCAuBC;AACD,uCAAuC"}
1
+ {"version":3,"file":"nest/authService.cjs","sourceRoot":"","sources":["nest/authService.js"],"names":[],"mappings":";;;AAAA;;;;;;;;;;GAUG;AACH,yCAAoD;AACpD,+BAAqD;AACrD,yCAAiH;AACjH,MAAa,WAAW;IACpB,YAAY,OAAO,GAAG,EAAE;QACpB,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;QAChE,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,IAAI,CAAC,CAAC;QAChE,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,CAAC,SAAS,CAAC,CAAC;IAClE,CAAC;IACD,eAAe,CAAC,GAAG;QACf,OAAO,IAAA,0BAAe,EAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IACD,QAAQ,CAAC,GAAG;QACR,OAAO,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,CAAC,GAAG;QACP,OAAO,IAAA,kBAAO,EAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IACD,oBAAoB,CAAC,OAAO;QACxB,OAAO,IAAA,+BAAoB,EAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IACD;;;OAGG;IACH,kBAAkB,CAAC,OAAO;QACtB,OAAO,IAAA,yBAAc,EAAC,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IACzD,CAAC;IACD,kBAAkB,CAAC,GAAG;QAClB,OAAO,IAAA,6BAAkB,EAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IACD;;;;;;;;;OASG;IACH,KAAK,CAAC,gBAAgB,CAAC,GAAG;QACtB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM,IAAI,yBAAkB,CAAC,eAAe,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;IACD;;;OAGG;IACH,KAAK,CAAC,iBAAiB,CAAC,GAAG;QACvB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACb,IAAI,CAAC,IAAI,GAAG,IAAA,yBAAkB,EAAC,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC5D,CAAC;QACD,IAAI,CAAC;YACD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAA,gBAAS,EAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE;gBAChD,cAAc,EAAE,GAAG,IAAI,CAAC,qBAAqB,GAAG;aACnD,CAAC,CAAC;YACH,OAAO,OAAO,CAAC;QACnB,CAAC;QACD,OAAO,CAAC,EAAE,CAAC;YACP,MAAM,IAAI,yBAAkB,CAAC,iBAAiB,GAAG,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC;IACL,CAAC;CACJ;AAnED,kCAmEC;AACD,uCAAuC"}
@@ -19,9 +19,11 @@ __exportStar(require("./utils.cjs"), exports);
19
19
  __exportStar(require("./authService.cjs"), exports);
20
20
  __exportStar(require("./keycloakAuthHandler.cjs"), exports);
21
21
  __exportStar(require("./keycloakModule.cjs"), exports);
22
+ require("./logging.cjs");
22
23
  /**
23
24
  * @summary Nest-style auth integrations.
24
25
  * @description Framework-agnostic auth helpers compatible with Decaf request context handling.
26
+ * Imports the `user`/`organization` log parameter registrations as a side effect.
25
27
  * @namespace nest
26
28
  * @memberOf module:integrations
27
29
  */
@@ -1 +1 @@
1
- {"version":3,"file":"nest/index.cjs","sourceRoot":"","sources":["nest/index.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,6CAA2B;AAC3B,mDAAiC;AACjC,2DAAyC;AACzC,sDAAoC;AACpC;;;;;GAKG;AACH,iCAAiC"}
1
+ {"version":3,"file":"nest/index.cjs","sourceRoot":"","sources":["nest/index.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,6CAA2B;AAC3B,mDAAiC;AACjC,2DAAyC;AACzC,sDAAoC;AACpC,wBAAsB;AACtB;;;;;;GAMG;AACH,iCAAiC"}
@@ -1,38 +1,57 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.KeycloakAuthHandler = void 0;
4
- /**
5
- * @module integrations/nest/keycloakAuthHandler
6
- * @summary Keycloak request auth handler.
7
- * @description Translates Keycloak JWT payloads into Decaf-friendly auth context values.
8
- */
3
+ exports.getClientRoles = exports.KeycloakAuthHandler = void 0;
9
4
  const core_1 = require("@decaf-ts/core");
5
+ const server_1 = require("@decaf-ts/for-http/server");
10
6
  const authService_js_1 = require("./authService.cjs");
11
7
  const utils_js_1 = require("./utils.cjs");
12
- class KeycloakAuthHandler {
13
- constructor(authService = new authService_js_1.AuthService()) {
14
- this.authService = authService;
8
+ Object.defineProperty(exports, "getClientRoles", { enumerable: true, get: function () { return utils_js_1.getClientRoles; } });
9
+ class KeycloakAuthHandler extends server_1.AuthHandler {
10
+ /**
11
+ * @param authService An {@link AuthService} configured with JWKS verification
12
+ * options. When omitted a default decode-only service is
13
+ * used.
14
+ * @param authServiceOptions Convenience: if `authService` is omitted, these
15
+ * options are used to construct one.
16
+ */
17
+ constructor(authService, authServiceOptions) {
18
+ super();
19
+ this.authService =
20
+ authService ?? new authService_js_1.AuthService(authServiceOptions ?? {});
15
21
  }
16
- async authorize(context) {
17
- const request = context.switchToHttp().getRequest();
18
- if (request.path?.startsWith("/public"))
19
- return;
20
- if (request.path === "/account" && request.method === "POST")
21
- return;
22
+ extractFromAuth(ctx) {
23
+ const request = ctx.switchToHttp().getRequest();
24
+ if (isPublicRoute(request)) {
25
+ return { roles: [], token: "", isPublic: true };
26
+ }
22
27
  const token = getToken(request);
23
28
  if (!token)
24
29
  throw new core_1.AuthorizationError("Token not found");
25
- const payload = this.authService.assertValidToken(token);
26
- const roles = this.authService.getRoles(token);
27
- const organization = payload.aud || payload.azp || (0, utils_js_1.getRealmFromIssuer)(token);
28
- request[utils_js_1.DECAF_ADAPTER_OPTIONS] = {
29
- roles,
30
- user: payload.email ?? payload.preferred_username,
31
- msp: organization,
32
- };
30
+ // Decode only — validation happens in `validate`
31
+ const payload = (0, utils_js_1.getTokenPayload)(token);
32
+ const roles = (0, utils_js_1.extractKeycloakRoles)(payload);
33
+ const organization = resolveOrganization(payload, token);
34
+ const user = payload?.email ?? payload?.preferred_username;
35
+ return { user, organization, roles, token, isPublic: false };
36
+ }
37
+ /**
38
+ * Validates the JWT against the Keycloak instance (signature, expiry, etc.)
39
+ * via {@link AuthService.assertValidToken}, then delegates to the base class
40
+ * for route-level and model-level role checks. Skips entirely for public routes.
41
+ */
42
+ async validate(data, routeRoles, model, ...args) {
43
+ if (data.isPublic)
44
+ return;
45
+ await this.authService.assertValidToken(data.token);
46
+ await super.validate(data, routeRoles, model, ...args);
33
47
  }
34
48
  }
35
49
  exports.KeycloakAuthHandler = KeycloakAuthHandler;
50
+ function isPublicRoute(req) {
51
+ if (req.path?.startsWith("/public"))
52
+ return true;
53
+ return false;
54
+ }
36
55
  function getToken(req) {
37
56
  const token = (req.headers?.["x-auth-request-access-token"] ??
38
57
  req.headers?.["authorization"]);
@@ -40,5 +59,12 @@ function getToken(req) {
40
59
  return undefined;
41
60
  return token.startsWith("Bearer ") ? token.slice("Bearer ".length) : token;
42
61
  }
62
+ function resolveOrganization(payload, token) {
63
+ if (payload?.aud)
64
+ return payload.aud;
65
+ if (payload?.azp)
66
+ return payload.azp;
67
+ return (0, utils_js_1.getRealmFromIssuer)(token);
68
+ }
43
69
  //# sourceMappingURL=keycloakAuthHandler.js.map
44
70
  //# sourceMappingURL=keycloakAuthHandler.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"nest/keycloakAuthHandler.cjs","sourceRoot":"","sources":["nest/keycloakAuthHandler.js"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,yCAAoD;AACpD,qDAA+C;AAC/C,yCAAuE;AACvE,MAAa,mBAAmB;IAC5B,YAAY,WAAW,GAAG,IAAI,4BAAW,EAAE;QACvC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,SAAS,CAAC,OAAO;QACnB,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QACpD,IAAI,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC;YACnC,OAAO;QACX,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM;YACxD,OAAO;QACX,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK;YACN,MAAM,IAAI,yBAAkB,CAAC,iBAAiB,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,IAAI,IAAA,6BAAkB,EAAC,KAAK,CAAC,CAAC;QAC7E,OAAO,CAAC,gCAAqB,CAAC,GAAG;YAC7B,KAAK;YACL,IAAI,EAAE,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,kBAAkB;YACjD,GAAG,EAAE,YAAY;SACpB,CAAC;IACN,CAAC;CACJ;AAtBD,kDAsBC;AACD,SAAS,QAAQ,CAAC,GAAG;IACjB,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,6BAA6B,CAAC;QACvD,GAAG,CAAC,OAAO,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,KAAK;QACN,OAAO,SAAS,CAAC;IACrB,OAAO,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/E,CAAC;AACD,+CAA+C"}
1
+ {"version":3,"file":"nest/keycloakAuthHandler.cjs","sourceRoot":"","sources":["nest/keycloakAuthHandler.js"],"names":[],"mappings":";;;AAAA,yCAAoD;AACpD,sDAAwD;AACxD,qDAA+C;AAC/C,yCAAwG;AA4D/F,+FA5D2D,yBAAc,OA4D3D;AA3DvB,MAAa,mBAAoB,SAAQ,oBAAW;IAChD;;;;;;OAMG;IACH,YAAY,WAAW,EAAE,kBAAkB;QACvC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,WAAW;YACZ,WAAW,IAAI,IAAI,4BAAW,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,eAAe,CAAC,GAAG;QACf,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QAChD,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QACpD,CAAC;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK;YACN,MAAM,IAAI,yBAAkB,CAAC,iBAAiB,CAAC,CAAC;QACpD,iDAAiD;QACjD,MAAM,OAAO,GAAG,IAAA,0BAAe,EAAC,KAAK,CAAC,CAAC;QACvC,MAAM,KAAK,GAAG,IAAA,+BAAoB,EAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,OAAO,EAAE,KAAK,IAAI,OAAO,EAAE,kBAAkB,CAAC;QAC3D,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACjE,CAAC;IACD;;;;OAIG;IACH,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,IAAI;QAC3C,IAAI,IAAI,CAAC,QAAQ;YACb,OAAO;QACX,MAAM,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IAC3D,CAAC;CACJ;AAvCD,kDAuCC;AACD,SAAS,aAAa,CAAC,GAAG;IACtB,IAAI,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC;QAC/B,OAAO,IAAI,CAAC;IAChB,OAAO,KAAK,CAAC;AACjB,CAAC;AACD,SAAS,QAAQ,CAAC,GAAG;IACjB,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,6BAA6B,CAAC;QACvD,GAAG,CAAC,OAAO,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,KAAK;QACN,OAAO,SAAS,CAAC;IACrB,OAAO,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/E,CAAC;AACD,SAAS,mBAAmB,CAAC,OAAO,EAAE,KAAK;IACvC,IAAI,OAAO,EAAE,GAAG;QACZ,OAAO,OAAO,CAAC,GAAG,CAAC;IACvB,IAAI,OAAO,EAAE,GAAG;QACZ,OAAO,OAAO,CAAC,GAAG,CAAC;IACvB,OAAO,IAAA,6BAAkB,EAAC,KAAK,CAAC,CAAC;AACrC,CAAC;AAED,+CAA+C"}
@@ -9,12 +9,14 @@ exports.AuthModule = exports.KeycloakModule = void 0;
9
9
  const authService_js_1 = require("./authService.cjs");
10
10
  const keycloakAuthHandler_js_1 = require("./keycloakAuthHandler.cjs");
11
11
  class KeycloakModule {
12
- constructor(authService = new authService_js_1.AuthService(), authHandler = new keycloakAuthHandler_js_1.KeycloakAuthHandler(authService)) {
12
+ constructor(authService, authHandler) {
13
13
  this.authService = authService;
14
14
  this.authHandler = authHandler;
15
15
  }
16
- static create() {
17
- return new KeycloakModule();
16
+ static create(options) {
17
+ const authService = new authService_js_1.AuthService(options);
18
+ const authHandler = new keycloakAuthHandler_js_1.KeycloakAuthHandler(authService);
19
+ return new KeycloakModule(authService, authHandler);
18
20
  }
19
21
  }
20
22
  exports.KeycloakModule = KeycloakModule;
@@ -1 +1 @@
1
- {"version":3,"file":"nest/keycloakModule.cjs","sourceRoot":"","sources":["nest/keycloakModule.js"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,qDAA+C;AAC/C,qEAA+D;AAC/D,MAAa,cAAc;IACvB,YAAY,WAAW,GAAG,IAAI,4BAAW,EAAE,EAAE,WAAW,GAAG,IAAI,4CAAmB,CAAC,WAAW,CAAC;QAC3F,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACnC,CAAC;IACD,MAAM,CAAC,MAAM;QACT,OAAO,IAAI,cAAc,EAAE,CAAC;IAChC,CAAC;CACJ;AARD,wCAQC;AACD,MAAa,UAAW,SAAQ,cAAc;CAC7C;AADD,gCACC;AACD,0CAA0C"}
1
+ {"version":3,"file":"nest/keycloakModule.cjs","sourceRoot":"","sources":["nest/keycloakModule.js"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,qDAA+C;AAC/C,qEAA+D;AAC/D,MAAa,cAAc;IACvB,YAAY,WAAW,EAAE,WAAW;QAChC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACnC,CAAC;IACD,MAAM,CAAC,MAAM,CAAC,OAAO;QACjB,MAAM,WAAW,GAAG,IAAI,4BAAW,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,WAAW,GAAG,IAAI,4CAAmB,CAAC,WAAW,CAAC,CAAC;QACzD,OAAO,IAAI,cAAc,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACxD,CAAC;CACJ;AAVD,wCAUC;AACD,MAAa,UAAW,SAAQ,cAAc;CAC7C;AADD,gCACC;AACD,0CAA0C"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * @module integrations/nest/logging
5
+ * @summary Keycloak auth log parameters.
6
+ * @description Registers `user` and `organization` as log parameters sourced from the
7
+ * logging config (populated by the auth interceptor's child logger via `Logging.for({ user, organization })`).
8
+ *
9
+ * These parameters are registered here — in `integrations/nest` — because this is the
10
+ * layer that extracts user/organization identity from Keycloak JWTs. The `ip` parameter,
11
+ * by contrast, is registered in `@decaf-ts/for-http/server` where the HTTP request IP is
12
+ * available.
13
+ */
14
+ const logging_1 = require("@decaf-ts/logging");
15
+ logging_1.logParameterRegistry
16
+ .register({
17
+ key: "user",
18
+ shouldInclude(payload) {
19
+ return Boolean(payload.config.user);
20
+ },
21
+ render(payload) {
22
+ return String(payload.config.user);
23
+ },
24
+ style(rendered, payload) {
25
+ return payload.applyTheme(rendered, "app");
26
+ },
27
+ })
28
+ .register({
29
+ key: "organization",
30
+ shouldInclude(payload) {
31
+ return Boolean(payload.config.organization);
32
+ },
33
+ render(payload) {
34
+ return String(payload.config.organization);
35
+ },
36
+ style(rendered, payload) {
37
+ return payload.applyTheme(rendered, "app");
38
+ },
39
+ });
40
+ //# sourceMappingURL=logging.js.map
41
+ //# sourceMappingURL=logging.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nest/logging.cjs","sourceRoot":"","sources":["nest/logging.js"],"names":[],"mappings":";;AAAA;;;;;;;;;;GAUG;AACH,+CAAyD;AACzD,8BAAoB;KACf,QAAQ,CAAC;IACV,GAAG,EAAE,MAAM;IACX,aAAa,CAAC,OAAO;QACjB,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IACD,MAAM,CAAC,OAAO;QACV,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IACD,KAAK,CAAC,QAAQ,EAAE,OAAO;QACnB,OAAO,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;CACJ,CAAC;KACG,QAAQ,CAAC;IACV,GAAG,EAAE,cAAc;IACnB,aAAa,CAAC,OAAO;QACjB,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAChD,CAAC;IACD,MAAM,CAAC,OAAO;QACV,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,CAAC,QAAQ,EAAE,OAAO;QACnB,OAAO,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;CACJ,CAAC,CAAC;AACH,mCAAmC"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DECAF_ADAPTER_OPTIONS = void 0;
4
4
  exports.getTokenPayload = getTokenPayload;
5
5
  exports.extractKeycloakRoles = extractKeycloakRoles;
6
+ exports.getClientRoles = getClientRoles;
6
7
  exports.getUser = getUser;
7
8
  exports.getRealmFromIssuer = getRealmFromIssuer;
8
9
  const core_1 = require("@decaf-ts/core");
@@ -30,6 +31,29 @@ function extractKeycloakRoles(payload) {
30
31
  }
31
32
  return [...roles];
32
33
  }
34
+ /**
35
+ * Extracts roles from the `resource_access` claim only, optionally excluding
36
+ * specified client IDs (e.g. `"account"`). Unlike {@link extractKeycloakRoles}
37
+ * this does NOT include `realm_access.roles`.
38
+ *
39
+ * @param payload The decoded JWT payload.
40
+ * @param excluded Client IDs to skip. @default ["account"]
41
+ */
42
+ function getClientRoles(payload, excluded = ["account"]) {
43
+ const resourceAccess = payload?.resource_access;
44
+ if (!resourceAccess || typeof resourceAccess !== "object")
45
+ return [];
46
+ const roles = new Set();
47
+ for (const [clientId, data] of Object.entries(resourceAccess)) {
48
+ if (excluded.includes(clientId))
49
+ continue;
50
+ if (Array.isArray(data?.roles)) {
51
+ for (const role of data.roles)
52
+ roles.add(role);
53
+ }
54
+ }
55
+ return [...roles];
56
+ }
33
57
  function getUser(jwt) {
34
58
  const payload = getTokenPayload(jwt);
35
59
  if (!payload)
@@ -1 +1 @@
1
- {"version":3,"file":"nest/utils.cjs","sourceRoot":"","sources":["nest/utils.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nest/utils.cjs","sourceRoot":"","sources":["nest/utils.js"],"names":[],"mappings":"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"}
package/lib/cjs/utils.cjs CHANGED
@@ -21,7 +21,7 @@ async function execWithLogging(command, options, log, level = logging_1.LogLevel
21
21
  });
22
22
  child.stderr?.on("data", (data) => {
23
23
  stderr.push(data);
24
- log.error(data.trim());
24
+ log.debug(data.trim());
25
25
  });
26
26
  child.on("close", (code) => {
27
27
  if (code === 0) {
package/lib/esm/index.js CHANGED
@@ -4,8 +4,8 @@
4
4
  * @description Exposes the package utilities and build-time version metadata for `@decaf-ts/integrations`.
5
5
  */
6
6
  export * from "./utils.js";
7
- export const VERSION = "0.1.4";
8
- export const COMMIT = "96f5884";
9
- export const FULL_VERSION = "0.1.4-96f5884";
7
+ export const VERSION = "0.2.1";
8
+ export const COMMIT = "4990f39";
9
+ export const FULL_VERSION = "0.2.1-4990f39";
10
10
  export const PACKAGE_NAME = "@decaf-ts/integrations";
11
11
  //# sourceMappingURL=index.js.map
@@ -2,15 +2,28 @@
2
2
  * @module integrations/nest/authService
3
3
  * @summary Nest-compatible authentication service helpers.
4
4
  * @description Request-context-aware auth helpers for Nest applications using Decaf integration patterns.
5
+ *
6
+ * Supports two modes:
7
+ * - **Verify mode** (default when `verifyUrl` is set): validates the JWT signature
8
+ * against the Keycloak JWKS endpoint using `jose.jwtVerify`.
9
+ * - **Decode-only mode** (when `verifyToken` is false / `verifyUrl` is empty):
10
+ * decodes the JWT without signature verification (dev/local mode).
5
11
  */
6
12
  import { AuthorizationError } from "@decaf-ts/core";
7
- import { extractKeycloakRoles, getRealmFromIssuer, getTokenPayload, getUser } from "./utils.js";
13
+ import { createRemoteJWKSet, jwtVerify } from "jose";
14
+ import { extractKeycloakRoles, getRealmFromIssuer, getTokenPayload, getUser, getClientRoles, } from "./utils.js";
8
15
  export class AuthService {
16
+ constructor(options = {}) {
17
+ this.verifyToken = !!options.verifyToken && !!options.verifyUrl;
18
+ this.verifyUrl = options.verifyUrl ?? "";
19
+ this.clockToleranceSeconds = options.clockToleranceSeconds ?? 5;
20
+ this.excludedClients = options.excludedClients ?? ["account"];
21
+ }
9
22
  getTokenPayload(jwt) {
10
23
  return getTokenPayload(jwt);
11
24
  }
12
25
  getRoles(jwt) {
13
- return extractKeycloakRoles(this.getTokenPayload(jwt));
26
+ return this.extractKeycloakRoles(this.getTokenPayload(jwt));
14
27
  }
15
28
  getUser(jwt) {
16
29
  return getUser(jwt);
@@ -18,15 +31,53 @@ export class AuthService {
18
31
  extractKeycloakRoles(payload) {
19
32
  return extractKeycloakRoles(payload);
20
33
  }
34
+ /**
35
+ * Extracts client roles from the `resource_access` claim, excluding
36
+ * the configured `excludedClients` (default: `["account"]`).
37
+ */
38
+ extractClientRoles(payload) {
39
+ return getClientRoles(payload, this.excludedClients);
40
+ }
21
41
  getRealmFromIssuer(jwt) {
22
42
  return getRealmFromIssuer(jwt);
23
43
  }
24
- assertValidToken(jwt) {
44
+ /**
45
+ * Validates a JWT:
46
+ * - In **verify mode**: verifies the signature against the Keycloak JWKS
47
+ * endpoint using `jose.jwtVerify`, then returns the verified payload.
48
+ * - In **decode-only mode**: decodes the JWT without verification and
49
+ * returns the payload (or throws if unparseable).
50
+ *
51
+ * @throws {AuthorizationError} when the token is invalid, expired, or
52
+ * signature verification fails.
53
+ */
54
+ async assertValidToken(jwt) {
55
+ if (this.verifyToken) {
56
+ return this.verifyAgainstJwks(jwt);
57
+ }
25
58
  const payload = this.getTokenPayload(jwt);
26
59
  if (!payload) {
27
60
  throw new AuthorizationError("Invalid token");
28
61
  }
29
62
  return payload;
30
63
  }
64
+ /**
65
+ * Verifies the JWT signature against the Keycloak JWKS endpoint.
66
+ * Lazily creates the JWKS keystore on first call.
67
+ */
68
+ async verifyAgainstJwks(jwt) {
69
+ if (!this.jwks) {
70
+ this.jwks = createRemoteJWKSet(new URL(this.verifyUrl));
71
+ }
72
+ try {
73
+ const { payload } = await jwtVerify(jwt, this.jwks, {
74
+ clockTolerance: `${this.clockToleranceSeconds}s`,
75
+ });
76
+ return payload;
77
+ }
78
+ catch (e) {
79
+ throw new AuthorizationError("Invalid token: " + (e?.message ?? e));
80
+ }
81
+ }
31
82
  }
32
83
  //# sourceMappingURL=authService.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"authService.js","sourceRoot":"","sources":["../../../src/nest/authService.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,eAAe,EAAE,OAAO,EAAE,mBAAgB;AAG7F,MAAM,OAAO,WAAW;IACtB,eAAe,CAAC,GAAW;QACzB,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,QAAQ,CAAC,GAAW;QAClB,OAAO,oBAAoB,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,CAAC,GAAW;QACjB,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,oBAAoB,CAAC,OAAgB;QACnC,OAAO,oBAAoB,CAAC,OAA4C,CAAC,CAAC;IAC5E,CAAC;IAED,kBAAkB,CAAC,GAAW;QAC5B,OAAO,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAED,gBAAgB,CAAC,GAAW;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,kBAAkB,CAAC,eAAe,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
1
+ {"version":3,"file":"authService.js","sourceRoot":"","sources":["../../../src/nest/authService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACrD,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,OAAO,EACP,cAAc,GACf,mBAAgB;AA+BjB,MAAM,OAAO,WAAW;IAOtB,YAAY,UAA8B,EAAE;QAC1C,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;QAChE,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,IAAI,CAAC,CAAC;QAChE,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;IAED,eAAe,CAAC,GAAW;QACzB,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,QAAQ,CAAC,GAAW;QAClB,OAAO,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,CAAC,GAAW;QACjB,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,oBAAoB,CAAC,OAA0C;QAC7D,OAAO,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED;;;OAGG;IACH,kBAAkB,CAChB,OAA0C;QAE1C,OAAO,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IACvD,CAAC;IAED,kBAAkB,CAAC,GAAW;QAC5B,OAAO,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,gBAAgB,CACpB,GAAW;QAEX,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,kBAAkB,CAAC,eAAe,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,iBAAiB,CAC7B,GAAW;QAEX,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,CAAC;YACH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE;gBAClD,cAAc,EAAE,GAAG,IAAI,CAAC,qBAAqB,GAAG;aACjD,CAAC,CAAC;YACH,OAAO,OAAgD,CAAC;QAC1D,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,MAAM,IAAI,kBAAkB,CAAC,iBAAiB,GAAG,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;CACF"}
@@ -3,9 +3,11 @@ export * from "./utils.js";
3
3
  export * from "./authService.js";
4
4
  export * from "./keycloakAuthHandler.js";
5
5
  export * from "./keycloakModule.js";
6
+ import "./logging.js";
6
7
  /**
7
8
  * @summary Nest-style auth integrations.
8
9
  * @description Framework-agnostic auth helpers compatible with Decaf request context handling.
10
+ * Imports the `user`/`organization` log parameter registrations as a side effect.
9
11
  * @namespace nest
10
12
  * @memberOf module:integrations
11
13
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/nest/index.ts"],"names":[],"mappings":"AAAA,2BAAwB;AACxB,2BAAwB;AACxB,iCAA8B;AAC9B,yCAAsC;AACtC,oCAAiC;AAEjC;;;;;GAKG"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/nest/index.ts"],"names":[],"mappings":"AAAA,2BAAwB;AACxB,2BAAwB;AACxB,iCAA8B;AAC9B,yCAAsC;AACtC,oCAAiC;AAEjC,sBAAmB;AAEnB;;;;;;GAMG"}
@@ -1,34 +1,52 @@
1
- /**
2
- * @module integrations/nest/keycloakAuthHandler
3
- * @summary Keycloak request auth handler.
4
- * @description Translates Keycloak JWT payloads into Decaf-friendly auth context values.
5
- */
6
1
  import { AuthorizationError } from "@decaf-ts/core";
2
+ import { AuthHandler } from "@decaf-ts/for-http/server";
7
3
  import { AuthService } from "./authService.js";
8
- import { DECAF_ADAPTER_OPTIONS, getRealmFromIssuer } from "./utils.js";
9
- export class KeycloakAuthHandler {
10
- constructor(authService = new AuthService()) {
11
- this.authService = authService;
4
+ import { getRealmFromIssuer, getTokenPayload, extractKeycloakRoles, getClientRoles, } from "./utils.js";
5
+ export class KeycloakAuthHandler extends AuthHandler {
6
+ /**
7
+ * @param authService An {@link AuthService} configured with JWKS verification
8
+ * options. When omitted a default decode-only service is
9
+ * used.
10
+ * @param authServiceOptions Convenience: if `authService` is omitted, these
11
+ * options are used to construct one.
12
+ */
13
+ constructor(authService, authServiceOptions) {
14
+ super();
15
+ this.authService =
16
+ authService ?? new AuthService(authServiceOptions ?? {});
12
17
  }
13
- async authorize(context) {
14
- const request = context.switchToHttp().getRequest();
15
- if (request.path?.startsWith("/public"))
16
- return;
17
- if (request.path === "/account" && request.method === "POST")
18
- return;
18
+ extractFromAuth(ctx) {
19
+ const request = ctx.switchToHttp().getRequest();
20
+ if (isPublicRoute(request)) {
21
+ return { roles: [], token: "", isPublic: true };
22
+ }
19
23
  const token = getToken(request);
20
24
  if (!token)
21
25
  throw new AuthorizationError("Token not found");
22
- const payload = this.authService.assertValidToken(token);
23
- const roles = this.authService.getRoles(token);
24
- const organization = payload.aud || payload.azp || getRealmFromIssuer(token);
25
- request[DECAF_ADAPTER_OPTIONS] = {
26
- roles,
27
- user: payload.email ?? payload.preferred_username,
28
- msp: organization,
29
- };
26
+ // Decode only — validation happens in `validate`
27
+ const payload = getTokenPayload(token);
28
+ const roles = extractKeycloakRoles(payload);
29
+ const organization = resolveOrganization(payload, token);
30
+ const user = payload?.email ?? payload?.preferred_username;
31
+ return { user, organization, roles, token, isPublic: false };
32
+ }
33
+ /**
34
+ * Validates the JWT against the Keycloak instance (signature, expiry, etc.)
35
+ * via {@link AuthService.assertValidToken}, then delegates to the base class
36
+ * for route-level and model-level role checks. Skips entirely for public routes.
37
+ */
38
+ async validate(data, routeRoles, model, ...args) {
39
+ if (data.isPublic)
40
+ return;
41
+ await this.authService.assertValidToken(data.token);
42
+ await super.validate(data, routeRoles, model, ...args);
30
43
  }
31
44
  }
45
+ function isPublicRoute(req) {
46
+ if (req.path?.startsWith("/public"))
47
+ return true;
48
+ return false;
49
+ }
32
50
  function getToken(req) {
33
51
  const token = (req.headers?.["x-auth-request-access-token"] ??
34
52
  req.headers?.["authorization"]);
@@ -36,4 +54,12 @@ function getToken(req) {
36
54
  return undefined;
37
55
  return token.startsWith("Bearer ") ? token.slice("Bearer ".length) : token;
38
56
  }
57
+ function resolveOrganization(payload, token) {
58
+ if (payload?.aud)
59
+ return payload.aud;
60
+ if (payload?.azp)
61
+ return payload.azp;
62
+ return getRealmFromIssuer(token);
63
+ }
64
+ export { getClientRoles };
39
65
  //# sourceMappingURL=keycloakAuthHandler.js.map