@strivacity/sdk-core 4.0.0-beta.0 → 4.0.0-beta.2
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 +228 -86
- package/dist/assets/oidc.cjs +1 -1
- package/dist/assets/oidc.cjs.map +1 -1
- package/dist/assets/oidc.mjs +1 -1
- package/dist/assets/oidc.mjs.map +1 -1
- package/dist/assets/server.cjs +1 -1
- package/dist/assets/server.cjs.map +1 -1
- package/dist/assets/server.mjs +1 -1
- package/dist/assets/server.mjs.map +1 -1
- package/dist/flows/base.cjs +1 -1
- package/dist/flows/base.cjs.map +1 -1
- package/dist/flows/base.d.ts +4 -2
- package/dist/flows/base.mjs +1 -1
- package/dist/flows/base.mjs.map +1 -1
- package/dist/flows/popup.cjs +1 -1
- package/dist/flows/popup.cjs.map +1 -1
- package/dist/flows/popup.mjs +1 -1
- package/dist/flows/popup.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/server/base.cjs +1 -1
- package/dist/server/base.cjs.map +1 -1
- package/dist/server/base.d.ts +2 -4
- package/dist/server/base.mjs +1 -1
- package/dist/server/base.mjs.map +1 -1
- package/dist/server/index.cjs +1 -1
- package/dist/server/index.mjs +1 -1
- package/dist/storages/cache.cjs.map +1 -1
- package/dist/storages/cache.d.ts +1 -1
- package/dist/storages/cache.mjs.map +1 -1
- package/dist/storages/index.cjs +1 -1
- package/dist/storages/index.mjs +1 -1
- package/dist/storages/server.cjs +1 -1
- package/dist/storages/server.d.ts +29 -1
- package/dist/storages/server.mjs +1 -1
- package/dist/storages/worker.cjs.map +1 -1
- package/dist/storages/worker.d.ts +1 -1
- package/dist/storages/worker.mjs.map +1 -1
- package/dist/types/native.d.ts +67 -2
- package/dist/types/oidc.cjs.map +1 -1
- package/dist/types/oidc.d.ts +47 -6
- package/dist/types/oidc.mjs.map +1 -1
- package/dist/types/popup.d.ts +1 -1
- package/dist/types/server.d.ts +21 -9
- package/dist/utils/index.cjs +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.mjs +1 -1
- package/dist/utils/native-login.cjs +2 -0
- package/dist/utils/native-login.cjs.map +1 -0
- package/dist/utils/native-login.d.ts +13 -0
- package/dist/utils/native-login.mjs +2 -0
- package/dist/utils/native-login.mjs.map +1 -0
- package/dist/utils/oidc.d.ts +3 -4
- package/dist/utils/path.cjs +2 -0
- package/dist/utils/path.cjs.map +1 -0
- package/dist/utils/path.d.ts +16 -0
- package/dist/utils/path.mjs +2 -0
- package/dist/utils/path.mjs.map +1 -0
- package/dist/utils/server.d.ts +5 -4
- package/dist/utils/session.cjs +1 -1
- package/dist/utils/session.cjs.map +1 -1
- package/dist/utils/session.d.ts +10 -1
- package/dist/utils/session.mjs +1 -1
- package/dist/utils/session.mjs.map +1 -1
- package/package.json +1 -1
- package/testing/tests/flows/base.spec.ts +0 -1030
- package/testing/tests/flows/embedded.spec.ts +0 -443
- package/testing/tests/flows/index.spec.ts +0 -8
- package/testing/tests/flows/native.spec.ts +0 -487
- package/testing/tests/flows/popup.spec.ts +0 -400
- package/testing/tests/flows/redirect.spec.ts +0 -369
- package/testing/tests/handlers/embedded.spec.ts +0 -199
- package/testing/tests/handlers/index.spec.ts +0 -8
- package/testing/tests/handlers/native.spec.ts +0 -283
- package/testing/tests/handlers/popup.spec.ts +0 -206
- package/testing/tests/handlers/redirect.spec.ts +0 -70
- package/testing/tests/index.spec.ts +0 -78
- package/testing/tests/server/base.spec.ts +0 -846
- package/testing/tests/server/index.spec.ts +0 -15
- package/testing/tests/storages/cache.spec.ts +0 -107
- package/testing/tests/storages/index.spec.ts +0 -22
- package/testing/tests/storages/indexeddb.spec.ts +0 -113
- package/testing/tests/storages/local.spec.ts +0 -46
- package/testing/tests/storages/memory.spec.ts +0 -51
- package/testing/tests/storages/server.spec.ts +0 -435
- package/testing/tests/storages/session.spec.ts +0 -46
- package/testing/tests/storages/worker.spec.ts +0 -106
- package/testing/tests/utils/common.spec.ts +0 -77
- package/testing/tests/utils/credentials.spec.ts +0 -195
- package/testing/tests/utils/dom.spec.ts +0 -37
- package/testing/tests/utils/index.spec.ts +0 -71
- package/testing/tests/utils/logging.spec.ts +0 -82
- package/testing/tests/utils/oidc.spec.ts +0 -744
- package/testing/tests/utils/server.spec.ts +0 -235
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, test, expect, vi } from 'vitest';
|
|
2
|
-
import { http, HttpResponse } from 'msw';
|
|
3
|
-
import { createOptions, getMockStorage } from '@strivacity/testing/mocks/sdk';
|
|
4
|
-
import { worker } from '@strivacity/testing/mocks/msw';
|
|
5
|
-
import { createEmbeddedFlow } from '../../../src/flows/embedded';
|
|
6
|
-
import { NetworkError, OidcError, ProtocolError, ServerError } from '../../../src/utils';
|
|
7
|
-
|
|
8
|
-
describe('createEmbeddedFlow', () => {
|
|
9
|
-
let options: ReturnType<typeof createOptions>;
|
|
10
|
-
let flow: ReturnType<typeof createEmbeddedFlow>;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
globalThis.location.href = 'https://brandtegrity.io';
|
|
14
|
-
globalThis.sty = {};
|
|
15
|
-
|
|
16
|
-
options = createOptions();
|
|
17
|
-
flow = createEmbeddedFlow(options);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
describe('init', () => {
|
|
21
|
-
test('lazyLoad: true', async () => {
|
|
22
|
-
flow = createEmbeddedFlow(createOptions({ lazyLoad: true }));
|
|
23
|
-
|
|
24
|
-
// We need to wait for the next tick to ensure that any asynchronous initialization logic has a chance to run before we check the initialized state.
|
|
25
|
-
await Promise.resolve();
|
|
26
|
-
|
|
27
|
-
expect(flow.initialized).toBe(false);
|
|
28
|
-
|
|
29
|
-
await flow.init();
|
|
30
|
-
|
|
31
|
-
expect(flow.initialized).toBe(true);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test('lazyLoad: false', async () => {
|
|
35
|
-
flow = createEmbeddedFlow(createOptions({ lazyLoad: false }));
|
|
36
|
-
|
|
37
|
-
await vi.waitFor(() => expect(flow.initialized).toBe(true));
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('startSession', () => {
|
|
42
|
-
test('should collect the session identifiers when the response contains a session_id', async () => {
|
|
43
|
-
worker.use(
|
|
44
|
-
http.get('https://brandtegrity.io/oauth2/auth', () =>
|
|
45
|
-
HttpResponse.text('https://brandtegrity.io/login?session_id=abc123&short_app_id=app&language=hu-HU'),
|
|
46
|
-
),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
await flow.startSession();
|
|
50
|
-
|
|
51
|
-
expect(flow.sessionId).toBe('abc123');
|
|
52
|
-
expect(flow.shortAppId).toBe('app');
|
|
53
|
-
expect(flow.language).toBe('hu-HU');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('should exchange the code for tokens', () => {
|
|
57
|
-
test('w/o serverSessionUri', async () => {
|
|
58
|
-
worker.use(
|
|
59
|
-
http.get('https://brandtegrity.io/oauth2/auth', ({ request }) => {
|
|
60
|
-
const state = new URL(request.url).searchParams.get('state');
|
|
61
|
-
|
|
62
|
-
return HttpResponse.text(`https://brandtegrity.io/callback?code=abc123&state=${state}`);
|
|
63
|
-
}),
|
|
64
|
-
http.post('https://brandtegrity.io/oauth2/token', () => HttpResponse.json({ access_token: 'access-token', token_type: 'Bearer', expires_in: 3600 })),
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
await flow.startSession();
|
|
68
|
-
|
|
69
|
-
expect(globalThis.location.href).toBe('https://brandtegrity.io/');
|
|
70
|
-
expect(flow.accessToken).toBe('access-token');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('w/ serverSessionUri', async () => {
|
|
74
|
-
worker.use(http.get('https://app.example.com/login', () => HttpResponse.text('https://brandtegrity.io/callback?code=abc123')));
|
|
75
|
-
|
|
76
|
-
options = createOptions({ serverSessionUri: 'https://app.example.com/login' });
|
|
77
|
-
flow = createEmbeddedFlow(options);
|
|
78
|
-
|
|
79
|
-
await flow.startSession();
|
|
80
|
-
|
|
81
|
-
expect(globalThis.location.href).toBe('https://brandtegrity.io/callback?code=abc123');
|
|
82
|
-
expect(flow.accessToken).toBeNull();
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
describe('finalizeSession', () => {
|
|
88
|
-
test('should throw error when the session ID is missing', async () => {
|
|
89
|
-
await expect(flow.finalizeSession('https://brandtegrity.io/finalize')).rejects.toThrow('Session ID is missing. Cannot finalize login session.');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
describe('should exchange the code for tokens', () => {
|
|
93
|
-
test('w/o serverSessionUri', async () => {
|
|
94
|
-
const stateData = await getMockStorage(options.stateStorage).generateState();
|
|
95
|
-
|
|
96
|
-
worker.use(
|
|
97
|
-
http.get('https://brandtegrity.io/finalize', () => HttpResponse.text(`https://brandtegrity.io/callback?code=abc123&state=${stateData.id}`)),
|
|
98
|
-
http.post('https://brandtegrity.io/oauth2/token', () => HttpResponse.json({ access_token: 'access-token', token_type: 'Bearer', expires_in: 3600 })),
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
flow.sessionId = 'abc123';
|
|
102
|
-
|
|
103
|
-
await flow.finalizeSession('https://brandtegrity.io/finalize');
|
|
104
|
-
|
|
105
|
-
expect(flow.accessToken).toBe('access-token');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test('w/ serverSessionUri', async () => {
|
|
109
|
-
worker.use(http.get('https://brandtegrity.io/finalize', () => HttpResponse.text('https://brandtegrity.io/callback?code=abc123')));
|
|
110
|
-
|
|
111
|
-
options = createOptions({ serverSessionUri: 'https://app.example.com/login' });
|
|
112
|
-
flow = createEmbeddedFlow(options);
|
|
113
|
-
|
|
114
|
-
flow.sessionId = 'abc123';
|
|
115
|
-
|
|
116
|
-
await flow.finalizeSession('https://brandtegrity.io/finalize');
|
|
117
|
-
|
|
118
|
-
expect(globalThis.location.href).toBe('https://brandtegrity.io/callback?code=abc123');
|
|
119
|
-
expect(flow.accessToken).toBeNull();
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test('should throw a ProtocolError when the redirect URI in the response is invalid', async () => {
|
|
124
|
-
worker.use(http.get('https://brandtegrity.io/finalize', () => HttpResponse.text('not-a-valid-url')));
|
|
125
|
-
|
|
126
|
-
flow.sessionId = 'abc123';
|
|
127
|
-
|
|
128
|
-
await expect(flow.finalizeSession('https://brandtegrity.io/finalize')).rejects.toThrow(new ProtocolError('Invalid redirect URI'));
|
|
129
|
-
expect(flow.options.logging?.error).toHaveBeenCalledWith('Finalize login session error', expect.any(ProtocolError));
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
describe('login', () => {
|
|
134
|
-
test('backward compatibility', async () => {
|
|
135
|
-
worker.use(
|
|
136
|
-
http.get('https://brandtegrity.io/oauth2/auth', () =>
|
|
137
|
-
HttpResponse.text('https://brandtegrity.io/login?session_id=xyz789&short_app_id=app2&language=en-US'),
|
|
138
|
-
),
|
|
139
|
-
http.get('https://brandtegrity.io/finalize', () => HttpResponse.text(`https://brandtegrity.io/callback?code=abc123&state=${stateData.id}`)),
|
|
140
|
-
http.post('https://brandtegrity.io/oauth2/token', () => HttpResponse.json({ access_token: 'access-token', token_type: 'Bearer', expires_in: 3600 })),
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
const loginFlow = flow.login();
|
|
144
|
-
|
|
145
|
-
expect(loginFlow.sessionId).toBeNull();
|
|
146
|
-
expect(loginFlow.shortAppId).toBeNull();
|
|
147
|
-
expect(loginFlow.language).toBe('en-US');
|
|
148
|
-
|
|
149
|
-
loginFlow.sessionId = 'abc123';
|
|
150
|
-
loginFlow.shortAppId = 'app';
|
|
151
|
-
loginFlow.language = 'hu-HU';
|
|
152
|
-
|
|
153
|
-
expect(flow.sessionId).toBe('abc123');
|
|
154
|
-
expect(flow.shortAppId).toBe('app');
|
|
155
|
-
expect(flow.language).toBe('hu-HU');
|
|
156
|
-
|
|
157
|
-
await loginFlow.startSession();
|
|
158
|
-
|
|
159
|
-
expect(flow.sessionId).toBe('xyz789');
|
|
160
|
-
expect(flow.shortAppId).toBe('app2');
|
|
161
|
-
expect(flow.language).toBe('en-US');
|
|
162
|
-
|
|
163
|
-
const stateData = await getMockStorage(options.stateStorage).generateState();
|
|
164
|
-
|
|
165
|
-
await loginFlow.finalizeSession('https://brandtegrity.io/finalize');
|
|
166
|
-
|
|
167
|
-
expect(flow.accessToken).toBe('access-token');
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
test('should forward login params to startSession', async () => {
|
|
171
|
-
let capturedUrl: URL | undefined;
|
|
172
|
-
|
|
173
|
-
worker.use(
|
|
174
|
-
http.get('https://brandtegrity.io/oauth2/auth', ({ request }) => {
|
|
175
|
-
capturedUrl = new URL(request.url);
|
|
176
|
-
|
|
177
|
-
return HttpResponse.text('https://brandtegrity.io/login?session_id=abc123&short_app_id=app&language=en-US');
|
|
178
|
-
}),
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
await flow.login({ prompt: 'login', loginHint: 'test@example.com' }).startSession();
|
|
182
|
-
|
|
183
|
-
expect(capturedUrl?.searchParams.get('prompt')).toBe('login');
|
|
184
|
-
expect(capturedUrl?.searchParams.get('login_hint')).toBe('test@example.com');
|
|
185
|
-
expect(flow.sessionId).toBe('abc123');
|
|
186
|
-
expect(flow.shortAppId).toBe('app');
|
|
187
|
-
expect(flow.language).toBe('en-US');
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
describe('register', () => {
|
|
192
|
-
test('should force the "create" prompt', async () => {
|
|
193
|
-
let capturedUrl: URL | undefined;
|
|
194
|
-
|
|
195
|
-
worker.use(
|
|
196
|
-
http.get('https://brandtegrity.io/oauth2/auth', ({ request }) => {
|
|
197
|
-
capturedUrl = new URL(request.url);
|
|
198
|
-
|
|
199
|
-
return HttpResponse.text('https://brandtegrity.io/login?session_id=abc123&short_app_id=app&language=en-US');
|
|
200
|
-
}),
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
await flow.register().startSession();
|
|
204
|
-
|
|
205
|
-
expect(capturedUrl?.searchParams.get('prompt')).toBe('create');
|
|
206
|
-
expect(flow.sessionId).toBe('abc123');
|
|
207
|
-
expect(flow.shortAppId).toBe('app');
|
|
208
|
-
expect(flow.language).toBe('en-US');
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
describe('entry', () => {
|
|
213
|
-
test('should fetch the entry data preserving query parameters', async () => {
|
|
214
|
-
let capturedUrl: URL | undefined;
|
|
215
|
-
|
|
216
|
-
worker.use(
|
|
217
|
-
http.get('https://brandtegrity.io/provider/flow/entry', ({ request }) => {
|
|
218
|
-
capturedUrl = new URL(request.url);
|
|
219
|
-
|
|
220
|
-
return HttpResponse.text('https://brandtegrity.io/entry?session_id=abc123&short_app_id=app&language=hu-HU');
|
|
221
|
-
}),
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
const data = await flow.entry('https://brandtegrity.io/entry?challenge=abc123&foo=bar');
|
|
225
|
-
|
|
226
|
-
expect(capturedUrl?.searchParams.get('challenge')).toBe('abc123');
|
|
227
|
-
expect(capturedUrl?.searchParams.get('foo')).toBe('bar');
|
|
228
|
-
expect(capturedUrl?.searchParams.get('sdk')).toBe('web-embedded');
|
|
229
|
-
expect(capturedUrl?.searchParams.get('client_id')).toBe('client-id');
|
|
230
|
-
expect(capturedUrl?.searchParams.get('redirect_uri')).toBe('https://brandtegrity.io/callback');
|
|
231
|
-
expect(data).toEqual({ session_id: 'abc123', short_app_id: 'app', language: 'hu-HU' });
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
test('should default to the current window location when no url is provided', async () => {
|
|
235
|
-
window.location.search = '?challenge=abc123&foo=bar';
|
|
236
|
-
|
|
237
|
-
let capturedUrl: URL | undefined;
|
|
238
|
-
|
|
239
|
-
worker.use(
|
|
240
|
-
http.get('https://brandtegrity.io/provider/flow/entry', ({ request }) => {
|
|
241
|
-
capturedUrl = new URL(request.url);
|
|
242
|
-
|
|
243
|
-
return HttpResponse.text('https://brandtegrity.io/entry?session_id=abc123&short_app_id=app&language=hu-HU');
|
|
244
|
-
}),
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
const data = await flow.entry();
|
|
248
|
-
|
|
249
|
-
expect(capturedUrl?.searchParams.get('challenge')).toBe('abc123');
|
|
250
|
-
expect(capturedUrl?.searchParams.get('foo')).toBe('bar');
|
|
251
|
-
expect(data.session_id).toBe('abc123');
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
describe('should reject with an error when the HTTP request fails', () => {
|
|
255
|
-
test('HTTP 400', async () => {
|
|
256
|
-
worker.use(http.get('https://brandtegrity.io/provider/flow/entry', () => new HttpResponse('{}', { status: 400 })));
|
|
257
|
-
|
|
258
|
-
await expect(flow.entry('https://brandtegrity.io/entry?challenge=abc123')).rejects.toThrow(new ProtocolError('Entry request failed'));
|
|
259
|
-
expect(flow.options.logging?.error).toHaveBeenCalledWith('Entry request failed', expect.any(ProtocolError));
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
test('HTTP 400 with an OIDC error body', async () => {
|
|
263
|
-
worker.use(
|
|
264
|
-
http.get('https://brandtegrity.io/provider/flow/entry', () =>
|
|
265
|
-
HttpResponse.json({ error: 'access_denied', error_description: 'User denied access' }, { status: 400 }),
|
|
266
|
-
),
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
await expect(flow.entry('https://brandtegrity.io/entry?challenge=abc123')).rejects.toThrow(new OidcError('access_denied', 'User denied access'));
|
|
270
|
-
expect(flow.options.logging?.error).toHaveBeenCalledWith('Entry request failed', expect.any(OidcError));
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
test('HTTP 400 with an errorKey body', async () => {
|
|
274
|
-
worker.use(http.get('https://brandtegrity.io/provider/flow/entry', () => HttpResponse.json({ errorKey: 'invalid_request' }, { status: 400 })));
|
|
275
|
-
|
|
276
|
-
await expect(flow.entry('https://brandtegrity.io/entry?challenge=abc123')).rejects.toThrow(new ProtocolError('invalid_request'));
|
|
277
|
-
expect(flow.options.logging?.error).toHaveBeenCalledWith('Entry request failed', expect.any(ProtocolError));
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
test('HTTP 429', async () => {
|
|
281
|
-
worker.use(http.get('https://brandtegrity.io/provider/flow/entry', () => new HttpResponse(null, { status: 429 })));
|
|
282
|
-
|
|
283
|
-
await expect(flow.entry('https://brandtegrity.io/entry?challenge=abc123')).rejects.toThrow(new ServerError('Entry request failed', 429));
|
|
284
|
-
expect(flow.options.logging?.error).toHaveBeenCalledWith('Entry request failed', expect.any(ServerError));
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
test('HTTP 500', async () => {
|
|
288
|
-
worker.use(http.get('https://brandtegrity.io/provider/flow/entry', () => new HttpResponse(null, { status: 500 })));
|
|
289
|
-
|
|
290
|
-
await expect(flow.entry('https://brandtegrity.io/entry?challenge=abc123')).rejects.toThrow(new ServerError('Entry request failed', 500));
|
|
291
|
-
expect(flow.options.logging?.error).toHaveBeenCalledWith('Entry request failed', expect.any(ServerError));
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
test('HTTP 503', async () => {
|
|
295
|
-
worker.use(http.get('https://brandtegrity.io/provider/flow/entry', () => HttpResponse.error()));
|
|
296
|
-
|
|
297
|
-
await expect(flow.entry('https://brandtegrity.io/entry?challenge=abc123')).rejects.toThrow(
|
|
298
|
-
new NetworkError('Network request failed: https://brandtegrity.io/provider/flow/entry'),
|
|
299
|
-
);
|
|
300
|
-
expect(flow.options.logging?.error).toHaveBeenCalledWith('Entry request failed', expect.any(NetworkError));
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
test('should throw a ProtocolError when "session_id" is missing from the response', async () => {
|
|
304
|
-
worker.use(http.get('https://brandtegrity.io/provider/flow/entry', () => HttpResponse.text('https://brandtegrity.io/entry?short_app_id=app')));
|
|
305
|
-
|
|
306
|
-
await expect(flow.entry('https://brandtegrity.io/entry?challenge=abc123')).rejects.toThrow(
|
|
307
|
-
new ProtocolError('"session_id" is missing from the response'),
|
|
308
|
-
);
|
|
309
|
-
expect(flow.options.logging?.error).toHaveBeenCalledWith('Entry request failed', expect.any(ProtocolError));
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
test('should fall back to the response URL when the response body is not a valid URL', async () => {
|
|
314
|
-
worker.use(
|
|
315
|
-
http.get('https://brandtegrity.io/provider/flow/entry', () =>
|
|
316
|
-
HttpResponse.redirect('https://brandtegrity.io/entry-redirected?session_id=abc123&short_app_id=app&language=hu-HU'),
|
|
317
|
-
),
|
|
318
|
-
http.get('https://brandtegrity.io/entry-redirected', () => HttpResponse.text('<html>not a url</html>')),
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
const data = await flow.entry('https://brandtegrity.io/entry?challenge=abc123');
|
|
322
|
-
|
|
323
|
-
expect(data).toEqual({ session_id: 'abc123', short_app_id: 'app', language: 'hu-HU' });
|
|
324
|
-
});
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
test('flow state accessors', async () => {
|
|
328
|
-
options = createOptions({ lazyLoad: true });
|
|
329
|
-
flow = createEmbeddedFlow(options);
|
|
330
|
-
|
|
331
|
-
expect(flow.options).toBe(options);
|
|
332
|
-
expect(flow.initialized).toBeFalsy();
|
|
333
|
-
expect(flow.storage).toBe(options.storage);
|
|
334
|
-
expect(flow.httpClient).toBe(options.httpClient);
|
|
335
|
-
expect(flow.logging).toBe(options.logging);
|
|
336
|
-
expect(flow.sessionId).toBeNull();
|
|
337
|
-
expect(flow.shortAppId).toBeNull();
|
|
338
|
-
expect(flow.language).toBe('en-US');
|
|
339
|
-
await expect(flow.isAuthenticated).resolves.toBeFalsy();
|
|
340
|
-
expect(flow.isAuthenticatedSync).toBeFalsy();
|
|
341
|
-
expect(flow.accessToken).toBeNull();
|
|
342
|
-
expect(flow.accessTokenExpired).toBeTruthy();
|
|
343
|
-
expect(flow.accessTokenExpirationDate).toBeNull();
|
|
344
|
-
expect(flow.refreshToken).toBeNull();
|
|
345
|
-
expect(flow.idToken).toBeNull();
|
|
346
|
-
expect(flow.idTokenClaims).toBeNull();
|
|
347
|
-
|
|
348
|
-
await flow.init();
|
|
349
|
-
|
|
350
|
-
flow.sessionId = 'abc123';
|
|
351
|
-
flow.shortAppId = 'app';
|
|
352
|
-
flow.language = 'hu-HU';
|
|
353
|
-
flow.session = getMockStorage(options.storage).generateSession();
|
|
354
|
-
|
|
355
|
-
expect(flow.initialized).toBeTruthy();
|
|
356
|
-
expect(flow.sessionId).toBe('abc123');
|
|
357
|
-
expect(flow.shortAppId).toBe('app');
|
|
358
|
-
expect(flow.language).toBe('hu-HU');
|
|
359
|
-
await expect(flow.isAuthenticated).resolves.toBeTruthy();
|
|
360
|
-
expect(flow.isAuthenticatedSync).toBeTruthy();
|
|
361
|
-
expect(flow.accessToken).toBe(flow.session.access_token);
|
|
362
|
-
expect(flow.accessTokenExpired).toBeFalsy();
|
|
363
|
-
expect(flow.accessTokenExpirationDate).toBe(flow.session.expires_at);
|
|
364
|
-
expect(flow.refreshToken).toBe(flow.session.refresh_token);
|
|
365
|
-
expect(flow.idToken).toBe(flow.session.id_token);
|
|
366
|
-
expect(flow.idTokenClaims).toBe(flow.session?.claims);
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
describe('globalThis.sty.oidcService', () => {
|
|
370
|
-
test('should publish the flow correctly', () => {
|
|
371
|
-
expect(globalThis.sty.oidcService).toBeDefined();
|
|
372
|
-
expect(globalThis.sty.oidcService?.sessionId).toBe(flow.sessionId);
|
|
373
|
-
|
|
374
|
-
flow.sessionId = 'abc123';
|
|
375
|
-
|
|
376
|
-
expect(globalThis.sty.oidcService?.sessionId).toBe('abc123');
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
test('should keep the existing value instead of republishing it when creating another flow', () => {
|
|
380
|
-
const value = globalThis.sty.oidcService;
|
|
381
|
-
|
|
382
|
-
createEmbeddedFlow(createOptions());
|
|
383
|
-
|
|
384
|
-
expect(globalThis.sty.oidcService).toBe(value);
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
test('should forward to the most recently created flow while keeping the same proxy identity', () => {
|
|
388
|
-
const value = globalThis.sty.oidcService;
|
|
389
|
-
const newFlow = createEmbeddedFlow(createOptions());
|
|
390
|
-
newFlow.sessionId = 'new-flow-session';
|
|
391
|
-
|
|
392
|
-
expect(globalThis.sty.oidcService).toBe(value);
|
|
393
|
-
expect(globalThis.sty.oidcService?.sessionId).toBe('new-flow-session');
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
test('should expose oidcService as a non-writable, non-configurable, enumerable property', () => {
|
|
397
|
-
const descriptor = Object.getOwnPropertyDescriptor(globalThis.sty, 'oidcService');
|
|
398
|
-
|
|
399
|
-
expect(descriptor?.writable).toBe(false);
|
|
400
|
-
expect(descriptor?.configurable).toBe(false);
|
|
401
|
-
expect(descriptor?.enumerable).toBe(true);
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
test('should prevent replacing oidcService on globalThis.sty', () => {
|
|
405
|
-
expect(() => {
|
|
406
|
-
globalThis.sty.oidcService = undefined;
|
|
407
|
-
}).toThrow(TypeError);
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
test('should support the "in" operator via the has trap', () => {
|
|
411
|
-
expect('sessionId' in globalThis.sty.oidcService!).toBe(true);
|
|
412
|
-
expect('notAProperty' in globalThis.sty.oidcService!).toBe(false);
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
test('should list the active flow keys via the ownKeys trap', () => {
|
|
416
|
-
expect(Object.keys(globalThis.sty.oidcService!)).toEqual(expect.arrayContaining(['sessionId', 'shortAppId', 'language']));
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
test('should prevent setting properties through the proxy', () => {
|
|
420
|
-
expect(() => {
|
|
421
|
-
(globalThis.sty.oidcService as unknown as Record<string, unknown>).sessionId = 'hacked';
|
|
422
|
-
}).toThrow(TypeError);
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
test('should prevent deleting properties through the proxy', () => {
|
|
426
|
-
expect(() => {
|
|
427
|
-
delete (globalThis.sty.oidcService as unknown as Record<string, unknown>).sessionId;
|
|
428
|
-
}).toThrow(TypeError);
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
test('should prevent redefining properties through the proxy', () => {
|
|
432
|
-
expect(() => {
|
|
433
|
-
Object.defineProperty(globalThis.sty.oidcService!, 'sessionId', { value: 'hacked' });
|
|
434
|
-
}).toThrow(TypeError);
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
test('should prevent changing the prototype of the proxy', () => {
|
|
438
|
-
expect(() => {
|
|
439
|
-
Object.setPrototypeOf(globalThis.sty.oidcService!, {});
|
|
440
|
-
}).toThrow(TypeError);
|
|
441
|
-
});
|
|
442
|
-
});
|
|
443
|
-
});
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { test, expect } from 'vitest';
|
|
2
|
-
import * as index from '../../../src/flows';
|
|
3
|
-
|
|
4
|
-
test('should export the correct things', () => {
|
|
5
|
-
const expectedExports = ['createBaseFlow', 'createEmbeddedFlow', 'createNativeFlow', 'createPopupFlow', 'createRedirectFlow'];
|
|
6
|
-
|
|
7
|
-
expect(Object.keys(index)).toEqual(expectedExports);
|
|
8
|
-
});
|