@ingram-tech/nk-auth 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/jwt.d.ts CHANGED
@@ -7,7 +7,7 @@ import { type JWTPayload } from "jose";
7
7
  * exposed at `/api/auth/jwks` so the backend can verify it (`verifyBackendJwt`).
8
8
  */
9
9
  export interface BackendJwtConfig {
10
- /** Expected audience of the site's backend, e.g. "ingram-wiki-backend". */
10
+ /** Expected audience of the site's backend, e.g. "my-app-backend". */
11
11
  audience: string;
12
12
  /** Token lifetime, e.g. "15m". */
13
13
  expirationTime?: string;
@@ -23,6 +23,14 @@ export declare const backendJwtOptions: (config: BackendJwtConfig) => JwtOptions
23
23
  * Verify a Better-Auth-minted backend JWT (EdDSA) against the issuer's JWKS.
24
24
  * For use by the backend that consumes `backendJwtOptions` tokens. Throws on an
25
25
  * invalid/expired token or audience/issuer mismatch; returns the claims.
26
+ *
27
+ * Hardened against Better Auth signing-key rotation: jose's `createRemoteJWKSet`
28
+ * refuses to refetch the JWKS for `cooldownDuration` (30s default) after any
29
+ * fetch, so a token signed with a freshly rotated key whose `kid` isn't yet in
30
+ * the cached set would fail for the *whole* cooldown window — a ~30s burst of
31
+ * auth failures on every request that verifies a token. On a no-matching-key
32
+ * miss we force a single `.reload()` (which bypasses the cooldown) and retry, so
33
+ * a rotation costs one extra fetch instead of a brief outage.
26
34
  */
27
35
  export declare const verifyBackendJwt: (params: {
28
36
  token: string;
package/dist/jwt.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAsB,KAAK,UAAU,EAAa,MAAM,MAAM,CAAC;AAEtE;;;;;GAKG;AAEH,MAAM,WAAW,gBAAgB;IAChC,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,sEAAsE;AACtE,eAAO,MAAM,iBAAiB,GAAI,QAAQ,gBAAgB,KAAG,UAM3D,CAAC;AAIH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAU,QAAQ;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CACf,KAAG,OAAO,CAAC,UAAU,CAerB,CAAC"}
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAGN,KAAK,UAAU,EAEf,MAAM,MAAM,CAAC;AAEd;;;;;GAKG;AAEH,MAAM,WAAW,gBAAgB;IAChC,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,sEAAsE;AACtE,eAAO,MAAM,iBAAiB,GAAI,QAAQ,gBAAgB,KAAG,UAM3D,CAAC;AAIH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,GAAU,QAAQ;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CACf,KAAG,OAAO,CAAC,UAAU,CAyBrB,CAAC"}
package/dist/jwt.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createRemoteJWKSet, jwtVerify } from "jose";
1
+ import { createRemoteJWKSet, errors as joseErrors, jwtVerify, } from "jose";
2
2
  /** `jwt` plugin options for a backend-API token (custom audience). */
3
3
  export const backendJwtOptions = (config) => ({
4
4
  disableSettingJwtHeader: config.disableSettingJwtHeader,
@@ -12,6 +12,14 @@ const jwksCache = new Map();
12
12
  * Verify a Better-Auth-minted backend JWT (EdDSA) against the issuer's JWKS.
13
13
  * For use by the backend that consumes `backendJwtOptions` tokens. Throws on an
14
14
  * invalid/expired token or audience/issuer mismatch; returns the claims.
15
+ *
16
+ * Hardened against Better Auth signing-key rotation: jose's `createRemoteJWKSet`
17
+ * refuses to refetch the JWKS for `cooldownDuration` (30s default) after any
18
+ * fetch, so a token signed with a freshly rotated key whose `kid` isn't yet in
19
+ * the cached set would fail for the *whole* cooldown window — a ~30s burst of
20
+ * auth failures on every request that verifies a token. On a no-matching-key
21
+ * miss we force a single `.reload()` (which bypasses the cooldown) and retry, so
22
+ * a rotation costs one extra fetch instead of a brief outage.
15
23
  */
16
24
  export const verifyBackendJwt = async (params) => {
17
25
  const url = typeof params.jwksUrl === "string" ? new URL(params.jwksUrl) : params.jwksUrl;
@@ -21,11 +29,23 @@ export const verifyBackendJwt = async (params) => {
21
29
  jwks = createRemoteJWKSet(url);
22
30
  jwksCache.set(cacheKey, jwks);
23
31
  }
24
- const { payload } = await jwtVerify(params.token, jwks, {
32
+ const options = {
25
33
  audience: params.audience,
26
34
  issuer: params.issuer,
27
35
  algorithms: ["EdDSA"],
28
- });
29
- return payload;
36
+ };
37
+ try {
38
+ const { payload } = await jwtVerify(params.token, jwks, options);
39
+ return payload;
40
+ }
41
+ catch (err) {
42
+ if (!(err instanceof joseErrors.JWKSNoMatchingKey))
43
+ throw err;
44
+ // `kid` absent from the cached JWKS — almost always a just-rotated signing
45
+ // key. Force a refresh past the cooldown and retry exactly once.
46
+ await jwks.reload();
47
+ const { payload } = await jwtVerify(params.token, jwks, options);
48
+ return payload;
49
+ }
30
50
  };
31
51
  //# sourceMappingURL=jwt.js.map
package/dist/jwt.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAmB,SAAS,EAAE,MAAM,MAAM,CAAC;AAqBtE,sEAAsE;AACtE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAwB,EAAc,EAAE,CAAC,CAAC;IAC3E,uBAAuB,EAAE,MAAM,CAAC,uBAAuB;IACvD,GAAG,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,cAAc,EAAE,MAAM,CAAC,cAAc;KACrC;CACD,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,IAAI,GAAG,EAAiD,CAAC;AAE3E;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,MAMtC,EAAuB,EAAE;IACzB,MAAM,GAAG,GACR,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IAC/E,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAChC,IAAI,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,IAAI,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC/B,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE;QACvD,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,UAAU,EAAE,CAAC,OAAO,CAAC;KACrB,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC"}
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AACA,OAAO,EACN,kBAAkB,EAClB,MAAM,IAAI,UAAU,EAEpB,SAAS,GACT,MAAM,MAAM,CAAC;AAqBd,sEAAsE;AACtE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAwB,EAAc,EAAE,CAAC,CAAC;IAC3E,uBAAuB,EAAE,MAAM,CAAC,uBAAuB;IACvD,GAAG,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,cAAc,EAAE,MAAM,CAAC,cAAc;KACrC;CACD,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,IAAI,GAAG,EAAiD,CAAC;AAE3E;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,MAMtC,EAAuB,EAAE;IACzB,MAAM,GAAG,GACR,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IAC/E,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IAChC,IAAI,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,IAAI,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAC/B,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,MAAM,OAAO,GAAG;QACf,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,UAAU,EAAE,CAAC,OAAO,CAAC;KACrB,CAAC;IACF,IAAI,CAAC;QACJ,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACjE,OAAO,OAAO,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,CAAC,GAAG,YAAY,UAAU,CAAC,iBAAiB,CAAC;YAAE,MAAM,GAAG,CAAC;QAC9D,2EAA2E;QAC3E,iEAAiE;QACjE,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACjE,OAAO,OAAO,CAAC;IAChB,CAAC;AACF,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingram-tech/nk-auth",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "The Ingram Better Auth foundation: composable presets (org, dual-shape JWT, active-org hooks, pg pool) for Next.js sites.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -61,10 +61,10 @@
61
61
  "test": "vitest run"
62
62
  },
63
63
  "dependencies": {
64
- "@ingram-tech/nk-db": "^1.0.0",
64
+ "@ingram-tech/nk-db": "^1.1.0",
65
65
  "bcrypt": "^6.0.0",
66
- "jose": "^6.0.0",
67
- "zod": "^4.0.0"
66
+ "jose": "^6.2.3",
67
+ "zod": "^4.4.3"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "@better-auth/passkey": "^1.6.0",
@@ -88,17 +88,17 @@
88
88
  }
89
89
  },
90
90
  "devDependencies": {
91
- "@better-auth/passkey": "^1.6.0",
91
+ "@better-auth/passkey": "^1.6.22",
92
92
  "@ingram-tech/nk-dev": "0.2.4",
93
93
  "@types/bcrypt": "^6.0.0",
94
- "@types/node": "^25.0.0",
95
- "@types/pg": "^8.11.0",
96
- "@types/react": "^19.0.0",
97
- "better-auth": "^1.6.0",
94
+ "@types/node": "^26.0.1",
95
+ "@types/pg": "^8.20.0",
96
+ "@types/react": "^19.2.17",
97
+ "better-auth": "^1.6.22",
98
98
  "next": "^16.2.9",
99
- "pg": "^8.13.0",
100
- "react": "^19.0.0",
99
+ "pg": "^8.22.0",
100
+ "react": "^19.2.7",
101
101
  "typescript": "^6.0.3",
102
- "vitest": "^4.1.6"
102
+ "vitest": "^4.1.9"
103
103
  }
104
104
  }