@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.
- package/dist/OidcServiceWorker.js +762 -901
- package/dist/OidcServiceWorker.js.map +1 -1
- package/dist/src/oidcConfig.d.ts +5 -2
- package/dist/src/oidcConfig.d.ts.map +1 -1
- package/dist/src/types.d.ts +2 -3
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/__tests__/testHelper.d.ts.map +1 -1
- package/dist/src/utils/serializeHeaders.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/package.json +8 -8
- package/src/OidcServiceWorker.ts +15 -2
- package/src/__tests__/oidcConfig.spec.ts +119 -2
- package/src/oidcConfig.ts +88 -3
- package/src/types.ts +2 -5
- package/src/utils/__tests__/domains.spec.ts +1 -0
- package/src/utils/__tests__/testHelper.ts +1 -0
- package/src/utils/serializeHeaders.ts +1 -3
- package/src/version.ts +1 -1
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
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 {
|
|
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 = {
|
|
@@ -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
|
|
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.
|
|
1
|
+
export default '7.27.7';
|