@axa-fr/oidc-client-service-worker 7.27.3 → 7.27.7

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,7 +1,12 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
 
3
- import { getCurrentDatabasesTokenEndpoint } from '../oidcConfig';
4
- import { Database } from '../types';
3
+ import {
4
+ getCurrentDatabasesTokenEndpoint,
5
+ isAccessTokenDomainRequest,
6
+ isOidcServerRequest,
7
+ shouldBypassNonOidcRequest,
8
+ } from '../oidcConfig';
9
+ import { Database, TrustedDomains } from '../types';
5
10
 
6
11
  const oidcConfigDefaults = {
7
12
  demonstratingProofOfPossessionConfiguration: null,
@@ -18,6 +23,7 @@ const oidcConfigDefaults = {
18
23
  demonstratingProofOfPossessionJwkJson: null,
19
24
  demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent: false,
20
25
  allowMultiTabLogin: true,
26
+ bypassAllNonOidcRequests: false,
21
27
  };
22
28
 
23
29
  const oidcServerConfigDefault = {
@@ -152,3 +158,114 @@ describe('getCurrentDatabasesTokenEndpoint', () => {
152
158
  expect(result).toHaveLength(0);
153
159
  });
154
160
  });
161
+
162
+ describe('shouldBypassNonOidcRequest', () => {
163
+ const database: Database = {
164
+ config1: {
165
+ ...oidcConfigDefaults,
166
+ bypassAllNonOidcRequests: true,
167
+ oidcServerConfiguration: {
168
+ ...oidcServerConfigDefault,
169
+ issuer: 'https://oidc.example.com',
170
+ authorizationEndpoint: 'https://oidc.example.com/connect/authorize',
171
+ tokenEndpoint: 'https://oidc.example.com/connect/token',
172
+ revocationEndpoint: 'https://oidc.example.com/connect/revoke',
173
+ userInfoEndpoint: 'https://oidc.example.com/connect/userinfo',
174
+ },
175
+ },
176
+ };
177
+
178
+ it('should bypass non-OIDC requests when enabled', () => {
179
+ expect(shouldBypassNonOidcRequest(database, 'https://api.example.com/users', null)).toBe(true);
180
+ });
181
+
182
+ it.each([
183
+ 'https://oidc.example.com/.well-known/openid-configuration',
184
+ 'https://oidc.example.com/connect/authorize',
185
+ 'https://oidc.example.com/connect/token',
186
+ 'https://oidc.example.com/connect/revoke',
187
+ 'https://oidc.example.com/connect/userinfo',
188
+ ])('should never bypass OIDC server request %s', url => {
189
+ expect(isOidcServerRequest(database, url)).toBe(true);
190
+ expect(shouldBypassNonOidcRequest(database, url, null)).toBe(false);
191
+ });
192
+
193
+ it('should keep existing behavior when disabled', () => {
194
+ expect(
195
+ shouldBypassNonOidcRequest(
196
+ {
197
+ config1: {
198
+ ...database.config1,
199
+ bypassAllNonOidcRequests: false,
200
+ },
201
+ },
202
+ 'https://api.example.com/users',
203
+ null,
204
+ ),
205
+ ).toBe(false);
206
+ });
207
+
208
+ it('should keep existing behavior until OIDC server configuration is initialized', () => {
209
+ expect(
210
+ shouldBypassNonOidcRequest(
211
+ {
212
+ config1: {
213
+ ...oidcConfigDefaults,
214
+ bypassAllNonOidcRequests: true,
215
+ oidcServerConfiguration: null,
216
+ },
217
+ },
218
+ 'https://api.example.com/users',
219
+ null,
220
+ ),
221
+ ).toBe(false);
222
+ });
223
+
224
+ it('should keep accessTokenDomains requests intercepted when bypass is enabled', () => {
225
+ const trustedDomains: TrustedDomains = {
226
+ config1: {
227
+ accessTokenDomains: ['https://api.example.com'],
228
+ showAccessToken: false,
229
+ },
230
+ };
231
+
232
+ expect(
233
+ isAccessTokenDomainRequest(database, 'https://api.example.com/users', trustedDomains),
234
+ ).toBe(true);
235
+ expect(
236
+ shouldBypassNonOidcRequest(database, 'https://api.example.com/users', trustedDomains),
237
+ ).toBe(false);
238
+ });
239
+
240
+ it('should keep domains fallback requests intercepted when bypass is enabled', () => {
241
+ const trustedDomains: TrustedDomains = {
242
+ config1: {
243
+ domains: ['https://api.example.com'],
244
+ showAccessToken: false,
245
+ },
246
+ };
247
+
248
+ expect(
249
+ shouldBypassNonOidcRequest(database, 'https://api.example.com/users', trustedDomains),
250
+ ).toBe(false);
251
+ });
252
+
253
+ it('should keep existing behavior unless all initialized configurations enable bypass', () => {
254
+ expect(
255
+ shouldBypassNonOidcRequest(
256
+ {
257
+ ...database,
258
+ config2: {
259
+ ...oidcConfigDefaults,
260
+ oidcServerConfiguration: {
261
+ ...oidcServerConfigDefault,
262
+ issuer: 'https://other-oidc.example.com',
263
+ },
264
+ },
265
+ },
266
+ 'https://api.example.com/users',
267
+ null,
268
+ ),
269
+ ).toBe(false);
270
+ });
271
+ });
package/src/oidcConfig.ts CHANGED
@@ -1,5 +1,85 @@
1
- import { Database, OidcConfig } from './types';
2
- import { normalizeUrl } from './utils';
1
+ import { acceptAnyDomainToken } from './constants';
2
+ import { Database, Domain, OidcConfig, OidcServerConfiguration, TrustedDomains } from './types';
3
+ import { getDomains, normalizeUrl } from './utils';
4
+
5
+ const getOidcServerUrls = (oidcServerConfiguration: OidcServerConfiguration): string[] => {
6
+ return [
7
+ oidcServerConfiguration.issuer,
8
+ oidcServerConfiguration.authorizationEndpoint,
9
+ oidcServerConfiguration.tokenEndpoint,
10
+ oidcServerConfiguration.revocationEndpoint,
11
+ oidcServerConfiguration.userInfoEndpoint,
12
+ ]
13
+ .filter(Boolean)
14
+ .map(normalizeUrl);
15
+ };
16
+
17
+ const isOidcServerRequest = (database: Database, normalizedUrl: string): boolean => {
18
+ return Object.values(database).some(config => {
19
+ const { oidcServerConfiguration } = config || {};
20
+ if (!oidcServerConfiguration) {
21
+ return false;
22
+ }
23
+
24
+ return getOidcServerUrls(oidcServerConfiguration).some(oidcUrl =>
25
+ normalizedUrl.startsWith(oidcUrl),
26
+ );
27
+ });
28
+ };
29
+
30
+ const isDomainMatchingUrl = (domain: Domain, normalizedUrl: string): boolean => {
31
+ if (typeof domain === 'string') {
32
+ domain = new RegExp(`^${domain}`);
33
+ }
34
+
35
+ return domain.test?.(normalizedUrl) ?? false;
36
+ };
37
+
38
+ const isAccessTokenDomainRequest = (
39
+ database: Database,
40
+ normalizedUrl: string,
41
+ trustedDomains: TrustedDomains,
42
+ ): boolean => {
43
+ return Object.entries(database).some(([key, currentDatabase]) => {
44
+ if (!currentDatabase.oidcServerConfiguration) {
45
+ return false;
46
+ }
47
+
48
+ const trustedDomain = trustedDomains?.[key.split('#')[0]] ?? [];
49
+ const domains = getDomains(trustedDomain, 'accessToken');
50
+
51
+ if (domains.some(domain => domain === acceptAnyDomainToken)) {
52
+ return true;
53
+ }
54
+
55
+ return domains.some(domain => isDomainMatchingUrl(domain, normalizedUrl));
56
+ });
57
+ };
58
+
59
+ const shouldBypassNonOidcRequest = (
60
+ database: Database,
61
+ normalizedUrl: string,
62
+ trustedDomains: TrustedDomains,
63
+ ): boolean => {
64
+ const configurations = Object.values(database);
65
+
66
+ if (configurations.length === 0) {
67
+ return false;
68
+ }
69
+
70
+ if (!configurations.every(config => config.bypassAllNonOidcRequests)) {
71
+ return false;
72
+ }
73
+
74
+ if (!configurations.every(config => config.oidcServerConfiguration != null)) {
75
+ return false;
76
+ }
77
+
78
+ return (
79
+ !isOidcServerRequest(database, normalizedUrl) &&
80
+ !isAccessTokenDomainRequest(database, normalizedUrl, trustedDomains)
81
+ );
82
+ };
3
83
 
4
84
  const getMatchingOidcConfigurations = (database: Database, url: string): OidcConfig[] => {
5
85
  return Object.values(database).filter(config => {
@@ -14,4 +94,9 @@ const getMatchingOidcConfigurations = (database: Database, url: string): OidcCon
14
94
  });
15
95
  };
16
96
 
17
- export { getMatchingOidcConfigurations as getCurrentDatabasesTokenEndpoint };
97
+ export {
98
+ getMatchingOidcConfigurations as getCurrentDatabasesTokenEndpoint,
99
+ isAccessTokenDomainRequest,
100
+ isOidcServerRequest,
101
+ shouldBypassNonOidcRequest,
102
+ };
package/src/types.ts CHANGED
@@ -9,6 +9,7 @@ export type DomainDetails = {
9
9
  demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent?: boolean;
10
10
  demonstratingProofOfPossessionConfiguration?: DemonstratingProofOfPossessionConfiguration;
11
11
  allowMultiTabLogin?: boolean;
12
+ bypassAllNonOidcRequests?: boolean;
12
13
  };
13
14
 
14
15
  export interface DemonstratingProofOfPossessionConfiguration {
@@ -43,11 +44,6 @@ export type OidcConfiguration = {
43
44
  demonstrating_proof_of_possession: boolean;
44
45
  };
45
46
 
46
- // Uncertain why the Headers interface in lib.webworker.d.ts does not have a keys() function, so extending
47
- export interface FetchHeaders extends Headers {
48
- keys(): string[];
49
- }
50
-
51
47
  export type Status =
52
48
  | 'LOGGED'
53
49
  | 'LOGGED_IN'
@@ -117,6 +113,7 @@ export type OidcConfig = {
117
113
  demonstratingProofOfPossessionJwkJson: string | null;
118
114
  demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent: boolean;
119
115
  allowMultiTabLogin: boolean;
116
+ bypassAllNonOidcRequests: boolean;
120
117
  };
121
118
 
122
119
  export type IdTokenPayload = {
@@ -57,6 +57,7 @@ describe('domains', () => {
57
57
  demonstratingProofOfPossessionConfiguration: null,
58
58
  demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent: false,
59
59
  allowMultiTabLogin: true,
60
+ bypassAllNonOidcRequests: false,
60
61
  },
61
62
  };
62
63
  });
@@ -127,6 +127,7 @@ class OidcConfigBuilder {
127
127
  demonstratingProofOfPossessionConfiguration: null,
128
128
  demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent: false,
129
129
  allowMultiTabLogin: true,
130
+ bypassAllNonOidcRequests: false,
130
131
  };
131
132
 
132
133
  public withTestingDefault(): OidcConfigBuilder {
@@ -1,8 +1,6 @@
1
- import { FetchHeaders } from '../types';
2
-
3
1
  function serializeHeaders(headers: Headers) {
4
2
  const headersObj: Record<string, string> = {};
5
- for (const key of (headers as FetchHeaders).keys()) {
3
+ for (const key of headers.keys()) {
6
4
  if (headers.has(key)) {
7
5
  headersObj[key] = headers.get(key) as string;
8
6
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export default '7.27.3';
1
+ export default '7.27.7';