@productcraft/heimdall-passport 0.0.2 → 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ProductCraft
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.cjs CHANGED
@@ -3,7 +3,12 @@
3
3
  var crypto = require('crypto');
4
4
  var jose = require('jose');
5
5
 
6
- // src/index.ts
6
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
7
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
8
+ }) : x)(function(x) {
9
+ if (typeof require !== "undefined") return require.apply(this, arguments);
10
+ throw Error('Dynamic require of "' + x + '" is not supported');
11
+ });
7
12
  function createPassportSecretOrKeyProvider(scope) {
8
13
  return (_request, rawJwt, done) => {
9
14
  let header;
@@ -26,7 +31,36 @@ function createPassportSecretOrKeyProvider(scope) {
26
31
  );
27
32
  };
28
33
  }
34
+ function loadPassportJwt() {
35
+ try {
36
+ const mod = __require("passport-jwt");
37
+ return {
38
+ Strategy: mod.Strategy ?? mod.default?.Strategy,
39
+ ExtractJwt: mod.ExtractJwt ?? mod.default?.ExtractJwt
40
+ };
41
+ } catch {
42
+ throw new Error(
43
+ "@productcraft/heimdall-passport: `passport-jwt` is not installed. Run `npm install passport-jwt @types/passport-jwt`."
44
+ );
45
+ }
46
+ }
47
+ function createHeimdallJwtStrategy(scope, verify, options = {}) {
48
+ const passportJwt = loadPassportJwt();
49
+ const issuer = options.strictIssuer ? scope.expectedIssuer : scope.acceptedIssuers;
50
+ return new passportJwt.Strategy(
51
+ {
52
+ jwtFromRequest: options.jwtFromRequest ?? passportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),
53
+ secretOrKeyProvider: createPassportSecretOrKeyProvider(scope),
54
+ issuer,
55
+ audience: scope.expectedAudience,
56
+ algorithms: options.algorithms ?? ["RS256"],
57
+ passReqToCallback: options.passReqToCallback ?? false
58
+ },
59
+ verify
60
+ );
61
+ }
29
62
 
63
+ exports.createHeimdallJwtStrategy = createHeimdallJwtStrategy;
30
64
  exports.createPassportSecretOrKeyProvider = createPassportSecretOrKeyProvider;
31
65
  //# sourceMappingURL=index.cjs.map
32
66
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["decodeProtectedHeader","KeyObject"],"mappings":";;;;;;AAsDO,SAAS,kCACd,KAAA,EAC6B;AAC7B,EAAA,OAAO,CAAC,QAAA,EAAU,MAAA,EAAQ,IAAA,KAAS;AACjC,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAA,GAASA,2BAAsB,MAAM,CAAA;AAAA,IACvC,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,GAAG,CAAA;AACR,MAAA;AAAA,IACF;AAEA,IAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,MAA6B,CAAA,CAAE,IAAA;AAAA,MAC/C,CAAC,SAAA,KAAc;AACb,QAAA,IAAI;AAKF,UAAA,MAAM,SAAA,GAAYC,gBAAA,CAAU,IAAA,CAAK,SAAS,CAAA;AAC1C,UAAA,IAAA,CAAK,MAAM,SAAS,CAAA;AAAA,QACtB,SAAS,GAAA,EAAK;AACZ,UAAA,IAAA,CAAK,GAAG,CAAA;AAAA,QACV;AAAA,MACF,CAAA;AAAA,MACA,CAAC,GAAA,KAAQ,IAAA,CAAK,GAAG;AAAA,KACnB;AAAA,EACF,CAAA;AACF","file":"index.cjs","sourcesContent":["/**\n * `@productcraft/heimdall-passport` — passport-jwt adapter for Heimdall.\n *\n * Install alongside `@productcraft/heimdall` + `passport-jwt`:\n *\n * ```bash\n * npm install @productcraft/heimdall @productcraft/heimdall-passport passport-jwt\n * ```\n *\n * Usage:\n *\n * ```ts\n * import passportJwt from \"passport-jwt\";\n * import { Heimdall } from \"@productcraft/heimdall\";\n * import { createPassportSecretOrKeyProvider } from \"@productcraft/heimdall-passport\";\n *\n * const heimdall = new Heimdall({ auth: { type: \"apiKey\", key: process.env.PCFT_KEY! } });\n * const scope = heimdall.consumer(\"my-app-slug\");\n *\n * new passportJwt.Strategy(\n * {\n * jwtFromRequest: passportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),\n * secretOrKeyProvider: createPassportSecretOrKeyProvider(scope),\n * issuer: scope.expectedIssuer,\n * algorithms: [\"ES256\"],\n * },\n * (payload, done) => done(null, payload),\n * );\n * ```\n */\n\nimport { KeyObject } from \"node:crypto\";\nimport { decodeProtectedHeader, type JWTHeaderParameters } from \"jose\";\n\nimport type { ConsumerScope } from \"@productcraft/heimdall\";\n\n/**\n * passport-jwt's `secretOrKeyProvider` callback signature. We don't\n * depend on passport-jwt's types directly (to keep it a soft dep),\n * so this is the same shape as `SecretOrKeyProvider` in their typings.\n */\ntype PassportSecretOrKeyProvider = (\n request: unknown,\n rawJwtToken: string,\n done: (err: unknown, secretOrKey?: string | Buffer | KeyObject) => void,\n) => void;\n\n/**\n * Returns a passport-jwt `secretOrKeyProvider` bound to a Heimdall\n * `ConsumerScope`. Decodes the protected header, looks up the matching\n * key via the scope's JWKS cache, and converts the resulting jose\n * `CryptoKey` into a Node `KeyObject` that `jsonwebtoken` (the library\n * passport-jwt delegates to) accepts.\n */\nexport function createPassportSecretOrKeyProvider(\n scope: ConsumerScope,\n): PassportSecretOrKeyProvider {\n return (_request, rawJwt, done) => {\n let header;\n try {\n header = decodeProtectedHeader(rawJwt);\n } catch (err) {\n done(err);\n return;\n }\n\n scope.jwks.getKey(header as JWTHeaderParameters).then(\n (cryptoKey) => {\n try {\n // `jsonwebtoken` (passport-jwt's verifier) doesn't accept\n // Web Crypto `CryptoKey`, but it does accept Node's\n // `KeyObject`. `KeyObject.from(cryptoKey)` was added in\n // Node 16.6 and is the right adapter.\n const keyObject = KeyObject.from(cryptoKey);\n done(null, keyObject);\n } catch (err) {\n done(err);\n }\n },\n (err) => done(err),\n );\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["decodeProtectedHeader","KeyObject"],"mappings":";;;;;;;;;;;AAsEO,SAAS,kCACd,KAAA,EAC6B;AAC7B,EAAA,OAAO,CAAC,QAAA,EAAU,MAAA,EAAQ,IAAA,KAAS;AACjC,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAA,GAASA,2BAAsB,MAAM,CAAA;AAAA,IACvC,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,GAAG,CAAA;AACR,MAAA;AAAA,IACF;AAEA,IAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,MAA6B,CAAA,CAAE,IAAA;AAAA,MAC/C,CAAC,SAAA,KAAc;AACb,QAAA,IAAI;AAKF,UAAA,MAAM,SAAA,GAAYC,gBAAA,CAAU,IAAA,CAAK,SAAS,CAAA;AAC1C,UAAA,IAAA,CAAK,MAAM,SAAS,CAAA;AAAA,QACtB,SAAS,GAAA,EAAK;AACZ,UAAA,IAAA,CAAK,GAAG,CAAA;AAAA,QACV;AAAA,MACF,CAAA;AAAA,MACA,CAAC,GAAA,KAAQ,IAAA,CAAK,GAAG;AAAA,KACnB;AAAA,EACF,CAAA;AACF;AA+DA,SAAS,eAAA,GAGP;AACA,EAAA,IAAI;AAEF,IAAA,MAAM,GAAA,GAAM,UAAQ,cAAc,CAAA;AAClC,IAAA,OAAO;AAAA,MACL,QAAA,EAAU,GAAA,CAAI,QAAA,IAAY,GAAA,CAAI,OAAA,EAAS,QAAA;AAAA,MACvC,UAAA,EAAY,GAAA,CAAI,UAAA,IAAc,GAAA,CAAI,OAAA,EAAS;AAAA,KAC7C;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACF;AAeO,SAAS,yBAAA,CACd,KAAA,EACA,MAAA,EACA,OAAA,GAA4C,EAAC,EAC7C;AACA,EAAA,MAAM,cAAc,eAAA,EAAgB;AACpC,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,YAAA,GACnB,KAAA,CAAM,iBACL,KAAA,CAAM,eAAA;AAEX,EAAA,OAAO,IAAI,WAAA,CAAY,QAAA;AAAA,IACrB;AAAA,MACE,cAAA,EACE,OAAA,CAAQ,cAAA,IAAkB,WAAA,CAAY,WAAW,2BAAA,EAA4B;AAAA,MAC/E,mBAAA,EAAqB,kCAAkC,KAAK,CAAA;AAAA,MAC5D,MAAA;AAAA,MACA,UAAU,KAAA,CAAM,gBAAA;AAAA,MAChB,UAAA,EAAY,OAAA,CAAQ,UAAA,IAAc,CAAC,OAAO,CAAA;AAAA,MAC1C,iBAAA,EAAmB,QAAQ,iBAAA,IAAqB;AAAA,KAClD;AAAA,IACA;AAAA,GACF;AACF","file":"index.cjs","sourcesContent":["/**\n * `@productcraft/heimdall-passport` — passport-jwt adapter for Heimdall.\n *\n * Install alongside `@productcraft/heimdall` + `passport-jwt`:\n *\n * ```bash\n * npm install @productcraft/heimdall @productcraft/heimdall-passport passport-jwt\n * ```\n *\n * Recommended one-call form:\n *\n * ```ts\n * import passportJwt from \"passport-jwt\";\n * import passport from \"passport\";\n * import { Heimdall } from \"@productcraft/heimdall\";\n * import { createHeimdallJwtStrategy } from \"@productcraft/heimdall-passport\";\n *\n * const heimdall = new Heimdall({ auth: { type: \"apiKey\", key: process.env.PCFT_KEY! } });\n * const scope = heimdall.consumer(\"my-app-slug\");\n *\n * passport.use(createHeimdallJwtStrategy(scope, (claims, done) => {\n * // claims is the verified Heimdall claims object (sub, role, permissions, ...)\n * return done(null, claims);\n * }));\n * ```\n *\n * The helper wires `secretOrKeyProvider` + `issuer` (per-app URL) +\n * `audience` (app slug) + algorithms automatically from the scope.\n *\n * If you need the underlying primitives — to compose with your own\n * options or pass a custom `jwtFromRequest` — use the lower-level\n * `createPassportSecretOrKeyProvider` directly:\n *\n * ```ts\n * new passportJwt.Strategy(\n * {\n * jwtFromRequest: passportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),\n * secretOrKeyProvider: createPassportSecretOrKeyProvider(scope),\n * issuer: scope.acceptedIssuers as string[],\n * audience: scope.expectedAudience,\n * algorithms: [\"RS256\", \"ES256\"],\n * },\n * (payload, done) => done(null, payload),\n * );\n * ```\n */\n\nimport { KeyObject } from \"node:crypto\";\nimport { decodeProtectedHeader, type JWTHeaderParameters } from \"jose\";\n\nimport type { ConsumerScope } from \"@productcraft/heimdall\";\n\n/**\n * passport-jwt's `secretOrKeyProvider` callback signature. We don't\n * depend on passport-jwt's types directly (to keep it a soft dep),\n * so this is the same shape as `SecretOrKeyProvider` in their typings.\n */\ntype PassportSecretOrKeyProvider = (\n request: unknown,\n rawJwtToken: string,\n done: (err: unknown, secretOrKey?: string | Buffer | KeyObject) => void,\n) => void;\n\n/**\n * Returns a passport-jwt `secretOrKeyProvider` bound to a Heimdall\n * `ConsumerScope`. Decodes the protected header, looks up the matching\n * key via the scope's JWKS cache, and converts the resulting jose\n * `CryptoKey` into a Node `KeyObject` that `jsonwebtoken` (the library\n * passport-jwt delegates to) accepts.\n */\nexport function createPassportSecretOrKeyProvider(\n scope: ConsumerScope,\n): PassportSecretOrKeyProvider {\n return (_request, rawJwt, done) => {\n let header;\n try {\n header = decodeProtectedHeader(rawJwt);\n } catch (err) {\n done(err);\n return;\n }\n\n scope.jwks.getKey(header as JWTHeaderParameters).then(\n (cryptoKey) => {\n try {\n // `jsonwebtoken` (passport-jwt's verifier) doesn't accept\n // Web Crypto `CryptoKey`, but it does accept Node's\n // `KeyObject`. `KeyObject.from(cryptoKey)` was added in\n // Node 16.6 and is the right adapter.\n const keyObject = KeyObject.from(cryptoKey);\n done(null, keyObject);\n } catch (err) {\n done(err);\n }\n },\n (err) => done(err),\n );\n };\n}\n\n/**\n * Shape of the verified-claims callback passport delegates to once a\n * token passes signature + issuer + audience + expiry checks. Mirrors\n * `passport-jwt`'s own `VerifyCallback` / `VerifyCallbackWithRequest`.\n */\nexport type HeimdallVerifyCallback = (\n payload: Record<string, unknown>,\n done: (err: unknown, user?: unknown, info?: unknown) => void,\n) => void;\n\n/**\n * Extra options for `createHeimdallJwtStrategy`. Defaults are the\n * Heimdall-recommended values; override here if you need a different\n * token source (cookie, custom header, ...), a stricter algorithm\n * allow-list, or want to skip the legacy-issuer acceptance window.\n */\nexport interface CreateHeimdallJwtStrategyOptions {\n /**\n * Where to read the bearer from. Defaults to\n * `ExtractJwt.fromAuthHeaderAsBearerToken()`. Pass any\n * passport-jwt extractor when you need a different source.\n */\n jwtFromRequest?: (req: unknown) => string | null;\n\n /**\n * JWS algorithms accepted by the underlying verifier. Defaults to\n * the algorithms Heimdall mints today (`['RS256']`); set explicitly\n * if your app is on a different signing alg or you also accept\n * customer-minted tokens with a different alg.\n */\n algorithms?: string[];\n\n /**\n * Pin only the per-app issuer URL (skip the legacy `'heimdall'`\n * literal). Defaults to `false` — verifying both keeps in-flight\n * tokens minted before the per-app-issuer migration valid. Set to\n * `true` once you're confident all old tokens have expired.\n */\n strictIssuer?: boolean;\n\n /** Whether the request object should be passed to the verify callback. Defaults to `false`. */\n passReqToCallback?: boolean;\n}\n\n/**\n * Constructor signature for `passport-jwt`'s `Strategy`. We don't take\n * a direct dep on passport-jwt to keep it a peer/soft dep — at runtime\n * the caller imports passportJwt and the helper does\n * `new passportJwt.Strategy(...)` via the constructor reference.\n */\nexport interface PassportJwtStrategyCtor {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (options: Record<string, unknown>, verify: HeimdallVerifyCallback): any;\n}\n\n/**\n * Lazy passport-jwt loader. We try to `require('passport-jwt')` from\n * the caller's dep graph at first use. Throws a clear error if it's\n * missing so the user doesn't see a cryptic `Cannot find module`\n * deep in the strategy constructor.\n */\nfunction loadPassportJwt(): {\n Strategy: PassportJwtStrategyCtor;\n ExtractJwt: { fromAuthHeaderAsBearerToken: () => (req: unknown) => string | null };\n} {\n try {\n // dynamic require so passport-jwt stays a peer dep\n const mod = require(\"passport-jwt\");\n return {\n Strategy: mod.Strategy ?? mod.default?.Strategy,\n ExtractJwt: mod.ExtractJwt ?? mod.default?.ExtractJwt,\n };\n } catch {\n throw new Error(\n \"@productcraft/heimdall-passport: `passport-jwt` is not installed. Run `npm install passport-jwt @types/passport-jwt`.\",\n );\n }\n}\n\n/**\n * Build a fully-configured `passport-jwt` strategy from a Heimdall\n * `ConsumerScope`. Wires:\n *\n * - `jwtFromRequest` → `ExtractJwt.fromAuthHeaderAsBearerToken()` (override via opts)\n * - `secretOrKeyProvider` → JWKS-backed key resolution via `scope.jwks`\n * - `issuer` → both `scope.expectedIssuer` AND the legacy `'heimdall'` literal (or only the per-app URL if `strictIssuer: true`)\n * - `audience` → `scope.expectedAudience` (the app slug)\n * - `algorithms` → `['RS256']` (override via opts)\n *\n * The verify callback you pass receives the parsed claims after\n * passport-jwt has finished its checks.\n */\nexport function createHeimdallJwtStrategy(\n scope: ConsumerScope,\n verify: HeimdallVerifyCallback,\n options: CreateHeimdallJwtStrategyOptions = {},\n) {\n const passportJwt = loadPassportJwt();\n const issuer = options.strictIssuer\n ? scope.expectedIssuer\n : (scope.acceptedIssuers as string[]);\n\n return new passportJwt.Strategy(\n {\n jwtFromRequest:\n options.jwtFromRequest ?? passportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),\n secretOrKeyProvider: createPassportSecretOrKeyProvider(scope),\n issuer,\n audience: scope.expectedAudience,\n algorithms: options.algorithms ?? [\"RS256\"],\n passReqToCallback: options.passReqToCallback ?? false,\n },\n verify,\n );\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -10,22 +10,38 @@ import { ConsumerScope } from '@productcraft/heimdall';
10
10
  * npm install @productcraft/heimdall @productcraft/heimdall-passport passport-jwt
11
11
  * ```
12
12
  *
13
- * Usage:
13
+ * Recommended one-call form:
14
14
  *
15
15
  * ```ts
16
16
  * import passportJwt from "passport-jwt";
17
+ * import passport from "passport";
17
18
  * import { Heimdall } from "@productcraft/heimdall";
18
- * import { createPassportSecretOrKeyProvider } from "@productcraft/heimdall-passport";
19
+ * import { createHeimdallJwtStrategy } from "@productcraft/heimdall-passport";
19
20
  *
20
21
  * const heimdall = new Heimdall({ auth: { type: "apiKey", key: process.env.PCFT_KEY! } });
21
22
  * const scope = heimdall.consumer("my-app-slug");
22
23
  *
24
+ * passport.use(createHeimdallJwtStrategy(scope, (claims, done) => {
25
+ * // claims is the verified Heimdall claims object (sub, role, permissions, ...)
26
+ * return done(null, claims);
27
+ * }));
28
+ * ```
29
+ *
30
+ * The helper wires `secretOrKeyProvider` + `issuer` (per-app URL) +
31
+ * `audience` (app slug) + algorithms automatically from the scope.
32
+ *
33
+ * If you need the underlying primitives — to compose with your own
34
+ * options or pass a custom `jwtFromRequest` — use the lower-level
35
+ * `createPassportSecretOrKeyProvider` directly:
36
+ *
37
+ * ```ts
23
38
  * new passportJwt.Strategy(
24
39
  * {
25
40
  * jwtFromRequest: passportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),
26
41
  * secretOrKeyProvider: createPassportSecretOrKeyProvider(scope),
27
- * issuer: scope.expectedIssuer,
28
- * algorithms: ["ES256"],
42
+ * issuer: scope.acceptedIssuers as string[],
43
+ * audience: scope.expectedAudience,
44
+ * algorithms: ["RS256", "ES256"],
29
45
  * },
30
46
  * (payload, done) => done(null, payload),
31
47
  * );
@@ -46,5 +62,64 @@ type PassportSecretOrKeyProvider = (request: unknown, rawJwtToken: string, done:
46
62
  * passport-jwt delegates to) accepts.
47
63
  */
48
64
  declare function createPassportSecretOrKeyProvider(scope: ConsumerScope): PassportSecretOrKeyProvider;
65
+ /**
66
+ * Shape of the verified-claims callback passport delegates to once a
67
+ * token passes signature + issuer + audience + expiry checks. Mirrors
68
+ * `passport-jwt`'s own `VerifyCallback` / `VerifyCallbackWithRequest`.
69
+ */
70
+ type HeimdallVerifyCallback = (payload: Record<string, unknown>, done: (err: unknown, user?: unknown, info?: unknown) => void) => void;
71
+ /**
72
+ * Extra options for `createHeimdallJwtStrategy`. Defaults are the
73
+ * Heimdall-recommended values; override here if you need a different
74
+ * token source (cookie, custom header, ...), a stricter algorithm
75
+ * allow-list, or want to skip the legacy-issuer acceptance window.
76
+ */
77
+ interface CreateHeimdallJwtStrategyOptions {
78
+ /**
79
+ * Where to read the bearer from. Defaults to
80
+ * `ExtractJwt.fromAuthHeaderAsBearerToken()`. Pass any
81
+ * passport-jwt extractor when you need a different source.
82
+ */
83
+ jwtFromRequest?: (req: unknown) => string | null;
84
+ /**
85
+ * JWS algorithms accepted by the underlying verifier. Defaults to
86
+ * the algorithms Heimdall mints today (`['RS256']`); set explicitly
87
+ * if your app is on a different signing alg or you also accept
88
+ * customer-minted tokens with a different alg.
89
+ */
90
+ algorithms?: string[];
91
+ /**
92
+ * Pin only the per-app issuer URL (skip the legacy `'heimdall'`
93
+ * literal). Defaults to `false` — verifying both keeps in-flight
94
+ * tokens minted before the per-app-issuer migration valid. Set to
95
+ * `true` once you're confident all old tokens have expired.
96
+ */
97
+ strictIssuer?: boolean;
98
+ /** Whether the request object should be passed to the verify callback. Defaults to `false`. */
99
+ passReqToCallback?: boolean;
100
+ }
101
+ /**
102
+ * Constructor signature for `passport-jwt`'s `Strategy`. We don't take
103
+ * a direct dep on passport-jwt to keep it a peer/soft dep — at runtime
104
+ * the caller imports passportJwt and the helper does
105
+ * `new passportJwt.Strategy(...)` via the constructor reference.
106
+ */
107
+ interface PassportJwtStrategyCtor {
108
+ new (options: Record<string, unknown>, verify: HeimdallVerifyCallback): any;
109
+ }
110
+ /**
111
+ * Build a fully-configured `passport-jwt` strategy from a Heimdall
112
+ * `ConsumerScope`. Wires:
113
+ *
114
+ * - `jwtFromRequest` → `ExtractJwt.fromAuthHeaderAsBearerToken()` (override via opts)
115
+ * - `secretOrKeyProvider` → JWKS-backed key resolution via `scope.jwks`
116
+ * - `issuer` → both `scope.expectedIssuer` AND the legacy `'heimdall'` literal (or only the per-app URL if `strictIssuer: true`)
117
+ * - `audience` → `scope.expectedAudience` (the app slug)
118
+ * - `algorithms` → `['RS256']` (override via opts)
119
+ *
120
+ * The verify callback you pass receives the parsed claims after
121
+ * passport-jwt has finished its checks.
122
+ */
123
+ declare function createHeimdallJwtStrategy(scope: ConsumerScope, verify: HeimdallVerifyCallback, options?: CreateHeimdallJwtStrategyOptions): any;
49
124
 
50
- export { createPassportSecretOrKeyProvider };
125
+ export { type CreateHeimdallJwtStrategyOptions, type HeimdallVerifyCallback, type PassportJwtStrategyCtor, createHeimdallJwtStrategy, createPassportSecretOrKeyProvider };
package/dist/index.d.ts CHANGED
@@ -10,22 +10,38 @@ import { ConsumerScope } from '@productcraft/heimdall';
10
10
  * npm install @productcraft/heimdall @productcraft/heimdall-passport passport-jwt
11
11
  * ```
12
12
  *
13
- * Usage:
13
+ * Recommended one-call form:
14
14
  *
15
15
  * ```ts
16
16
  * import passportJwt from "passport-jwt";
17
+ * import passport from "passport";
17
18
  * import { Heimdall } from "@productcraft/heimdall";
18
- * import { createPassportSecretOrKeyProvider } from "@productcraft/heimdall-passport";
19
+ * import { createHeimdallJwtStrategy } from "@productcraft/heimdall-passport";
19
20
  *
20
21
  * const heimdall = new Heimdall({ auth: { type: "apiKey", key: process.env.PCFT_KEY! } });
21
22
  * const scope = heimdall.consumer("my-app-slug");
22
23
  *
24
+ * passport.use(createHeimdallJwtStrategy(scope, (claims, done) => {
25
+ * // claims is the verified Heimdall claims object (sub, role, permissions, ...)
26
+ * return done(null, claims);
27
+ * }));
28
+ * ```
29
+ *
30
+ * The helper wires `secretOrKeyProvider` + `issuer` (per-app URL) +
31
+ * `audience` (app slug) + algorithms automatically from the scope.
32
+ *
33
+ * If you need the underlying primitives — to compose with your own
34
+ * options or pass a custom `jwtFromRequest` — use the lower-level
35
+ * `createPassportSecretOrKeyProvider` directly:
36
+ *
37
+ * ```ts
23
38
  * new passportJwt.Strategy(
24
39
  * {
25
40
  * jwtFromRequest: passportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),
26
41
  * secretOrKeyProvider: createPassportSecretOrKeyProvider(scope),
27
- * issuer: scope.expectedIssuer,
28
- * algorithms: ["ES256"],
42
+ * issuer: scope.acceptedIssuers as string[],
43
+ * audience: scope.expectedAudience,
44
+ * algorithms: ["RS256", "ES256"],
29
45
  * },
30
46
  * (payload, done) => done(null, payload),
31
47
  * );
@@ -46,5 +62,64 @@ type PassportSecretOrKeyProvider = (request: unknown, rawJwtToken: string, done:
46
62
  * passport-jwt delegates to) accepts.
47
63
  */
48
64
  declare function createPassportSecretOrKeyProvider(scope: ConsumerScope): PassportSecretOrKeyProvider;
65
+ /**
66
+ * Shape of the verified-claims callback passport delegates to once a
67
+ * token passes signature + issuer + audience + expiry checks. Mirrors
68
+ * `passport-jwt`'s own `VerifyCallback` / `VerifyCallbackWithRequest`.
69
+ */
70
+ type HeimdallVerifyCallback = (payload: Record<string, unknown>, done: (err: unknown, user?: unknown, info?: unknown) => void) => void;
71
+ /**
72
+ * Extra options for `createHeimdallJwtStrategy`. Defaults are the
73
+ * Heimdall-recommended values; override here if you need a different
74
+ * token source (cookie, custom header, ...), a stricter algorithm
75
+ * allow-list, or want to skip the legacy-issuer acceptance window.
76
+ */
77
+ interface CreateHeimdallJwtStrategyOptions {
78
+ /**
79
+ * Where to read the bearer from. Defaults to
80
+ * `ExtractJwt.fromAuthHeaderAsBearerToken()`. Pass any
81
+ * passport-jwt extractor when you need a different source.
82
+ */
83
+ jwtFromRequest?: (req: unknown) => string | null;
84
+ /**
85
+ * JWS algorithms accepted by the underlying verifier. Defaults to
86
+ * the algorithms Heimdall mints today (`['RS256']`); set explicitly
87
+ * if your app is on a different signing alg or you also accept
88
+ * customer-minted tokens with a different alg.
89
+ */
90
+ algorithms?: string[];
91
+ /**
92
+ * Pin only the per-app issuer URL (skip the legacy `'heimdall'`
93
+ * literal). Defaults to `false` — verifying both keeps in-flight
94
+ * tokens minted before the per-app-issuer migration valid. Set to
95
+ * `true` once you're confident all old tokens have expired.
96
+ */
97
+ strictIssuer?: boolean;
98
+ /** Whether the request object should be passed to the verify callback. Defaults to `false`. */
99
+ passReqToCallback?: boolean;
100
+ }
101
+ /**
102
+ * Constructor signature for `passport-jwt`'s `Strategy`. We don't take
103
+ * a direct dep on passport-jwt to keep it a peer/soft dep — at runtime
104
+ * the caller imports passportJwt and the helper does
105
+ * `new passportJwt.Strategy(...)` via the constructor reference.
106
+ */
107
+ interface PassportJwtStrategyCtor {
108
+ new (options: Record<string, unknown>, verify: HeimdallVerifyCallback): any;
109
+ }
110
+ /**
111
+ * Build a fully-configured `passport-jwt` strategy from a Heimdall
112
+ * `ConsumerScope`. Wires:
113
+ *
114
+ * - `jwtFromRequest` → `ExtractJwt.fromAuthHeaderAsBearerToken()` (override via opts)
115
+ * - `secretOrKeyProvider` → JWKS-backed key resolution via `scope.jwks`
116
+ * - `issuer` → both `scope.expectedIssuer` AND the legacy `'heimdall'` literal (or only the per-app URL if `strictIssuer: true`)
117
+ * - `audience` → `scope.expectedAudience` (the app slug)
118
+ * - `algorithms` → `['RS256']` (override via opts)
119
+ *
120
+ * The verify callback you pass receives the parsed claims after
121
+ * passport-jwt has finished its checks.
122
+ */
123
+ declare function createHeimdallJwtStrategy(scope: ConsumerScope, verify: HeimdallVerifyCallback, options?: CreateHeimdallJwtStrategyOptions): any;
49
124
 
50
- export { createPassportSecretOrKeyProvider };
125
+ export { type CreateHeimdallJwtStrategyOptions, type HeimdallVerifyCallback, type PassportJwtStrategyCtor, createHeimdallJwtStrategy, createPassportSecretOrKeyProvider };
package/dist/index.js CHANGED
@@ -1,7 +1,12 @@
1
1
  import { KeyObject } from 'crypto';
2
2
  import { decodeProtectedHeader } from 'jose';
3
3
 
4
- // src/index.ts
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
5
10
  function createPassportSecretOrKeyProvider(scope) {
6
11
  return (_request, rawJwt, done) => {
7
12
  let header;
@@ -24,7 +29,35 @@ function createPassportSecretOrKeyProvider(scope) {
24
29
  );
25
30
  };
26
31
  }
32
+ function loadPassportJwt() {
33
+ try {
34
+ const mod = __require("passport-jwt");
35
+ return {
36
+ Strategy: mod.Strategy ?? mod.default?.Strategy,
37
+ ExtractJwt: mod.ExtractJwt ?? mod.default?.ExtractJwt
38
+ };
39
+ } catch {
40
+ throw new Error(
41
+ "@productcraft/heimdall-passport: `passport-jwt` is not installed. Run `npm install passport-jwt @types/passport-jwt`."
42
+ );
43
+ }
44
+ }
45
+ function createHeimdallJwtStrategy(scope, verify, options = {}) {
46
+ const passportJwt = loadPassportJwt();
47
+ const issuer = options.strictIssuer ? scope.expectedIssuer : scope.acceptedIssuers;
48
+ return new passportJwt.Strategy(
49
+ {
50
+ jwtFromRequest: options.jwtFromRequest ?? passportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),
51
+ secretOrKeyProvider: createPassportSecretOrKeyProvider(scope),
52
+ issuer,
53
+ audience: scope.expectedAudience,
54
+ algorithms: options.algorithms ?? ["RS256"],
55
+ passReqToCallback: options.passReqToCallback ?? false
56
+ },
57
+ verify
58
+ );
59
+ }
27
60
 
28
- export { createPassportSecretOrKeyProvider };
61
+ export { createHeimdallJwtStrategy, createPassportSecretOrKeyProvider };
29
62
  //# sourceMappingURL=index.js.map
30
63
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;AAsDO,SAAS,kCACd,KAAA,EAC6B;AAC7B,EAAA,OAAO,CAAC,QAAA,EAAU,MAAA,EAAQ,IAAA,KAAS;AACjC,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAA,GAAS,sBAAsB,MAAM,CAAA;AAAA,IACvC,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,GAAG,CAAA;AACR,MAAA;AAAA,IACF;AAEA,IAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,MAA6B,CAAA,CAAE,IAAA;AAAA,MAC/C,CAAC,SAAA,KAAc;AACb,QAAA,IAAI;AAKF,UAAA,MAAM,SAAA,GAAY,SAAA,CAAU,IAAA,CAAK,SAAS,CAAA;AAC1C,UAAA,IAAA,CAAK,MAAM,SAAS,CAAA;AAAA,QACtB,SAAS,GAAA,EAAK;AACZ,UAAA,IAAA,CAAK,GAAG,CAAA;AAAA,QACV;AAAA,MACF,CAAA;AAAA,MACA,CAAC,GAAA,KAAQ,IAAA,CAAK,GAAG;AAAA,KACnB;AAAA,EACF,CAAA;AACF","file":"index.js","sourcesContent":["/**\n * `@productcraft/heimdall-passport` — passport-jwt adapter for Heimdall.\n *\n * Install alongside `@productcraft/heimdall` + `passport-jwt`:\n *\n * ```bash\n * npm install @productcraft/heimdall @productcraft/heimdall-passport passport-jwt\n * ```\n *\n * Usage:\n *\n * ```ts\n * import passportJwt from \"passport-jwt\";\n * import { Heimdall } from \"@productcraft/heimdall\";\n * import { createPassportSecretOrKeyProvider } from \"@productcraft/heimdall-passport\";\n *\n * const heimdall = new Heimdall({ auth: { type: \"apiKey\", key: process.env.PCFT_KEY! } });\n * const scope = heimdall.consumer(\"my-app-slug\");\n *\n * new passportJwt.Strategy(\n * {\n * jwtFromRequest: passportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),\n * secretOrKeyProvider: createPassportSecretOrKeyProvider(scope),\n * issuer: scope.expectedIssuer,\n * algorithms: [\"ES256\"],\n * },\n * (payload, done) => done(null, payload),\n * );\n * ```\n */\n\nimport { KeyObject } from \"node:crypto\";\nimport { decodeProtectedHeader, type JWTHeaderParameters } from \"jose\";\n\nimport type { ConsumerScope } from \"@productcraft/heimdall\";\n\n/**\n * passport-jwt's `secretOrKeyProvider` callback signature. We don't\n * depend on passport-jwt's types directly (to keep it a soft dep),\n * so this is the same shape as `SecretOrKeyProvider` in their typings.\n */\ntype PassportSecretOrKeyProvider = (\n request: unknown,\n rawJwtToken: string,\n done: (err: unknown, secretOrKey?: string | Buffer | KeyObject) => void,\n) => void;\n\n/**\n * Returns a passport-jwt `secretOrKeyProvider` bound to a Heimdall\n * `ConsumerScope`. Decodes the protected header, looks up the matching\n * key via the scope's JWKS cache, and converts the resulting jose\n * `CryptoKey` into a Node `KeyObject` that `jsonwebtoken` (the library\n * passport-jwt delegates to) accepts.\n */\nexport function createPassportSecretOrKeyProvider(\n scope: ConsumerScope,\n): PassportSecretOrKeyProvider {\n return (_request, rawJwt, done) => {\n let header;\n try {\n header = decodeProtectedHeader(rawJwt);\n } catch (err) {\n done(err);\n return;\n }\n\n scope.jwks.getKey(header as JWTHeaderParameters).then(\n (cryptoKey) => {\n try {\n // `jsonwebtoken` (passport-jwt's verifier) doesn't accept\n // Web Crypto `CryptoKey`, but it does accept Node's\n // `KeyObject`. `KeyObject.from(cryptoKey)` was added in\n // Node 16.6 and is the right adapter.\n const keyObject = KeyObject.from(cryptoKey);\n done(null, keyObject);\n } catch (err) {\n done(err);\n }\n },\n (err) => done(err),\n );\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;AAsEO,SAAS,kCACd,KAAA,EAC6B;AAC7B,EAAA,OAAO,CAAC,QAAA,EAAU,MAAA,EAAQ,IAAA,KAAS;AACjC,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAA,GAAS,sBAAsB,MAAM,CAAA;AAAA,IACvC,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,GAAG,CAAA;AACR,MAAA;AAAA,IACF;AAEA,IAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,MAA6B,CAAA,CAAE,IAAA;AAAA,MAC/C,CAAC,SAAA,KAAc;AACb,QAAA,IAAI;AAKF,UAAA,MAAM,SAAA,GAAY,SAAA,CAAU,IAAA,CAAK,SAAS,CAAA;AAC1C,UAAA,IAAA,CAAK,MAAM,SAAS,CAAA;AAAA,QACtB,SAAS,GAAA,EAAK;AACZ,UAAA,IAAA,CAAK,GAAG,CAAA;AAAA,QACV;AAAA,MACF,CAAA;AAAA,MACA,CAAC,GAAA,KAAQ,IAAA,CAAK,GAAG;AAAA,KACnB;AAAA,EACF,CAAA;AACF;AA+DA,SAAS,eAAA,GAGP;AACA,EAAA,IAAI;AAEF,IAAA,MAAM,GAAA,GAAM,UAAQ,cAAc,CAAA;AAClC,IAAA,OAAO;AAAA,MACL,QAAA,EAAU,GAAA,CAAI,QAAA,IAAY,GAAA,CAAI,OAAA,EAAS,QAAA;AAAA,MACvC,UAAA,EAAY,GAAA,CAAI,UAAA,IAAc,GAAA,CAAI,OAAA,EAAS;AAAA,KAC7C;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACF;AAeO,SAAS,yBAAA,CACd,KAAA,EACA,MAAA,EACA,OAAA,GAA4C,EAAC,EAC7C;AACA,EAAA,MAAM,cAAc,eAAA,EAAgB;AACpC,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,YAAA,GACnB,KAAA,CAAM,iBACL,KAAA,CAAM,eAAA;AAEX,EAAA,OAAO,IAAI,WAAA,CAAY,QAAA;AAAA,IACrB;AAAA,MACE,cAAA,EACE,OAAA,CAAQ,cAAA,IAAkB,WAAA,CAAY,WAAW,2BAAA,EAA4B;AAAA,MAC/E,mBAAA,EAAqB,kCAAkC,KAAK,CAAA;AAAA,MAC5D,MAAA;AAAA,MACA,UAAU,KAAA,CAAM,gBAAA;AAAA,MAChB,UAAA,EAAY,OAAA,CAAQ,UAAA,IAAc,CAAC,OAAO,CAAA;AAAA,MAC1C,iBAAA,EAAmB,QAAQ,iBAAA,IAAqB;AAAA,KAClD;AAAA,IACA;AAAA,GACF;AACF","file":"index.js","sourcesContent":["/**\n * `@productcraft/heimdall-passport` — passport-jwt adapter for Heimdall.\n *\n * Install alongside `@productcraft/heimdall` + `passport-jwt`:\n *\n * ```bash\n * npm install @productcraft/heimdall @productcraft/heimdall-passport passport-jwt\n * ```\n *\n * Recommended one-call form:\n *\n * ```ts\n * import passportJwt from \"passport-jwt\";\n * import passport from \"passport\";\n * import { Heimdall } from \"@productcraft/heimdall\";\n * import { createHeimdallJwtStrategy } from \"@productcraft/heimdall-passport\";\n *\n * const heimdall = new Heimdall({ auth: { type: \"apiKey\", key: process.env.PCFT_KEY! } });\n * const scope = heimdall.consumer(\"my-app-slug\");\n *\n * passport.use(createHeimdallJwtStrategy(scope, (claims, done) => {\n * // claims is the verified Heimdall claims object (sub, role, permissions, ...)\n * return done(null, claims);\n * }));\n * ```\n *\n * The helper wires `secretOrKeyProvider` + `issuer` (per-app URL) +\n * `audience` (app slug) + algorithms automatically from the scope.\n *\n * If you need the underlying primitives — to compose with your own\n * options or pass a custom `jwtFromRequest` — use the lower-level\n * `createPassportSecretOrKeyProvider` directly:\n *\n * ```ts\n * new passportJwt.Strategy(\n * {\n * jwtFromRequest: passportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),\n * secretOrKeyProvider: createPassportSecretOrKeyProvider(scope),\n * issuer: scope.acceptedIssuers as string[],\n * audience: scope.expectedAudience,\n * algorithms: [\"RS256\", \"ES256\"],\n * },\n * (payload, done) => done(null, payload),\n * );\n * ```\n */\n\nimport { KeyObject } from \"node:crypto\";\nimport { decodeProtectedHeader, type JWTHeaderParameters } from \"jose\";\n\nimport type { ConsumerScope } from \"@productcraft/heimdall\";\n\n/**\n * passport-jwt's `secretOrKeyProvider` callback signature. We don't\n * depend on passport-jwt's types directly (to keep it a soft dep),\n * so this is the same shape as `SecretOrKeyProvider` in their typings.\n */\ntype PassportSecretOrKeyProvider = (\n request: unknown,\n rawJwtToken: string,\n done: (err: unknown, secretOrKey?: string | Buffer | KeyObject) => void,\n) => void;\n\n/**\n * Returns a passport-jwt `secretOrKeyProvider` bound to a Heimdall\n * `ConsumerScope`. Decodes the protected header, looks up the matching\n * key via the scope's JWKS cache, and converts the resulting jose\n * `CryptoKey` into a Node `KeyObject` that `jsonwebtoken` (the library\n * passport-jwt delegates to) accepts.\n */\nexport function createPassportSecretOrKeyProvider(\n scope: ConsumerScope,\n): PassportSecretOrKeyProvider {\n return (_request, rawJwt, done) => {\n let header;\n try {\n header = decodeProtectedHeader(rawJwt);\n } catch (err) {\n done(err);\n return;\n }\n\n scope.jwks.getKey(header as JWTHeaderParameters).then(\n (cryptoKey) => {\n try {\n // `jsonwebtoken` (passport-jwt's verifier) doesn't accept\n // Web Crypto `CryptoKey`, but it does accept Node's\n // `KeyObject`. `KeyObject.from(cryptoKey)` was added in\n // Node 16.6 and is the right adapter.\n const keyObject = KeyObject.from(cryptoKey);\n done(null, keyObject);\n } catch (err) {\n done(err);\n }\n },\n (err) => done(err),\n );\n };\n}\n\n/**\n * Shape of the verified-claims callback passport delegates to once a\n * token passes signature + issuer + audience + expiry checks. Mirrors\n * `passport-jwt`'s own `VerifyCallback` / `VerifyCallbackWithRequest`.\n */\nexport type HeimdallVerifyCallback = (\n payload: Record<string, unknown>,\n done: (err: unknown, user?: unknown, info?: unknown) => void,\n) => void;\n\n/**\n * Extra options for `createHeimdallJwtStrategy`. Defaults are the\n * Heimdall-recommended values; override here if you need a different\n * token source (cookie, custom header, ...), a stricter algorithm\n * allow-list, or want to skip the legacy-issuer acceptance window.\n */\nexport interface CreateHeimdallJwtStrategyOptions {\n /**\n * Where to read the bearer from. Defaults to\n * `ExtractJwt.fromAuthHeaderAsBearerToken()`. Pass any\n * passport-jwt extractor when you need a different source.\n */\n jwtFromRequest?: (req: unknown) => string | null;\n\n /**\n * JWS algorithms accepted by the underlying verifier. Defaults to\n * the algorithms Heimdall mints today (`['RS256']`); set explicitly\n * if your app is on a different signing alg or you also accept\n * customer-minted tokens with a different alg.\n */\n algorithms?: string[];\n\n /**\n * Pin only the per-app issuer URL (skip the legacy `'heimdall'`\n * literal). Defaults to `false` — verifying both keeps in-flight\n * tokens minted before the per-app-issuer migration valid. Set to\n * `true` once you're confident all old tokens have expired.\n */\n strictIssuer?: boolean;\n\n /** Whether the request object should be passed to the verify callback. Defaults to `false`. */\n passReqToCallback?: boolean;\n}\n\n/**\n * Constructor signature for `passport-jwt`'s `Strategy`. We don't take\n * a direct dep on passport-jwt to keep it a peer/soft dep — at runtime\n * the caller imports passportJwt and the helper does\n * `new passportJwt.Strategy(...)` via the constructor reference.\n */\nexport interface PassportJwtStrategyCtor {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n new (options: Record<string, unknown>, verify: HeimdallVerifyCallback): any;\n}\n\n/**\n * Lazy passport-jwt loader. We try to `require('passport-jwt')` from\n * the caller's dep graph at first use. Throws a clear error if it's\n * missing so the user doesn't see a cryptic `Cannot find module`\n * deep in the strategy constructor.\n */\nfunction loadPassportJwt(): {\n Strategy: PassportJwtStrategyCtor;\n ExtractJwt: { fromAuthHeaderAsBearerToken: () => (req: unknown) => string | null };\n} {\n try {\n // dynamic require so passport-jwt stays a peer dep\n const mod = require(\"passport-jwt\");\n return {\n Strategy: mod.Strategy ?? mod.default?.Strategy,\n ExtractJwt: mod.ExtractJwt ?? mod.default?.ExtractJwt,\n };\n } catch {\n throw new Error(\n \"@productcraft/heimdall-passport: `passport-jwt` is not installed. Run `npm install passport-jwt @types/passport-jwt`.\",\n );\n }\n}\n\n/**\n * Build a fully-configured `passport-jwt` strategy from a Heimdall\n * `ConsumerScope`. Wires:\n *\n * - `jwtFromRequest` → `ExtractJwt.fromAuthHeaderAsBearerToken()` (override via opts)\n * - `secretOrKeyProvider` → JWKS-backed key resolution via `scope.jwks`\n * - `issuer` → both `scope.expectedIssuer` AND the legacy `'heimdall'` literal (or only the per-app URL if `strictIssuer: true`)\n * - `audience` → `scope.expectedAudience` (the app slug)\n * - `algorithms` → `['RS256']` (override via opts)\n *\n * The verify callback you pass receives the parsed claims after\n * passport-jwt has finished its checks.\n */\nexport function createHeimdallJwtStrategy(\n scope: ConsumerScope,\n verify: HeimdallVerifyCallback,\n options: CreateHeimdallJwtStrategyOptions = {},\n) {\n const passportJwt = loadPassportJwt();\n const issuer = options.strictIssuer\n ? scope.expectedIssuer\n : (scope.acceptedIssuers as string[]);\n\n return new passportJwt.Strategy(\n {\n jwtFromRequest:\n options.jwtFromRequest ?? passportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),\n secretOrKeyProvider: createPassportSecretOrKeyProvider(scope),\n issuer,\n audience: scope.expectedAudience,\n algorithms: options.algorithms ?? [\"RS256\"],\n passReqToCallback: options.passReqToCallback ?? false,\n },\n verify,\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productcraft/heimdall-passport",
3
- "version": "0.0.2",
3
+ "version": "0.1.0",
4
4
  "description": "Passport-JWT adapter for ProductCraft Heimdall. Plug a Heimdall ConsumerScope into a `passport-jwt` Strategy so existing Passport setups can authenticate Heimdall-issued tokens.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -21,10 +21,6 @@
21
21
  "engines": {
22
22
  "node": ">=18"
23
23
  },
24
- "scripts": {
25
- "build": "tsup",
26
- "test": "vitest run"
27
- },
28
24
  "keywords": [
29
25
  "productcraft",
30
26
  "sdk",
@@ -47,10 +43,14 @@
47
43
  "provenance": true
48
44
  },
49
45
  "dependencies": {
50
- "@productcraft/heimdall": "workspace:^",
51
- "jose": "^6.2.3"
46
+ "jose": "^6.2.3",
47
+ "@productcraft/heimdall": "^0.2.0"
52
48
  },
53
49
  "peerDependencies": {
54
50
  "passport-jwt": "^4.0.0"
51
+ },
52
+ "scripts": {
53
+ "build": "tsup",
54
+ "test": "vitest run"
55
55
  }
56
- }
56
+ }