@memberjunction/auth-providers 5.33.0 → 5.34.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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @memberjunction/auth-providers@5.33.0 build
2
+ > @memberjunction/auth-providers@5.34.0 build
3
3
  > tsc && tsc-alias -f
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @memberjunction/auth-providers
2
2
 
3
+ ## 5.34.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [003317f]
8
+ - Updated dependencies [cfffb6d]
9
+ - Updated dependencies [e999e0d]
10
+ - Updated dependencies [389d356]
11
+ - Updated dependencies [ae5cfbd]
12
+ - Updated dependencies [6d8ee1a]
13
+ - Updated dependencies [72cb92e]
14
+ - @memberjunction/core@5.34.0
15
+ - @memberjunction/global@5.34.0
16
+
3
17
  ## 5.33.0
4
18
 
5
19
  ### Patch Changes
@@ -12,7 +12,17 @@ import './providers/GoogleProvider.js';
12
12
  */
13
13
  export declare class AuthProviderFactory extends BaseSingleton<AuthProviderFactory> {
14
14
  private providers;
15
+ /**
16
+ * Cache of resolved issuer → provider mappings. Bounded LRU(50) — prior
17
+ * unbounded `Map` was a low-effort DoS vector: a misconfigured/malicious
18
+ * client supplying arbitrary issuer URLs would walk the map up indefinitely.
19
+ * In production there should never be more than a handful of legitimate
20
+ * issuers. See audit R2-C4.
21
+ */
15
22
  private issuerCache;
23
+ /**
24
+ * Cache of issuer → all matching providers. Same LRU bound as `issuerCache`.
25
+ */
16
26
  private issuerMultiCache;
17
27
  constructor();
18
28
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"AuthProviderFactory.d.ts","sourceRoot":"","sources":["../src/AuthProviderFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAY,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGjE,OAAO,8BAA8B,CAAC;AACtC,OAAO,6BAA6B,CAAC;AACrC,OAAO,6BAA6B,CAAC;AACrC,OAAO,gCAAgC,CAAC;AACxC,OAAO,+BAA+B,CAAC;AAEvC;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,aAAa,CAAC,mBAAmB,CAAC;IACzE,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,WAAW,CAAyC;IAC5D,OAAO,CAAC,gBAAgB,CAA2C;;IAMnE;;OAEG;IACH,WAAkB,QAAQ,IAAI,mBAAmB,CAEhD;IAED;;;OAGG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa;IAsBhE;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAcvC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAkBtD;;;;;;OAMG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE;IAoB/C;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIlD;;OAEG;IACH,eAAe,IAAI,aAAa,EAAE;IAIlC;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,MAAM,CAAC,0BAA0B,IAAI,MAAM,EAAE;IAW7C;;OAEG;IACH,MAAM,CAAC,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CASvD"}
1
+ {"version":3,"file":"AuthProviderFactory.d.ts","sourceRoot":"","sources":["../src/AuthProviderFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAY,aAAa,EAAc,MAAM,wBAAwB,CAAC;AAG7E,OAAO,8BAA8B,CAAC;AACtC,OAAO,6BAA6B,CAAC;AACrC,OAAO,6BAA6B,CAAC;AACrC,OAAO,gCAAgC,CAAC;AACxC,OAAO,+BAA+B,CAAC;AAEvC;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,aAAa,CAAC,mBAAmB,CAAC;IACzE,OAAO,CAAC,SAAS,CAAyC;IAC1D;;;;;;OAMG;IACH,OAAO,CAAC,WAAW,CAA6F;IAChH;;OAEG;IACH,OAAO,CAAC,gBAAgB,CAAiG;;IAMzH;;OAEG;IACH,WAAkB,QAAQ,IAAI,mBAAmB,CAEhD;IAED;;;OAGG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa;IAsBhE;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAcvC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAmBtD;;;;;;OAMG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE;IAqB/C;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIlD;;OAEG;IACH,eAAe,IAAI,aAAa,EAAE;IAIlC;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,MAAM,CAAC,0BAA0B,IAAI,MAAM,EAAE;IAW7C;;OAEG;IACH,MAAM,CAAC,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CASvD"}
@@ -1,5 +1,5 @@
1
1
  import { BaseAuthProvider } from './BaseAuthProvider.js';
2
- import { MJGlobal, BaseSingleton } from '@memberjunction/global';
2
+ import { MJGlobal, BaseSingleton, MJLruCache } from '@memberjunction/global';
3
3
  // Import providers to ensure they're registered
4
4
  import './providers/Auth0Provider.js';
5
5
  import './providers/MSALProvider.js';
@@ -14,8 +14,18 @@ export class AuthProviderFactory extends BaseSingleton {
14
14
  constructor() {
15
15
  super();
16
16
  this.providers = new Map();
17
- this.issuerCache = new Map();
18
- this.issuerMultiCache = new Map();
17
+ /**
18
+ * Cache of resolved issuer → provider mappings. Bounded LRU(50) — prior
19
+ * unbounded `Map` was a low-effort DoS vector: a misconfigured/malicious
20
+ * client supplying arbitrary issuer URLs would walk the map up indefinitely.
21
+ * In production there should never be more than a handful of legitimate
22
+ * issuers. See audit R2-C4.
23
+ */
24
+ this.issuerCache = new MJLruCache({ maxSize: 50 });
25
+ /**
26
+ * Cache of issuer → all matching providers. Same LRU bound as `issuerCache`.
27
+ */
28
+ this.issuerMultiCache = new MJLruCache({ maxSize: 50 });
19
29
  }
20
30
  /**
21
31
  * Gets the singleton instance of the factory
@@ -52,8 +62,8 @@ export class AuthProviderFactory extends BaseSingleton {
52
62
  }
53
63
  this.providers.set(provider.name, provider);
54
64
  // Clear issuer caches when registering new provider
55
- this.issuerCache.clear();
56
- this.issuerMultiCache.clear();
65
+ this.issuerCache.Clear();
66
+ this.issuerMultiCache.Clear();
57
67
  console.log(`Registered auth provider: ${provider.name} with issuer: ${provider.issuer}`);
58
68
  }
59
69
  /**
@@ -61,14 +71,15 @@ export class AuthProviderFactory extends BaseSingleton {
61
71
  */
62
72
  getByIssuer(issuer) {
63
73
  // Check cache first
64
- if (this.issuerCache.has(issuer)) {
65
- return this.issuerCache.get(issuer);
74
+ const cached = this.issuerCache.Get(issuer);
75
+ if (cached) {
76
+ return cached;
66
77
  }
67
78
  // Search through providers
68
79
  for (const provider of this.providers.values()) {
69
80
  if (provider.matchesIssuer(issuer)) {
70
81
  // Cache for future lookups
71
- this.issuerCache.set(issuer, provider);
82
+ this.issuerCache.Set(issuer, provider);
72
83
  return provider;
73
84
  }
74
85
  }
@@ -83,8 +94,9 @@ export class AuthProviderFactory extends BaseSingleton {
83
94
  */
84
95
  getAllByIssuer(issuer) {
85
96
  // Check multi-provider cache first
86
- if (this.issuerMultiCache.has(issuer)) {
87
- return this.issuerMultiCache.get(issuer);
97
+ const cached = this.issuerMultiCache.Get(issuer);
98
+ if (cached) {
99
+ return cached;
88
100
  }
89
101
  const matches = [];
90
102
  for (const provider of this.providers.values()) {
@@ -93,7 +105,7 @@ export class AuthProviderFactory extends BaseSingleton {
93
105
  }
94
106
  }
95
107
  if (matches.length > 0) {
96
- this.issuerMultiCache.set(issuer, matches);
108
+ this.issuerMultiCache.Set(issuer, matches);
97
109
  }
98
110
  return matches;
99
111
  }
@@ -120,8 +132,8 @@ export class AuthProviderFactory extends BaseSingleton {
120
132
  */
121
133
  clear() {
122
134
  this.providers.clear();
123
- this.issuerCache.clear();
124
- this.issuerMultiCache.clear();
135
+ this.issuerCache.Clear();
136
+ this.issuerMultiCache.Clear();
125
137
  }
126
138
  /**
127
139
  * Gets all registered provider types from the ClassFactory
@@ -1 +1 @@
1
- {"version":3,"file":"AuthProviderFactory.js","sourceRoot":"","sources":["../src/AuthProviderFactory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AuthProviderFactory.js","sourceRoot":"","sources":["../src/AuthProviderFactory.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/auth-providers",
3
- "version": "5.33.0",
3
+ "version": "5.34.0",
4
4
  "description": "Authentication provider interfaces, base classes, and implementations for MemberJunction",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,8 +11,8 @@
11
11
  "test:watch": "vitest"
12
12
  },
13
13
  "dependencies": {
14
- "@memberjunction/core": "5.33.0",
15
- "@memberjunction/global": "5.33.0",
14
+ "@memberjunction/core": "5.34.0",
15
+ "@memberjunction/global": "5.34.0",
16
16
  "graphql": "^16.12.0",
17
17
  "jsonwebtoken": "9.0.3",
18
18
  "jwks-rsa": "^3.2.2"
@@ -1,7 +1,7 @@
1
1
  import { AuthProviderConfig } from '@memberjunction/core';
2
2
  import { IAuthProvider } from './IAuthProvider.js';
3
3
  import { BaseAuthProvider } from './BaseAuthProvider.js';
4
- import { MJGlobal, BaseSingleton } from '@memberjunction/global';
4
+ import { MJGlobal, BaseSingleton, MJLruCache } from '@memberjunction/global';
5
5
 
6
6
  // Import providers to ensure they're registered
7
7
  import './providers/Auth0Provider.js';
@@ -16,8 +16,18 @@ import './providers/GoogleProvider.js';
16
16
  */
17
17
  export class AuthProviderFactory extends BaseSingleton<AuthProviderFactory> {
18
18
  private providers: Map<string, IAuthProvider> = new Map();
19
- private issuerCache: Map<string, IAuthProvider> = new Map();
20
- private issuerMultiCache: Map<string, IAuthProvider[]> = new Map();
19
+ /**
20
+ * Cache of resolved issuer provider mappings. Bounded LRU(50) — prior
21
+ * unbounded `Map` was a low-effort DoS vector: a misconfigured/malicious
22
+ * client supplying arbitrary issuer URLs would walk the map up indefinitely.
23
+ * In production there should never be more than a handful of legitimate
24
+ * issuers. See audit R2-C4.
25
+ */
26
+ private issuerCache: MJLruCache<string, IAuthProvider> = new MJLruCache<string, IAuthProvider>({ maxSize: 50 });
27
+ /**
28
+ * Cache of issuer → all matching providers. Same LRU bound as `issuerCache`.
29
+ */
30
+ private issuerMultiCache: MJLruCache<string, IAuthProvider[]> = new MJLruCache<string, IAuthProvider[]>({ maxSize: 50 });
21
31
 
22
32
  public constructor() {
23
33
  super();
@@ -65,10 +75,10 @@ export class AuthProviderFactory extends BaseSingleton<AuthProviderFactory> {
65
75
  }
66
76
 
67
77
  this.providers.set(provider.name, provider);
68
-
78
+
69
79
  // Clear issuer caches when registering new provider
70
- this.issuerCache.clear();
71
- this.issuerMultiCache.clear();
80
+ this.issuerCache.Clear();
81
+ this.issuerMultiCache.Clear();
72
82
 
73
83
  console.log(`Registered auth provider: ${provider.name} with issuer: ${provider.issuer}`);
74
84
  }
@@ -78,15 +88,16 @@ export class AuthProviderFactory extends BaseSingleton<AuthProviderFactory> {
78
88
  */
79
89
  getByIssuer(issuer: string): IAuthProvider | undefined {
80
90
  // Check cache first
81
- if (this.issuerCache.has(issuer)) {
82
- return this.issuerCache.get(issuer);
91
+ const cached = this.issuerCache.Get(issuer);
92
+ if (cached) {
93
+ return cached;
83
94
  }
84
95
 
85
96
  // Search through providers
86
97
  for (const provider of this.providers.values()) {
87
98
  if (provider.matchesIssuer(issuer)) {
88
99
  // Cache for future lookups
89
- this.issuerCache.set(issuer, provider);
100
+ this.issuerCache.Set(issuer, provider);
90
101
  return provider;
91
102
  }
92
103
  }
@@ -103,8 +114,9 @@ export class AuthProviderFactory extends BaseSingleton<AuthProviderFactory> {
103
114
  */
104
115
  getAllByIssuer(issuer: string): IAuthProvider[] {
105
116
  // Check multi-provider cache first
106
- if (this.issuerMultiCache.has(issuer)) {
107
- return this.issuerMultiCache.get(issuer)!;
117
+ const cached = this.issuerMultiCache.Get(issuer);
118
+ if (cached) {
119
+ return cached;
108
120
  }
109
121
 
110
122
  const matches: IAuthProvider[] = [];
@@ -115,7 +127,7 @@ export class AuthProviderFactory extends BaseSingleton<AuthProviderFactory> {
115
127
  }
116
128
 
117
129
  if (matches.length > 0) {
118
- this.issuerMultiCache.set(issuer, matches);
130
+ this.issuerMultiCache.Set(issuer, matches);
119
131
  }
120
132
 
121
133
  return matches;
@@ -147,8 +159,8 @@ export class AuthProviderFactory extends BaseSingleton<AuthProviderFactory> {
147
159
  */
148
160
  clear(): void {
149
161
  this.providers.clear();
150
- this.issuerCache.clear();
151
- this.issuerMultiCache.clear();
162
+ this.issuerCache.Clear();
163
+ this.issuerMultiCache.Clear();
152
164
  }
153
165
 
154
166
  /**
@@ -0,0 +1,105 @@
1
+ /**
2
+ * AuthProviderFactory cache-bound tests (memory-leak fix R2-C4).
3
+ *
4
+ * The pre-fix `issuerCache` and `issuerMultiCache` were unbounded `Map`s keyed
5
+ * by JWT `iss` claims — a misconfigured or malicious client supplying arbitrary
6
+ * issuer URLs would walk the maps up indefinitely. Both are now `MJLruCache`
7
+ * with `maxSize: 50`. These tests verify:
8
+ * - Lookups still hit the cache after the first miss.
9
+ * - LRU eviction kicks in past `maxSize` (no unbounded growth).
10
+ * - `register()` still clears both caches.
11
+ */
12
+
13
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
14
+ import { AuthProviderFactory } from '../AuthProviderFactory';
15
+ import { IAuthProvider } from '../IAuthProvider';
16
+
17
+ function makeProvider(name: string, issuerSubstr: string): IAuthProvider {
18
+ return {
19
+ name,
20
+ issuer: issuerSubstr,
21
+ validateConfig: () => true,
22
+ matchesIssuer: (iss: string) => iss.includes(issuerSubstr),
23
+ getJwksUri: () => 'https://example.com/.well-known/jwks.json',
24
+ getAlgorithm: () => 'RS256',
25
+ getAudience: () => 'aud',
26
+ getRequiredScopes: () => [],
27
+ validateToken: vi.fn(),
28
+ extractUserInfo: vi.fn(),
29
+ } as unknown as IAuthProvider;
30
+ }
31
+
32
+ describe('AuthProviderFactory cache (memory-leak fix R2-C4)', () => {
33
+ let factory: AuthProviderFactory;
34
+
35
+ beforeEach(() => {
36
+ factory = AuthProviderFactory.Instance;
37
+ factory.clear();
38
+ });
39
+
40
+ it('caches getByIssuer results so a hot lookup returns from cache', () => {
41
+ const provider = makeProvider('p1', 'auth0.com/foo');
42
+ factory.register(provider);
43
+
44
+ const matchSpy = vi.spyOn(provider, 'matchesIssuer');
45
+ // First call — cache miss, walks providers
46
+ expect(factory.getByIssuer('https://auth0.com/foo')).toBe(provider);
47
+ expect(matchSpy).toHaveBeenCalledTimes(1);
48
+ // Second call — cache hit, no further matchesIssuer calls
49
+ expect(factory.getByIssuer('https://auth0.com/foo')).toBe(provider);
50
+ expect(matchSpy).toHaveBeenCalledTimes(1);
51
+ });
52
+
53
+ it('caches getAllByIssuer results similarly', () => {
54
+ const p1 = makeProvider('p1', 'shared.example.com');
55
+ const p2 = makeProvider('p2', 'shared.example.com');
56
+ factory.register(p1);
57
+ factory.register(p2);
58
+
59
+ const a = factory.getAllByIssuer('https://shared.example.com');
60
+ const b = factory.getAllByIssuer('https://shared.example.com');
61
+ expect(a).toEqual([p1, p2]);
62
+ expect(b).toBe(a); // cached array, same reference
63
+ });
64
+
65
+ it('does NOT grow without bound when arbitrary issuers are queried', () => {
66
+ const provider = makeProvider('p1', 'auth0.com');
67
+ factory.register(provider);
68
+
69
+ // Walk past the LRU cap with distinct issuer keys (a malicious or
70
+ // misconfigured client could do this). Cache must stay bounded.
71
+ for (let i = 0; i < 200; i++) {
72
+ factory.getByIssuer(`https://auth0.com/tenant-${i}`);
73
+ }
74
+
75
+ // Direct field inspection — verify the LRU enforced its cap.
76
+ const issuerCache = (factory as unknown as { issuerCache: { Size: number; MaxSize: number } }).issuerCache;
77
+ expect(issuerCache.Size).toBeLessThanOrEqual(issuerCache.MaxSize);
78
+ expect(issuerCache.MaxSize).toBe(50);
79
+ });
80
+
81
+ it('clears both caches on register()', () => {
82
+ const provider = makeProvider('p1', 'auth0.com');
83
+ factory.register(provider);
84
+ factory.getByIssuer('https://auth0.com/foo'); // populate cache
85
+
86
+ const issuerCache = (factory as unknown as { issuerCache: { Size: number } }).issuerCache;
87
+ expect(issuerCache.Size).toBe(1);
88
+
89
+ factory.register(makeProvider('p2', 'okta.com'));
90
+ expect(issuerCache.Size).toBe(0);
91
+ });
92
+
93
+ it('clears both caches on clear()', () => {
94
+ factory.register(makeProvider('p1', 'auth0.com'));
95
+ factory.getByIssuer('https://auth0.com/foo');
96
+ factory.getAllByIssuer('https://auth0.com/foo');
97
+
98
+ factory.clear();
99
+ expect(factory.hasProviders()).toBe(false);
100
+ const issuerCache = (factory as unknown as { issuerCache: { Size: number } }).issuerCache;
101
+ const issuerMultiCache = (factory as unknown as { issuerMultiCache: { Size: number } }).issuerMultiCache;
102
+ expect(issuerCache.Size).toBe(0);
103
+ expect(issuerMultiCache.Size).toBe(0);
104
+ });
105
+ });