@frontmcp/auth 0.8.1 → 0.10.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/README.md CHANGED
@@ -1,11 +1,85 @@
1
- # auth
1
+ # @frontmcp/auth
2
2
 
3
- This library was generated with [Nx](https://nx.dev).
3
+ Authentication, session management, and credential vault for FrontMCP servers.
4
4
 
5
- ## Building
5
+ [![NPM](https://img.shields.io/npm/v/@frontmcp/auth.svg)](https://www.npmjs.com/package/@frontmcp/auth)
6
6
 
7
- Run `nx build auth` to build the library.
7
+ ## Install
8
8
 
9
- ## Running unit tests
9
+ ```bash
10
+ npm install @frontmcp/auth
11
+ ```
10
12
 
11
- Run `nx test auth` to execute the unit tests via [Jest](https://jestjs.io).
13
+ > Typically consumed via `@frontmcp/sdk` direct installation is only needed for advanced use cases.
14
+
15
+ ## Features
16
+
17
+ - **Remote OAuth** — delegate authentication to an external IdP with optional DCR ([docs][docs-remote])
18
+ - **Local OAuth** — built-in token issuance with configurable sign keys ([docs][docs-local])
19
+ - **JWKS validation** — JSON Web Key Set discovery and token verification ([docs][docs-jwks])
20
+ - **OAuth stores** — session, token, and authorization code persistence (memory, Redis, Vercel KV) ([docs][docs-stores])
21
+ - **Credential vault** — encrypted storage for secrets and API keys ([docs][docs-vault])
22
+ - **PKCE** — Proof Key for Code Exchange (RFC 7636) built on `@frontmcp/utils` crypto ([docs][docs-pkce])
23
+ - **CIMD** — Client Instance Machine Detection for session continuity ([docs][docs-cimd])
24
+ - **Auth UI templates** — consent, login, and error pages ([docs][docs-ui])
25
+ - **Audience validation** — per-app audience and scope enforcement ([docs][docs-audience])
26
+ - **Token vault** — secure token exchange and refresh management ([docs][docs-token-vault])
27
+
28
+ ## Quick Example
29
+
30
+ ```ts
31
+ import { FrontMcp, App } from '@frontmcp/sdk';
32
+
33
+ @FrontMcp({
34
+ info: { name: 'Secure Server', version: '1.0.0' },
35
+ apps: [MyApp],
36
+ auth: {
37
+ type: 'remote',
38
+ name: 'my-idp',
39
+ baseUrl: 'https://idp.example.com',
40
+ },
41
+ })
42
+ export default class Server {}
43
+ ```
44
+
45
+ > Full guide: [Authentication Overview][docs-overview]
46
+
47
+ ## Docs
48
+
49
+ | Topic | Link |
50
+ | ----------------- | ---------------------------------------------- |
51
+ | Overview | [Authentication Overview][docs-overview] |
52
+ | Remote OAuth | [Remote OAuth][docs-remote] |
53
+ | Local OAuth | [Local OAuth][docs-local] |
54
+ | JWKS | [JWKS Validation][docs-jwks] |
55
+ | Session stores | [Session Stores][docs-stores] |
56
+ | Credential vault | [Credential Vault][docs-vault] |
57
+ | PKCE | [PKCE][docs-pkce] |
58
+ | CIMD | [Client Instance Machine Detection][docs-cimd] |
59
+ | Auth UI | [Auth UI Templates][docs-ui] |
60
+ | Audience & scopes | [Audience Validation][docs-audience] |
61
+ | Token vault | [Token Vault][docs-token-vault] |
62
+
63
+ ## Related Packages
64
+
65
+ - [`@frontmcp/sdk`](../sdk) — core framework (imports auth internally)
66
+ - [`@frontmcp/utils`](../utils) — crypto primitives used by PKCE and vault
67
+ - [`@frontmcp/ui`](../ui) — consent and login page components
68
+
69
+ ## License
70
+
71
+ Apache-2.0 — see [LICENSE](../../LICENSE).
72
+
73
+ <!-- links -->
74
+
75
+ [docs-overview]: https://docs.agentfront.dev/frontmcp/authentication/overview
76
+ [docs-remote]: https://docs.agentfront.dev/frontmcp/authentication/remote
77
+ [docs-local]: https://docs.agentfront.dev/frontmcp/authentication/local
78
+ [docs-jwks]: https://docs.agentfront.dev/frontmcp/authentication/jwks
79
+ [docs-stores]: https://docs.agentfront.dev/frontmcp/authentication/session-stores
80
+ [docs-vault]: https://docs.agentfront.dev/frontmcp/authentication/credential-vault
81
+ [docs-pkce]: https://docs.agentfront.dev/frontmcp/authentication/pkce
82
+ [docs-cimd]: https://docs.agentfront.dev/frontmcp/authentication/cimd
83
+ [docs-ui]: https://docs.agentfront.dev/frontmcp/authentication/auth-ui
84
+ [docs-audience]: https://docs.agentfront.dev/frontmcp/authentication/audience
85
+ [docs-token-vault]: https://docs.agentfront.dev/frontmcp/authentication/token-vault
package/esm/index.mjs CHANGED
@@ -219,7 +219,12 @@ import { bytesToHex, randomBytes, rsaVerify, createKeyPersistence } from "@front
219
219
 
220
220
  // libs/auth/src/jwks/jwks.utils.ts
221
221
  function trimSlash(s) {
222
- return (s ?? "").replace(/\/+$/, "");
222
+ const str = s ?? "";
223
+ let end = str.length;
224
+ while (end > 0 && str[end - 1] === "/") {
225
+ end--;
226
+ }
227
+ return str.slice(0, end);
223
228
  }
224
229
  function normalizeIssuer(u) {
225
230
  return trimSlash(String(u ?? ""));
@@ -2627,10 +2632,8 @@ function parseWwwAuthenticate(header) {
2627
2632
  return result;
2628
2633
  }
2629
2634
  const paramString = header.substring(6).trim();
2630
- const paramRegex = /(\w+)="([^"\\]*(?:\\.[^"\\]*)*)"/g;
2631
- let match;
2632
- while ((match = paramRegex.exec(paramString)) !== null) {
2633
- const [, key, value] = match;
2635
+ const params = parseQuotedParams(paramString);
2636
+ for (const [key, value] of params) {
2634
2637
  const unescapedValue = unescapeQuotedString(value);
2635
2638
  switch (key.toLowerCase()) {
2636
2639
  case "resource_metadata":
@@ -2655,6 +2658,54 @@ function parseWwwAuthenticate(header) {
2655
2658
  }
2656
2659
  return result;
2657
2660
  }
2661
+ function parseQuotedParams(input) {
2662
+ const result = [];
2663
+ let i = 0;
2664
+ const len = input.length;
2665
+ while (i < len) {
2666
+ while (i < len && (input[i] === " " || input[i] === "," || input[i] === " ")) {
2667
+ i++;
2668
+ }
2669
+ if (i >= len) break;
2670
+ const keyStart = i;
2671
+ while (i < len && /\w/.test(input[i])) {
2672
+ i++;
2673
+ }
2674
+ const key = input.slice(keyStart, i);
2675
+ if (!key) break;
2676
+ while (i < len && (input[i] === " " || input[i] === " ")) {
2677
+ i++;
2678
+ }
2679
+ if (i >= len || input[i] !== "=") {
2680
+ while (i < len && input[i] !== ",") i++;
2681
+ continue;
2682
+ }
2683
+ i++;
2684
+ while (i < len && (input[i] === " " || input[i] === " ")) {
2685
+ i++;
2686
+ }
2687
+ if (i >= len || input[i] !== '"') {
2688
+ while (i < len && input[i] !== ",") i++;
2689
+ continue;
2690
+ }
2691
+ i++;
2692
+ let value = "";
2693
+ while (i < len && input[i] !== '"') {
2694
+ if (input[i] === "\\" && i + 1 < len) {
2695
+ value += input[i + 1];
2696
+ i += 2;
2697
+ } else {
2698
+ value += input[i];
2699
+ i++;
2700
+ }
2701
+ }
2702
+ if (i < len && input[i] === '"') {
2703
+ i++;
2704
+ }
2705
+ result.push([key, value]);
2706
+ }
2707
+ return result;
2708
+ }
2658
2709
  function escapeQuotedString(value) {
2659
2710
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
2660
2711
  }
@@ -2664,7 +2715,11 @@ function unescapeQuotedString(value) {
2664
2715
  function normalizePathSegment(path2) {
2665
2716
  if (!path2 || path2 === "/") return "";
2666
2717
  const normalized = path2.startsWith("/") ? path2 : `/${path2}`;
2667
- return normalized.replace(/\/+$/, "");
2718
+ let end = normalized.length;
2719
+ while (end > 0 && normalized[end - 1] === "/") {
2720
+ end--;
2721
+ }
2722
+ return normalized.slice(0, end);
2668
2723
  }
2669
2724
 
2670
2725
  // libs/auth/src/utils/audience.validator.ts
package/esm/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/auth",
3
- "version": "0.8.1",
3
+ "version": "0.10.0",
4
4
  "description": "FrontMCP Auth - Authentication, session management, and credential vault",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -50,7 +50,7 @@
50
50
  "zod": "^4.0.0"
51
51
  },
52
52
  "dependencies": {
53
- "@frontmcp/utils": "0.8.1",
53
+ "@frontmcp/utils": "0.10.0",
54
54
  "jose": "^6.0.0"
55
55
  },
56
56
  "devDependencies": {
package/index.js CHANGED
@@ -361,7 +361,12 @@ var import_utils = require("@frontmcp/utils");
361
361
 
362
362
  // libs/auth/src/jwks/jwks.utils.ts
363
363
  function trimSlash(s) {
364
- return (s ?? "").replace(/\/+$/, "");
364
+ const str = s ?? "";
365
+ let end = str.length;
366
+ while (end > 0 && str[end - 1] === "/") {
367
+ end--;
368
+ }
369
+ return str.slice(0, end);
365
370
  }
366
371
  function normalizeIssuer(u) {
367
372
  return trimSlash(String(u ?? ""));
@@ -2756,10 +2761,8 @@ function parseWwwAuthenticate(header) {
2756
2761
  return result;
2757
2762
  }
2758
2763
  const paramString = header.substring(6).trim();
2759
- const paramRegex = /(\w+)="([^"\\]*(?:\\.[^"\\]*)*)"/g;
2760
- let match;
2761
- while ((match = paramRegex.exec(paramString)) !== null) {
2762
- const [, key, value] = match;
2764
+ const params = parseQuotedParams(paramString);
2765
+ for (const [key, value] of params) {
2763
2766
  const unescapedValue = unescapeQuotedString(value);
2764
2767
  switch (key.toLowerCase()) {
2765
2768
  case "resource_metadata":
@@ -2784,6 +2787,54 @@ function parseWwwAuthenticate(header) {
2784
2787
  }
2785
2788
  return result;
2786
2789
  }
2790
+ function parseQuotedParams(input) {
2791
+ const result = [];
2792
+ let i = 0;
2793
+ const len = input.length;
2794
+ while (i < len) {
2795
+ while (i < len && (input[i] === " " || input[i] === "," || input[i] === " ")) {
2796
+ i++;
2797
+ }
2798
+ if (i >= len) break;
2799
+ const keyStart = i;
2800
+ while (i < len && /\w/.test(input[i])) {
2801
+ i++;
2802
+ }
2803
+ const key = input.slice(keyStart, i);
2804
+ if (!key) break;
2805
+ while (i < len && (input[i] === " " || input[i] === " ")) {
2806
+ i++;
2807
+ }
2808
+ if (i >= len || input[i] !== "=") {
2809
+ while (i < len && input[i] !== ",") i++;
2810
+ continue;
2811
+ }
2812
+ i++;
2813
+ while (i < len && (input[i] === " " || input[i] === " ")) {
2814
+ i++;
2815
+ }
2816
+ if (i >= len || input[i] !== '"') {
2817
+ while (i < len && input[i] !== ",") i++;
2818
+ continue;
2819
+ }
2820
+ i++;
2821
+ let value = "";
2822
+ while (i < len && input[i] !== '"') {
2823
+ if (input[i] === "\\" && i + 1 < len) {
2824
+ value += input[i + 1];
2825
+ i += 2;
2826
+ } else {
2827
+ value += input[i];
2828
+ i++;
2829
+ }
2830
+ }
2831
+ if (i < len && input[i] === '"') {
2832
+ i++;
2833
+ }
2834
+ result.push([key, value]);
2835
+ }
2836
+ return result;
2837
+ }
2787
2838
  function escapeQuotedString(value) {
2788
2839
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
2789
2840
  }
@@ -2793,7 +2844,11 @@ function unescapeQuotedString(value) {
2793
2844
  function normalizePathSegment(path2) {
2794
2845
  if (!path2 || path2 === "/") return "";
2795
2846
  const normalized = path2.startsWith("/") ? path2 : `/${path2}`;
2796
- return normalized.replace(/\/+$/, "");
2847
+ let end = normalized.length;
2848
+ while (end > 0 && normalized[end - 1] === "/") {
2849
+ end--;
2850
+ }
2851
+ return normalized.slice(0, end);
2797
2852
  }
2798
2853
 
2799
2854
  // libs/auth/src/utils/audience.validator.ts
@@ -1 +1 @@
1
- {"version":3,"file":"jwks.utils.d.ts","sourceRoot":"","sources":["../../src/jwks/jwks.utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,UAElC;AACD,wBAAgB,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,UAEzC;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAgBxF"}
1
+ {"version":3,"file":"jwks.utils.d.ts","sourceRoot":"","sources":["../../src/jwks/jwks.utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,UAQlC;AACD,wBAAgB,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,UAEzC;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAgBxF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/auth",
3
- "version": "0.8.1",
3
+ "version": "0.10.0",
4
4
  "description": "FrontMCP Auth - Authentication, session management, and credential vault",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -50,7 +50,7 @@
50
50
  "zod": "^4.0.0"
51
51
  },
52
52
  "dependencies": {
53
- "@frontmcp/utils": "0.8.1",
53
+ "@frontmcp/utils": "0.10.0",
54
54
  "jose": "^6.0.0"
55
55
  },
56
56
  "devDependencies": {
@@ -91,6 +91,8 @@ export declare function buildInvalidRequestHeader(prmUrl: string, description?:
91
91
  /**
92
92
  * Parse a WWW-Authenticate header value
93
93
  *
94
+ * Uses character-by-character parsing to avoid ReDoS vulnerabilities.
95
+ *
94
96
  * @param header - The WWW-Authenticate header value
95
97
  * @returns Parsed header options
96
98
  */
@@ -1 +1 @@
1
- {"version":3,"file":"www-authenticate.utils.d.ts","sourceRoot":"","sources":["../../src/utils/www-authenticate.utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAEzF;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE1B;;OAEG;IACH,KAAK,CAAC,EAAE,eAAe,CAAC;IAExB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,sBAA2B,GAAG,MAAM,CAwCjF;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAIzF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAMpF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAOnH;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAMtF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,sBAAsB,CAwC3E"}
1
+ {"version":3,"file":"www-authenticate.utils.d.ts","sourceRoot":"","sources":["../../src/utils/www-authenticate.utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAEzF;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE1B;;OAEG;IACH,KAAK,CAAC,EAAE,eAAe,CAAC;IAExB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,sBAA2B,GAAG,MAAM,CAwCjF;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAIzF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAMpF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAOnH;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAMtF;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,sBAAsB,CAsC3E"}