@strivacity/sdk-core 3.0.2 → 4.0.0-beta.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.
Files changed (328) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +1873 -340
  3. package/dist/assets/oidc.cjs +2 -0
  4. package/dist/assets/oidc.cjs.map +1 -0
  5. package/dist/assets/oidc.mjs +2 -0
  6. package/dist/assets/oidc.mjs.map +1 -0
  7. package/dist/assets/server.cjs +2 -0
  8. package/dist/assets/server.cjs.map +1 -0
  9. package/dist/assets/server.mjs +2 -0
  10. package/dist/assets/server.mjs.map +1 -0
  11. package/dist/flows/base.cjs +2 -0
  12. package/dist/flows/base.cjs.map +1 -0
  13. package/dist/flows/base.d.ts +26 -0
  14. package/dist/flows/base.mjs +2 -0
  15. package/dist/flows/base.mjs.map +1 -0
  16. package/dist/flows/embedded.cjs +2 -0
  17. package/dist/flows/embedded.cjs.map +1 -0
  18. package/dist/flows/embedded.d.ts +9 -0
  19. package/dist/flows/embedded.mjs +2 -0
  20. package/dist/flows/embedded.mjs.map +1 -0
  21. package/dist/flows/index.cjs +1 -0
  22. package/dist/flows/index.d.ts +5 -0
  23. package/dist/flows/index.mjs +1 -0
  24. package/dist/flows/native.cjs +2 -0
  25. package/dist/flows/native.cjs.map +1 -0
  26. package/dist/flows/native.d.ts +9 -0
  27. package/dist/flows/native.mjs +2 -0
  28. package/dist/flows/native.mjs.map +1 -0
  29. package/dist/flows/popup.cjs +2 -0
  30. package/dist/flows/popup.cjs.map +1 -0
  31. package/dist/flows/popup.d.ts +9 -0
  32. package/dist/flows/popup.mjs +2 -0
  33. package/dist/flows/popup.mjs.map +1 -0
  34. package/dist/flows/redirect.cjs +2 -0
  35. package/dist/flows/redirect.cjs.map +1 -0
  36. package/dist/flows/redirect.d.ts +9 -0
  37. package/dist/flows/redirect.mjs +2 -0
  38. package/dist/flows/redirect.mjs.map +1 -0
  39. package/dist/handlers/embedded.cjs +2 -0
  40. package/dist/handlers/embedded.cjs.map +1 -0
  41. package/dist/handlers/embedded.d.ts +37 -0
  42. package/dist/handlers/embedded.mjs +2 -0
  43. package/dist/handlers/embedded.mjs.map +1 -0
  44. package/dist/handlers/index.cjs +1 -0
  45. package/dist/handlers/index.d.ts +2 -0
  46. package/dist/handlers/index.mjs +1 -0
  47. package/dist/handlers/native.cjs +2 -0
  48. package/dist/handlers/native.cjs.map +1 -0
  49. package/dist/handlers/native.d.ts +56 -0
  50. package/dist/handlers/native.mjs +2 -0
  51. package/dist/handlers/native.mjs.map +1 -0
  52. package/dist/handlers/popup.cjs +2 -0
  53. package/dist/handlers/popup.cjs.map +1 -0
  54. package/dist/handlers/popup.d.ts +21 -0
  55. package/dist/handlers/popup.mjs +2 -0
  56. package/dist/handlers/popup.mjs.map +1 -0
  57. package/dist/handlers/redirect.cjs +2 -0
  58. package/dist/handlers/redirect.cjs.map +1 -0
  59. package/dist/handlers/redirect.d.ts +18 -0
  60. package/dist/handlers/redirect.mjs +2 -0
  61. package/dist/handlers/redirect.mjs.map +1 -0
  62. package/dist/index.cjs +2 -2
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.ts +16 -21
  65. package/dist/index.mjs +2 -2
  66. package/dist/index.mjs.map +1 -1
  67. package/dist/server/base.cjs +2 -0
  68. package/dist/server/base.cjs.map +1 -0
  69. package/dist/server/base.d.ts +15 -0
  70. package/dist/server/base.mjs +2 -0
  71. package/dist/server/base.mjs.map +1 -0
  72. package/dist/server/index.cjs +1 -0
  73. package/dist/server/index.d.ts +1 -0
  74. package/dist/server/index.mjs +1 -0
  75. package/dist/storages/cache.cjs +2 -0
  76. package/dist/storages/cache.cjs.map +1 -0
  77. package/dist/storages/cache.d.ts +11 -0
  78. package/dist/storages/cache.mjs +2 -0
  79. package/dist/storages/cache.mjs.map +1 -0
  80. package/dist/storages/index.cjs +1 -0
  81. package/dist/storages/index.d.ts +7 -0
  82. package/dist/storages/index.mjs +1 -0
  83. package/dist/storages/indexeddb.cjs +2 -0
  84. package/dist/storages/indexeddb.cjs.map +1 -0
  85. package/dist/storages/indexeddb.d.ts +10 -0
  86. package/dist/storages/indexeddb.mjs +2 -0
  87. package/dist/storages/indexeddb.mjs.map +1 -0
  88. package/dist/storages/local.cjs +2 -0
  89. package/dist/storages/local.cjs.map +1 -0
  90. package/dist/storages/local.d.ts +7 -0
  91. package/dist/storages/local.mjs +2 -0
  92. package/dist/storages/local.mjs.map +1 -0
  93. package/dist/storages/memory.cjs +2 -0
  94. package/dist/storages/memory.cjs.map +1 -0
  95. package/dist/storages/memory.d.ts +8 -0
  96. package/dist/storages/memory.mjs +2 -0
  97. package/dist/storages/memory.mjs.map +1 -0
  98. package/dist/storages/server.cjs +1 -0
  99. package/dist/storages/server.d.ts +39 -0
  100. package/dist/storages/server.mjs +1 -0
  101. package/dist/storages/session.cjs +2 -0
  102. package/dist/storages/session.cjs.map +1 -0
  103. package/dist/storages/session.d.ts +7 -0
  104. package/dist/storages/session.mjs +2 -0
  105. package/dist/storages/session.mjs.map +1 -0
  106. package/dist/storages/worker.cjs +2 -0
  107. package/dist/storages/worker.cjs.map +1 -0
  108. package/dist/storages/worker.d.ts +46 -0
  109. package/dist/storages/worker.mjs +2 -0
  110. package/dist/storages/worker.mjs.map +1 -0
  111. package/dist/types/common.cjs +0 -0
  112. package/dist/types/common.d.ts +145 -0
  113. package/dist/types/common.mjs +0 -0
  114. package/dist/types/embedded.cjs +0 -0
  115. package/dist/types/embedded.d.ts +103 -0
  116. package/dist/types/embedded.mjs +0 -0
  117. package/dist/types/index.cjs +1 -0
  118. package/dist/types/index.d.ts +7 -0
  119. package/dist/types/index.mjs +1 -0
  120. package/dist/types/native.cjs +0 -0
  121. package/dist/types/native.d.ts +385 -0
  122. package/dist/types/native.mjs +0 -0
  123. package/dist/types/oidc.cjs +2 -0
  124. package/dist/types/oidc.cjs.map +1 -0
  125. package/dist/types/oidc.d.ts +768 -0
  126. package/dist/types/oidc.mjs +2 -0
  127. package/dist/types/oidc.mjs.map +1 -0
  128. package/dist/types/popup.cjs +0 -0
  129. package/dist/types/popup.d.ts +69 -0
  130. package/dist/types/popup.mjs +0 -0
  131. package/dist/types/redirect.cjs +0 -0
  132. package/dist/types/redirect.d.ts +21 -0
  133. package/dist/types/redirect.mjs +0 -0
  134. package/dist/types/server.cjs +0 -0
  135. package/dist/types/server.d.ts +200 -0
  136. package/dist/types/server.mjs +0 -0
  137. package/dist/utils/base64url.cjs +2 -0
  138. package/dist/utils/base64url.cjs.map +1 -0
  139. package/dist/utils/base64url.d.ts +14 -0
  140. package/dist/utils/base64url.mjs +2 -0
  141. package/dist/utils/base64url.mjs.map +1 -0
  142. package/dist/utils/common.cjs +2 -0
  143. package/dist/utils/common.cjs.map +1 -0
  144. package/dist/utils/common.d.ts +23 -0
  145. package/dist/utils/common.mjs +2 -0
  146. package/dist/utils/common.mjs.map +1 -0
  147. package/dist/utils/credentials.cjs +2 -2
  148. package/dist/utils/credentials.cjs.map +1 -1
  149. package/dist/utils/credentials.d.ts +19 -3
  150. package/dist/utils/credentials.mjs +2 -2
  151. package/dist/utils/credentials.mjs.map +1 -1
  152. package/dist/utils/crypto.cjs +2 -2
  153. package/dist/utils/crypto.cjs.map +1 -1
  154. package/dist/utils/crypto.d.ts +16 -26
  155. package/dist/utils/crypto.mjs +2 -2
  156. package/dist/utils/crypto.mjs.map +1 -1
  157. package/dist/utils/dom.cjs +2 -0
  158. package/dist/utils/dom.cjs.map +1 -0
  159. package/dist/utils/dom.d.ts +7 -0
  160. package/dist/utils/dom.mjs +2 -0
  161. package/dist/utils/dom.mjs.map +1 -0
  162. package/dist/utils/errors.cjs +2 -2
  163. package/dist/utils/errors.cjs.map +1 -1
  164. package/dist/utils/errors.d.ts +62 -1
  165. package/dist/utils/errors.mjs +2 -2
  166. package/dist/utils/errors.mjs.map +1 -1
  167. package/dist/utils/httpClient.cjs +2 -0
  168. package/dist/utils/httpClient.cjs.map +1 -0
  169. package/dist/utils/httpClient.d.ts +2 -0
  170. package/dist/utils/httpClient.mjs +2 -0
  171. package/dist/utils/httpClient.mjs.map +1 -0
  172. package/dist/utils/index.cjs +1 -0
  173. package/dist/utils/index.d.ts +12 -0
  174. package/dist/utils/index.mjs +1 -0
  175. package/dist/utils/logging.cjs +2 -0
  176. package/dist/utils/logging.cjs.map +1 -0
  177. package/dist/utils/logging.d.ts +2 -0
  178. package/dist/utils/logging.mjs +2 -0
  179. package/dist/utils/logging.mjs.map +1 -0
  180. package/dist/utils/oidc.cjs +1 -0
  181. package/dist/utils/oidc.d.ts +264 -0
  182. package/dist/utils/oidc.mjs +1 -0
  183. package/dist/utils/server.cjs +1 -0
  184. package/dist/utils/server.d.ts +72 -0
  185. package/dist/utils/server.mjs +1 -0
  186. package/dist/utils/session.cjs +2 -0
  187. package/dist/utils/session.cjs.map +1 -0
  188. package/dist/utils/session.d.ts +29 -0
  189. package/dist/utils/session.mjs +2 -0
  190. package/dist/utils/session.mjs.map +1 -0
  191. package/dist/utils/state.cjs +1 -0
  192. package/dist/utils/state.d.ts +23 -0
  193. package/dist/utils/state.mjs +1 -0
  194. package/package.json +53 -1
  195. package/testing/tests/flows/base.spec.ts +1030 -0
  196. package/testing/tests/flows/embedded.spec.ts +443 -0
  197. package/testing/tests/flows/index.spec.ts +8 -0
  198. package/testing/tests/flows/native.spec.ts +487 -0
  199. package/testing/tests/flows/popup.spec.ts +400 -0
  200. package/testing/tests/flows/redirect.spec.ts +369 -0
  201. package/testing/tests/handlers/embedded.spec.ts +199 -0
  202. package/testing/tests/handlers/index.spec.ts +8 -0
  203. package/testing/tests/handlers/native.spec.ts +283 -0
  204. package/testing/tests/handlers/popup.spec.ts +206 -0
  205. package/testing/tests/handlers/redirect.spec.ts +70 -0
  206. package/testing/tests/index.spec.ts +78 -0
  207. package/testing/tests/server/base.spec.ts +846 -0
  208. package/testing/tests/server/index.spec.ts +15 -0
  209. package/testing/tests/storages/cache.spec.ts +107 -0
  210. package/testing/tests/storages/index.spec.ts +22 -0
  211. package/testing/tests/storages/indexeddb.spec.ts +113 -0
  212. package/testing/tests/storages/local.spec.ts +46 -0
  213. package/testing/tests/storages/memory.spec.ts +51 -0
  214. package/testing/tests/storages/server.spec.ts +435 -0
  215. package/testing/tests/storages/session.spec.ts +46 -0
  216. package/testing/tests/storages/worker.spec.ts +106 -0
  217. package/testing/tests/utils/common.spec.ts +77 -0
  218. package/testing/tests/utils/credentials.spec.ts +195 -0
  219. package/testing/tests/utils/dom.spec.ts +37 -0
  220. package/testing/tests/utils/index.spec.ts +71 -0
  221. package/testing/tests/utils/logging.spec.ts +82 -0
  222. package/testing/tests/utils/oidc.spec.ts +744 -0
  223. package/testing/tests/utils/server.spec.ts +235 -0
  224. package/dist/flows/BaseFlow.cjs +0 -2
  225. package/dist/flows/BaseFlow.cjs.map +0 -1
  226. package/dist/flows/BaseFlow.d.ts +0 -191
  227. package/dist/flows/BaseFlow.mjs +0 -2
  228. package/dist/flows/BaseFlow.mjs.map +0 -1
  229. package/dist/flows/EmbeddedFlow.cjs +0 -2
  230. package/dist/flows/EmbeddedFlow.cjs.map +0 -1
  231. package/dist/flows/EmbeddedFlow.d.ts +0 -26
  232. package/dist/flows/EmbeddedFlow.mjs +0 -2
  233. package/dist/flows/EmbeddedFlow.mjs.map +0 -1
  234. package/dist/flows/NativeFlow.cjs +0 -2
  235. package/dist/flows/NativeFlow.cjs.map +0 -1
  236. package/dist/flows/NativeFlow.d.ts +0 -34
  237. package/dist/flows/NativeFlow.mjs +0 -2
  238. package/dist/flows/NativeFlow.mjs.map +0 -1
  239. package/dist/flows/PopupFlow.cjs +0 -2
  240. package/dist/flows/PopupFlow.cjs.map +0 -1
  241. package/dist/flows/PopupFlow.d.ts +0 -36
  242. package/dist/flows/PopupFlow.mjs +0 -2
  243. package/dist/flows/PopupFlow.mjs.map +0 -1
  244. package/dist/flows/RedirectFlow.cjs +0 -2
  245. package/dist/flows/RedirectFlow.cjs.map +0 -1
  246. package/dist/flows/RedirectFlow.d.ts +0 -38
  247. package/dist/flows/RedirectFlow.mjs +0 -2
  248. package/dist/flows/RedirectFlow.mjs.map +0 -1
  249. package/dist/handlers/BaseFlowHandler.cjs +0 -2
  250. package/dist/handlers/BaseFlowHandler.cjs.map +0 -1
  251. package/dist/handlers/BaseFlowHandler.d.ts +0 -61
  252. package/dist/handlers/BaseFlowHandler.mjs +0 -2
  253. package/dist/handlers/BaseFlowHandler.mjs.map +0 -1
  254. package/dist/handlers/EmbeddedFlowHandler.cjs +0 -2
  255. package/dist/handlers/EmbeddedFlowHandler.cjs.map +0 -1
  256. package/dist/handlers/EmbeddedFlowHandler.d.ts +0 -28
  257. package/dist/handlers/EmbeddedFlowHandler.mjs +0 -2
  258. package/dist/handlers/EmbeddedFlowHandler.mjs.map +0 -1
  259. package/dist/handlers/NativeFlowHandler.cjs +0 -2
  260. package/dist/handlers/NativeFlowHandler.cjs.map +0 -1
  261. package/dist/handlers/NativeFlowHandler.d.ts +0 -31
  262. package/dist/handlers/NativeFlowHandler.mjs +0 -2
  263. package/dist/handlers/NativeFlowHandler.mjs.map +0 -1
  264. package/dist/storages/LocalStorage.cjs +0 -2
  265. package/dist/storages/LocalStorage.cjs.map +0 -1
  266. package/dist/storages/LocalStorage.d.ts +0 -23
  267. package/dist/storages/LocalStorage.mjs +0 -2
  268. package/dist/storages/LocalStorage.mjs.map +0 -1
  269. package/dist/storages/SessionStorage.cjs +0 -2
  270. package/dist/storages/SessionStorage.cjs.map +0 -1
  271. package/dist/storages/SessionStorage.d.ts +0 -23
  272. package/dist/storages/SessionStorage.mjs +0 -2
  273. package/dist/storages/SessionStorage.mjs.map +0 -1
  274. package/dist/types.cjs +0 -2
  275. package/dist/types.cjs.map +0 -1
  276. package/dist/types.d.ts +0 -1263
  277. package/dist/types.mjs +0 -2
  278. package/dist/types.mjs.map +0 -1
  279. package/dist/utils/HttpClient.cjs +0 -2
  280. package/dist/utils/HttpClient.cjs.map +0 -1
  281. package/dist/utils/HttpClient.d.ts +0 -4
  282. package/dist/utils/HttpClient.mjs +0 -2
  283. package/dist/utils/HttpClient.mjs.map +0 -1
  284. package/dist/utils/Logging.cjs +0 -2
  285. package/dist/utils/Logging.cjs.map +0 -1
  286. package/dist/utils/Logging.d.ts +0 -8
  287. package/dist/utils/Logging.mjs +0 -2
  288. package/dist/utils/Logging.mjs.map +0 -1
  289. package/dist/utils/Metadata.cjs +0 -2
  290. package/dist/utils/Metadata.cjs.map +0 -1
  291. package/dist/utils/Metadata.d.ts +0 -98
  292. package/dist/utils/Metadata.mjs +0 -2
  293. package/dist/utils/Metadata.mjs.map +0 -1
  294. package/dist/utils/Session.cjs +0 -2
  295. package/dist/utils/Session.cjs.map +0 -1
  296. package/dist/utils/Session.d.ts +0 -82
  297. package/dist/utils/Session.mjs +0 -2
  298. package/dist/utils/Session.mjs.map +0 -1
  299. package/dist/utils/State.cjs +0 -2
  300. package/dist/utils/State.cjs.map +0 -1
  301. package/dist/utils/State.d.ts +0 -43
  302. package/dist/utils/State.mjs +0 -2
  303. package/dist/utils/State.mjs.map +0 -1
  304. package/dist/utils/base64Url.cjs +0 -2
  305. package/dist/utils/base64Url.cjs.map +0 -1
  306. package/dist/utils/base64Url.d.ts +0 -27
  307. package/dist/utils/base64Url.mjs +0 -2
  308. package/dist/utils/base64Url.mjs.map +0 -1
  309. package/dist/utils/date.cjs +0 -2
  310. package/dist/utils/date.cjs.map +0 -1
  311. package/dist/utils/date.d.ts +0 -6
  312. package/dist/utils/date.mjs +0 -2
  313. package/dist/utils/date.mjs.map +0 -1
  314. package/dist/utils/handlers.cjs +0 -2
  315. package/dist/utils/handlers.cjs.map +0 -1
  316. package/dist/utils/handlers.d.ts +0 -35
  317. package/dist/utils/handlers.mjs +0 -2
  318. package/dist/utils/handlers.mjs.map +0 -1
  319. package/dist/utils/jwt.cjs +0 -2
  320. package/dist/utils/jwt.cjs.map +0 -1
  321. package/dist/utils/jwt.d.ts +0 -34
  322. package/dist/utils/jwt.mjs +0 -2
  323. package/dist/utils/jwt.mjs.map +0 -1
  324. package/dist/utils/object.cjs +0 -2
  325. package/dist/utils/object.cjs.map +0 -1
  326. package/dist/utils/object.d.ts +0 -1
  327. package/dist/utils/object.mjs +0 -2
  328. package/dist/utils/object.mjs.map +0 -1
@@ -0,0 +1,369 @@
1
+ import { beforeEach, describe, test, expect, vi } from 'vitest';
2
+ import { createOptions, getMockStorage } from '@strivacity/testing/mocks/sdk';
3
+ import { createRedirectFlow } from '../../../src/flows/redirect';
4
+
5
+ describe('createRedirectFlow', () => {
6
+ let options: ReturnType<typeof createOptions>;
7
+ let flow: ReturnType<typeof createRedirectFlow>;
8
+
9
+ beforeEach(() => {
10
+ options = createOptions({ urlHandler: vi.fn() });
11
+ flow = createRedirectFlow(options);
12
+ });
13
+
14
+ describe('init', () => {
15
+ test('lazyLoad: true', async () => {
16
+ flow = createRedirectFlow(createOptions({ lazyLoad: true }));
17
+
18
+ // 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.
19
+ await Promise.resolve();
20
+
21
+ expect(flow.initialized).toBe(false);
22
+
23
+ await flow.init();
24
+
25
+ expect(flow.initialized).toBe(true);
26
+ });
27
+
28
+ test('lazyLoad: false', async () => {
29
+ flow = createRedirectFlow(createOptions({ lazyLoad: false }));
30
+
31
+ await vi.waitFor(() => expect(flow.initialized).toBe(true));
32
+ });
33
+ });
34
+
35
+ describe('login', () => {
36
+ describe('w/o serverSessionUri', () => {
37
+ test('w/o params', async () => {
38
+ await flow.login();
39
+
40
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), {});
41
+
42
+ const stateData = getMockStorage(options.stateStorage).getLastState();
43
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
44
+
45
+ expect(`${url.origin}${url.pathname}`).toBe('https://brandtegrity.io/oauth2/auth');
46
+ expect(url.searchParams.get('client_id')).toBe('client-id');
47
+ expect(url.searchParams.get('redirect_uri')).toBe('https://brandtegrity.io/callback');
48
+ expect(url.searchParams.get('response_type')).toBe('code');
49
+ expect(url.searchParams.get('response_mode')).toBe('query');
50
+ expect(url.searchParams.get('scope')).toBe('openid');
51
+ expect(url.searchParams.get('code_challenge_method')).toBe('S256');
52
+ expect(url.searchParams.get('state')).toBe(stateData?.id);
53
+ expect(url.searchParams.get('code_challenge')).toBe(stateData?.codeChallenge);
54
+ expect(url.searchParams.get('nonce')).toBe(stateData?.nonce);
55
+ expect(url.searchParams.has('prompt')).toBeFalsy();
56
+ expect(url.searchParams.has('display')).toBeFalsy();
57
+ expect(url.searchParams.has('login_hint')).toBeFalsy();
58
+ expect(url.searchParams.has('acr_values')).toBeFalsy();
59
+ expect(url.searchParams.has('ui_locales')).toBeFalsy();
60
+ expect(url.searchParams.has('audience')).toBeFalsy();
61
+ expect(params).toEqual({});
62
+ });
63
+
64
+ test('w/ params', async () => {
65
+ const extraParams = {
66
+ prompt: 'login',
67
+ display: 'page',
68
+ loginHint: 'test@example.com',
69
+ acrValues: ['urn:mace:incommon:iap:silver', 'urn:mace:incommon:iap:bronze'],
70
+ uiLocales: ['en-US', 'hu-HU'],
71
+ audiences: ['https://api.example.com', 'https://api2.example.com'],
72
+ };
73
+
74
+ await flow.login(extraParams);
75
+
76
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), extraParams);
77
+
78
+ const stateData = getMockStorage(options.stateStorage).getLastState();
79
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
80
+
81
+ expect(`${url.origin}${url.pathname}`).toBe('https://brandtegrity.io/oauth2/auth');
82
+ expect(url.searchParams.get('client_id')).toBe('client-id');
83
+ expect(url.searchParams.get('redirect_uri')).toBe('https://brandtegrity.io/callback');
84
+ expect(url.searchParams.get('response_type')).toBe('code');
85
+ expect(url.searchParams.get('response_mode')).toBe('query');
86
+ expect(url.searchParams.get('scope')).toBe('openid');
87
+ expect(url.searchParams.get('code_challenge_method')).toBe('S256');
88
+ expect(url.searchParams.get('state')).toBe(stateData?.id);
89
+ expect(url.searchParams.get('code_challenge')).toBe(stateData?.codeChallenge);
90
+ expect(url.searchParams.get('nonce')).toBe(stateData?.nonce);
91
+ expect(url.searchParams.get('prompt')).toBe('login');
92
+ expect(url.searchParams.get('display')).toBe('page');
93
+ expect(url.searchParams.get('login_hint')).toBe('test@example.com');
94
+ expect(url.searchParams.get('acr_values')).toBe('urn:mace:incommon:iap:silver urn:mace:incommon:iap:bronze');
95
+ expect(url.searchParams.get('ui_locales')).toBe('en-US hu-HU');
96
+ expect(url.searchParams.get('audience')).toBe('https://api.example.com https://api2.example.com');
97
+ expect(params).toEqual(extraParams);
98
+ });
99
+ });
100
+
101
+ describe('w/ serverSessionUri', () => {
102
+ test('w/o params', async () => {
103
+ options = createOptions({ urlHandler: vi.fn(), serverSessionUri: 'https://app.example.com/login' });
104
+ flow = createRedirectFlow(options);
105
+
106
+ await flow.login();
107
+
108
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), {});
109
+
110
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
111
+
112
+ expect(`${url.origin}${url.pathname}`).toBe('https://app.example.com/login');
113
+ expect(url.searchParams.has('client_id')).toBeFalsy();
114
+ expect(url.searchParams.has('redirect_uri')).toBeFalsy();
115
+ expect(url.searchParams.has('response_type')).toBeFalsy();
116
+ expect(url.searchParams.has('response_mode')).toBeFalsy();
117
+ expect(url.searchParams.has('scope')).toBeFalsy();
118
+ expect(url.searchParams.has('code_challenge_method')).toBeFalsy();
119
+ expect(url.searchParams.has('state')).toBeFalsy();
120
+ expect(url.searchParams.has('code_challenge')).toBeFalsy();
121
+ expect(url.searchParams.has('nonce')).toBeFalsy();
122
+ expect(url.searchParams.has('prompt')).toBeFalsy();
123
+ expect(url.searchParams.has('display')).toBeFalsy();
124
+ expect(url.searchParams.has('login_hint')).toBeFalsy();
125
+ expect(url.searchParams.has('acr_values')).toBeFalsy();
126
+ expect(url.searchParams.has('ui_locales')).toBeFalsy();
127
+ expect(url.searchParams.has('audience')).toBeFalsy();
128
+ expect(params).toEqual({});
129
+ });
130
+
131
+ test('w/ params', async () => {
132
+ options = createOptions({ urlHandler: vi.fn(), serverSessionUri: 'https://app.example.com/login' });
133
+ flow = createRedirectFlow(options);
134
+
135
+ const extraParams = {
136
+ prompt: 'login',
137
+ display: 'page',
138
+ loginHint: 'test@example.com',
139
+ acrValues: ['urn:mace:incommon:iap:silver', 'urn:mace:incommon:iap:bronze'],
140
+ uiLocales: ['en-US', 'hu-HU'],
141
+ audiences: ['https://api.example.com', 'https://api2.example.com'],
142
+ };
143
+
144
+ await flow.login(extraParams);
145
+
146
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), extraParams);
147
+
148
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
149
+
150
+ expect(`${url.origin}${url.pathname}`).toBe('https://app.example.com/login');
151
+ expect(url.searchParams.has('client_id')).toBeFalsy();
152
+ expect(url.searchParams.has('redirect_uri')).toBeFalsy();
153
+ expect(url.searchParams.has('response_type')).toBeFalsy();
154
+ expect(url.searchParams.has('response_mode')).toBeFalsy();
155
+ expect(url.searchParams.has('scope')).toBeFalsy();
156
+ expect(url.searchParams.has('code_challenge_method')).toBeFalsy();
157
+ expect(url.searchParams.has('state')).toBeFalsy();
158
+ expect(url.searchParams.has('code_challenge')).toBeFalsy();
159
+ expect(url.searchParams.has('nonce')).toBeFalsy();
160
+ expect(url.searchParams.get('prompt')).toBe('login');
161
+ expect(url.searchParams.get('display')).toBe('page');
162
+ expect(url.searchParams.get('login_hint')).toBe('test@example.com');
163
+ expect(url.searchParams.get('acr_values')).toBe('urn:mace:incommon:iap:silver urn:mace:incommon:iap:bronze');
164
+ expect(url.searchParams.get('ui_locales')).toBe('en-US hu-HU');
165
+ expect(url.searchParams.get('audience')).toBe('https://api.example.com https://api2.example.com');
166
+ expect(params).toEqual(extraParams);
167
+ });
168
+ });
169
+ });
170
+
171
+ describe('register', () => {
172
+ describe('w/o serverSessionUri', () => {
173
+ test('w/o params', async () => {
174
+ await flow.register();
175
+
176
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), { prompt: 'create' });
177
+
178
+ const stateData = getMockStorage(options.stateStorage).getLastState();
179
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
180
+
181
+ expect(`${url.origin}${url.pathname}`).toBe('https://brandtegrity.io/oauth2/auth');
182
+ expect(url.searchParams.get('client_id')).toBe('client-id');
183
+ expect(url.searchParams.get('redirect_uri')).toBe('https://brandtegrity.io/callback');
184
+ expect(url.searchParams.get('response_type')).toBe('code');
185
+ expect(url.searchParams.get('response_mode')).toBe('query');
186
+ expect(url.searchParams.get('scope')).toBe('openid');
187
+ expect(url.searchParams.get('code_challenge_method')).toBe('S256');
188
+ expect(url.searchParams.get('state')).toBe(stateData?.id);
189
+ expect(url.searchParams.get('code_challenge')).toBe(stateData?.codeChallenge);
190
+ expect(url.searchParams.get('nonce')).toBe(stateData?.nonce);
191
+ expect(url.searchParams.get('prompt')).toBe('create');
192
+ expect(url.searchParams.has('display')).toBeFalsy();
193
+ expect(url.searchParams.has('login_hint')).toBeFalsy();
194
+ expect(url.searchParams.has('acr_values')).toBeFalsy();
195
+ expect(url.searchParams.has('ui_locales')).toBeFalsy();
196
+ expect(url.searchParams.has('audience')).toBeFalsy();
197
+ expect(params).toEqual({ prompt: 'create' });
198
+ });
199
+
200
+ test('w/ params', async () => {
201
+ const extraParams = {
202
+ prompt: 'create',
203
+ display: 'page',
204
+ loginHint: 'test@example.com',
205
+ acrValues: ['urn:mace:incommon:iap:silver', 'urn:mace:incommon:iap:bronze'],
206
+ uiLocales: ['en-US', 'hu-HU'],
207
+ audiences: ['https://api.example.com', 'https://api2.example.com'],
208
+ };
209
+
210
+ await flow.register({ ...extraParams, prompt: undefined });
211
+
212
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), extraParams);
213
+
214
+ const stateData = getMockStorage(options.stateStorage).getLastState();
215
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
216
+
217
+ expect(`${url.origin}${url.pathname}`).toBe('https://brandtegrity.io/oauth2/auth');
218
+ expect(url.searchParams.get('client_id')).toBe('client-id');
219
+ expect(url.searchParams.get('redirect_uri')).toBe('https://brandtegrity.io/callback');
220
+ expect(url.searchParams.get('response_type')).toBe('code');
221
+ expect(url.searchParams.get('response_mode')).toBe('query');
222
+ expect(url.searchParams.get('scope')).toBe('openid');
223
+ expect(url.searchParams.get('code_challenge_method')).toBe('S256');
224
+ expect(url.searchParams.get('state')).toBe(stateData?.id);
225
+ expect(url.searchParams.get('code_challenge')).toBe(stateData?.codeChallenge);
226
+ expect(url.searchParams.get('nonce')).toBe(stateData?.nonce);
227
+ expect(url.searchParams.get('prompt')).toBe('create');
228
+ expect(url.searchParams.get('display')).toBe('page');
229
+ expect(url.searchParams.get('login_hint')).toBe('test@example.com');
230
+ expect(url.searchParams.get('acr_values')).toBe('urn:mace:incommon:iap:silver urn:mace:incommon:iap:bronze');
231
+ expect(url.searchParams.get('ui_locales')).toBe('en-US hu-HU');
232
+ expect(url.searchParams.get('audience')).toBe('https://api.example.com https://api2.example.com');
233
+ expect(params).toEqual(extraParams);
234
+ });
235
+ });
236
+
237
+ describe('w/ serverSessionUri', () => {
238
+ test('w/o params', async () => {
239
+ options = createOptions({ urlHandler: vi.fn(), serverSessionUri: 'https://app.example.com/login' });
240
+ flow = createRedirectFlow(options);
241
+
242
+ await flow.register();
243
+
244
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), { prompt: 'create' });
245
+
246
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
247
+
248
+ expect(`${url.origin}${url.pathname}`).toBe('https://app.example.com/login');
249
+ expect(url.searchParams.has('client_id')).toBeFalsy();
250
+ expect(url.searchParams.has('redirect_uri')).toBeFalsy();
251
+ expect(url.searchParams.has('response_type')).toBeFalsy();
252
+ expect(url.searchParams.has('response_mode')).toBeFalsy();
253
+ expect(url.searchParams.has('scope')).toBeFalsy();
254
+ expect(url.searchParams.has('code_challenge_method')).toBeFalsy();
255
+ expect(url.searchParams.has('state')).toBeFalsy();
256
+ expect(url.searchParams.has('code_challenge')).toBeFalsy();
257
+ expect(url.searchParams.has('nonce')).toBeFalsy();
258
+ expect(url.searchParams.get('prompt')).toBe('create');
259
+ expect(url.searchParams.has('display')).toBeFalsy();
260
+ expect(url.searchParams.has('login_hint')).toBeFalsy();
261
+ expect(url.searchParams.has('acr_values')).toBeFalsy();
262
+ expect(url.searchParams.has('ui_locales')).toBeFalsy();
263
+ expect(url.searchParams.has('audience')).toBeFalsy();
264
+ expect(params).toEqual({ prompt: 'create' });
265
+ });
266
+
267
+ test('w/ params', async () => {
268
+ options = createOptions({ urlHandler: vi.fn(), serverSessionUri: 'https://app.example.com/login' });
269
+ flow = createRedirectFlow(options);
270
+
271
+ const extraParams = {
272
+ prompt: 'create',
273
+ display: 'page',
274
+ loginHint: 'test@example.com',
275
+ acrValues: ['urn:mace:incommon:iap:silver', 'urn:mace:incommon:iap:bronze'],
276
+ uiLocales: ['en-US', 'hu-HU'],
277
+ audiences: ['https://api.example.com', 'https://api2.example.com'],
278
+ };
279
+
280
+ await flow.register({ ...extraParams, prompt: undefined });
281
+
282
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), extraParams);
283
+
284
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
285
+
286
+ expect(`${url.origin}${url.pathname}`).toBe('https://app.example.com/login');
287
+ expect(url.searchParams.has('client_id')).toBeFalsy();
288
+ expect(url.searchParams.has('redirect_uri')).toBeFalsy();
289
+ expect(url.searchParams.has('response_type')).toBeFalsy();
290
+ expect(url.searchParams.has('response_mode')).toBeFalsy();
291
+ expect(url.searchParams.has('scope')).toBeFalsy();
292
+ expect(url.searchParams.has('code_challenge_method')).toBeFalsy();
293
+ expect(url.searchParams.has('state')).toBeFalsy();
294
+ expect(url.searchParams.has('code_challenge')).toBeFalsy();
295
+ expect(url.searchParams.has('nonce')).toBeFalsy();
296
+ expect(url.searchParams.get('prompt')).toBe('create');
297
+ expect(url.searchParams.get('display')).toBe('page');
298
+ expect(url.searchParams.get('login_hint')).toBe('test@example.com');
299
+ expect(url.searchParams.get('acr_values')).toBe('urn:mace:incommon:iap:silver urn:mace:incommon:iap:bronze');
300
+ expect(url.searchParams.get('ui_locales')).toBe('en-US hu-HU');
301
+ expect(url.searchParams.get('audience')).toBe('https://api.example.com https://api2.example.com');
302
+ expect(params).toEqual(extraParams);
303
+ });
304
+ });
305
+ });
306
+
307
+ describe('entry', () => {
308
+ test('should redirect to the provider entry endpoint preserving query parameters', async () => {
309
+ await flow.entry('https://brandtegrity.io/entry?challenge=abc123&foo=bar');
310
+
311
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0];
312
+
313
+ expect((url as URL).toString()).toBe('https://brandtegrity.io/provider/entry?challenge=abc123&foo=bar');
314
+ expect(params).toBeUndefined();
315
+ });
316
+
317
+ test('should default to the current window location when no url is provided', async () => {
318
+ window.location.search = '?challenge=abc123&foo=bar';
319
+
320
+ await flow.entry();
321
+
322
+ const [url] = vi.mocked(options.urlHandler).mock.calls[0];
323
+
324
+ expect((url as URL).toString()).toBe('https://brandtegrity.io/provider/entry?challenge=abc123&foo=bar');
325
+ });
326
+ });
327
+
328
+ test('flow state accessors', async () => {
329
+ options = createOptions({ lazyLoad: true, urlHandler: vi.fn() });
330
+ flow = createRedirectFlow(options);
331
+
332
+ expect(flow.options).toBe(options);
333
+ expect(flow.initialized).toBeFalsy();
334
+ expect(flow.storage).toBe(options.storage);
335
+ expect(flow.httpClient).toBe(options.httpClient);
336
+ expect(flow.logging).toBe(options.logging);
337
+ expect(flow.sessionId).toBeNull();
338
+ expect(flow.shortAppId).toBeNull();
339
+ expect(flow.language).toBe('en-US');
340
+ await expect(flow.isAuthenticated).resolves.toBeFalsy();
341
+ expect(flow.isAuthenticatedSync).toBeFalsy();
342
+ expect(flow.accessToken).toBeNull();
343
+ expect(flow.accessTokenExpired).toBeTruthy();
344
+ expect(flow.accessTokenExpirationDate).toBeNull();
345
+ expect(flow.refreshToken).toBeNull();
346
+ expect(flow.idToken).toBeNull();
347
+ expect(flow.idTokenClaims).toBeNull();
348
+
349
+ await flow.init();
350
+
351
+ flow.sessionId = 'abc123';
352
+ flow.shortAppId = 'app';
353
+ flow.language = 'hu-HU';
354
+ flow.session = getMockStorage(options.storage).generateSession();
355
+
356
+ expect(flow.initialized).toBeTruthy();
357
+ expect(flow.sessionId).toBe('abc123');
358
+ expect(flow.shortAppId).toBe('app');
359
+ expect(flow.language).toBe('hu-HU');
360
+ await expect(flow.isAuthenticated).resolves.toBeTruthy();
361
+ expect(flow.isAuthenticatedSync).toBeTruthy();
362
+ expect(flow.accessToken).toBe(flow.session.access_token);
363
+ expect(flow.accessTokenExpired).toBeFalsy();
364
+ expect(flow.accessTokenExpirationDate).toBe(flow.session.expires_at);
365
+ expect(flow.refreshToken).toBe(flow.session.refresh_token);
366
+ expect(flow.idToken).toBe(flow.session.id_token);
367
+ expect(flow.idTokenClaims).toBe(flow.session?.claims);
368
+ });
369
+ });
@@ -0,0 +1,199 @@
1
+ import type { LoginComponent } from '../../../src/types';
2
+ import { beforeEach, describe, test, expect } from 'vitest';
3
+ import { createOptions, createFakeBaseFlow } from '@strivacity/testing/mocks/sdk';
4
+ import { worker, http, HttpResponse } from '@strivacity/testing/mocks/msw';
5
+ import { getDefaultFlowState, ProtocolError, OidcError, ServerError, NetworkError } from '../../../src/utils';
6
+ import { startSessionHandler, finalizeSessionHandler } from '../../../src/handlers/embedded';
7
+
8
+ describe('startSessionHandler', () => {
9
+ let params: Parameters<typeof startSessionHandler>[0];
10
+
11
+ beforeEach(() => {
12
+ globalThis.location.href = 'https://brandtegrity.io';
13
+ params = { base: createFakeBaseFlow(), options: createOptions(), flowState: getDefaultFlowState(), params: {} };
14
+ });
15
+
16
+ test('should start session correctly', async () => {
17
+ worker.use(
18
+ http.get('https://brandtegrity.io/oauth2/auth', () =>
19
+ HttpResponse.text('https://brandtegrity.io/login?session_id=abc123&short_app_id=app&language=hu-HU'),
20
+ ),
21
+ );
22
+
23
+ await startSessionHandler(params);
24
+
25
+ expect(params.base.dispatchEvent).toHaveBeenCalledWith('flowInitiated', []);
26
+ expect(params.flowState.sessionId).toBe('abc123');
27
+ expect(params.flowState.shortAppId).toBe('app');
28
+ expect(params.flowState.language).toBe('hu-HU');
29
+ });
30
+
31
+ describe('should exchange the code for tokens correctly', () => {
32
+ test('w/o serverSessionUri', async () => {
33
+ worker.use(http.get('https://brandtegrity.io/oauth2/auth', () => HttpResponse.text('https://brandtegrity.io/callback?code=abc123')));
34
+
35
+ await startSessionHandler(params);
36
+
37
+ expect(globalThis.location.href).not.toBe('https://brandtegrity.io/callback?code=abc123');
38
+ expect(params.base.tokenExchange).toHaveBeenCalledWith({ code: 'abc123' });
39
+ });
40
+
41
+ test('w/ serverSessionUri', async () => {
42
+ worker.use(http.get('https://brandtegrity.io/login', () => HttpResponse.text('https://brandtegrity.io/callback?code=abc123')));
43
+ params.options = createOptions({ serverSessionUri: 'https://brandtegrity.io/login' });
44
+
45
+ await startSessionHandler(params);
46
+
47
+ expect(globalThis.location.href).toBe('https://brandtegrity.io/callback?code=abc123');
48
+ expect(params.base.tokenExchange).not.toHaveBeenCalled();
49
+ });
50
+ });
51
+
52
+ test('should reject with a ProtocolError when short_app_id is missing', async () => {
53
+ worker.use(http.get('https://brandtegrity.io/oauth2/auth', () => HttpResponse.text('https://brandtegrity.io/login?session_id=abc')));
54
+
55
+ await expect(startSessionHandler(params)).rejects.toThrow(new ProtocolError('short_app_id is missing in the response'));
56
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Start login session error', expect.any(ProtocolError));
57
+ });
58
+
59
+ test('should reject with a ProtocolError when neither code nor session_id are present', async () => {
60
+ worker.use(http.get('https://brandtegrity.io/oauth2/auth', () => HttpResponse.text('https://brandtegrity.io/login?something=else')));
61
+
62
+ await expect(startSessionHandler(params)).rejects.toThrow(new ProtocolError('Neither "code" nor "session_id" is present in the response'));
63
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Start login session error', expect.any(ProtocolError));
64
+ });
65
+
66
+ test('should reject with a ProtocolError when neither code nor session_id are present', async () => {
67
+ worker.use(
68
+ http.get('https://brandtegrity.io/oauth2/auth', () =>
69
+ HttpResponse.text('https://brandtegrity.io/login?error=access_denied&error_description=nope&error_uri=https://example.com/error'),
70
+ ),
71
+ );
72
+
73
+ await expect(startSessionHandler(params)).rejects.toThrow(new OidcError('access_denied', 'nope', 'https://example.com/error'));
74
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Start login session error', expect.any(OidcError));
75
+ });
76
+
77
+ test('should reject with a ProtocolError when the redirect URI does not match the configured redirectUri', async () => {
78
+ worker.use(http.get('https://brandtegrity.io/oauth2/auth', () => HttpResponse.text('https://invalid.domain/callback?code=abc123')));
79
+
80
+ await expect(startSessionHandler(params)).rejects.toThrow(new ProtocolError('Invalid redirect URI'));
81
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Start login session error', expect.any(ProtocolError));
82
+ });
83
+
84
+ describe('should reject with an error when the HTTP request fails', () => {
85
+ test('HTTP 400', async () => {
86
+ worker.use(http.get('https://brandtegrity.io/oauth2/auth', () => new HttpResponse(null, { status: 400 })));
87
+
88
+ await expect(startSessionHandler(params)).rejects.toThrow(new ProtocolError('Failed to start login session'));
89
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Start login session error', expect.any(ProtocolError));
90
+ });
91
+
92
+ test('HTTP 429', async () => {
93
+ worker.use(http.get('https://brandtegrity.io/oauth2/auth', () => new HttpResponse(null, { status: 429 })));
94
+
95
+ await expect(startSessionHandler(params)).rejects.toThrow(new ServerError('Failed to start login session', 429));
96
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Start login session error', expect.any(ServerError));
97
+ });
98
+
99
+ test('HTTP 500', async () => {
100
+ worker.use(http.get('https://brandtegrity.io/oauth2/auth', () => new HttpResponse(null, { status: 500 })));
101
+
102
+ await expect(startSessionHandler(params)).rejects.toThrow(new ServerError('Failed to start login session', 500));
103
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Start login session error', expect.any(ServerError));
104
+ });
105
+
106
+ test('HTTP 503', async () => {
107
+ worker.use(http.get('https://brandtegrity.io/oauth2/auth', () => HttpResponse.error()));
108
+
109
+ await expect(startSessionHandler(params)).rejects.toThrow(new NetworkError('Network request failed: https://brandtegrity.io/oauth2/auth'));
110
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Start login session error', expect.any(NetworkError));
111
+ });
112
+ });
113
+ });
114
+
115
+ describe('finalizeSessionHandler', () => {
116
+ let params: Parameters<typeof finalizeSessionHandler>[0];
117
+
118
+ beforeEach(() => {
119
+ globalThis.sty = {};
120
+ globalThis.location.href = 'https://brandtegrity.io';
121
+ params = {
122
+ base: createFakeBaseFlow(),
123
+ options: createOptions(),
124
+ flowState: { ...getDefaultFlowState(), sessionId: 'abc123' },
125
+ url: 'https://brandtegrity.io/finalize',
126
+ };
127
+ });
128
+
129
+ describe('should finalize session correctly', () => {
130
+ test('w/o serverSessionUri', async () => {
131
+ globalThis.sty = { hostElement: { sessionId: 'abc123', shortAppId: 'app', lang: 'hu-HU' } as unknown as LoginComponent };
132
+ params.flowState = getDefaultFlowState();
133
+ worker.use(http.get('https://brandtegrity.io/finalize', () => HttpResponse.text('https://brandtegrity.io/callback?code=abc123')));
134
+
135
+ await finalizeSessionHandler(params);
136
+
137
+ expect(params.flowState.sessionId).toBe('abc123');
138
+ expect(params.flowState.shortAppId).toBe('app');
139
+ expect(params.flowState.language).toBe('hu-HU');
140
+
141
+ expect(globalThis.location.href).not.toBe('https://brandtegrity.io/callback?code=abc123');
142
+ expect(params.base.tokenExchange).toHaveBeenCalledWith({ code: 'abc123' });
143
+ });
144
+
145
+ test('w/ serverSessionUri', async () => {
146
+ worker.use(http.get('https://brandtegrity.io/finalize', () => HttpResponse.text('https://brandtegrity.io/callback?code=abc123')));
147
+ params.options = createOptions({ serverSessionUri: 'https://brandtegrity.io/login' });
148
+
149
+ await finalizeSessionHandler(params);
150
+
151
+ expect(globalThis.location.href).toBe('https://brandtegrity.io/callback?code=abc123');
152
+ expect(params.base.tokenExchange).not.toHaveBeenCalled();
153
+ });
154
+ });
155
+
156
+ test('should throw when the session ID is missing', async () => {
157
+ params.flowState = getDefaultFlowState();
158
+
159
+ await expect(finalizeSessionHandler(params)).rejects.toThrow(new Error('Session ID is missing. Cannot finalize login session.'));
160
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Failed to finalize login session', expect.any(Error));
161
+ });
162
+
163
+ test('should reject with a ProtocolError when the redirect URI does not match the configured redirectUri', async () => {
164
+ worker.use(http.get('https://brandtegrity.io/finalize', () => HttpResponse.text('https://invalid.domain/callback?code=abc123')));
165
+
166
+ await expect(finalizeSessionHandler(params)).rejects.toThrow(new ProtocolError('Invalid redirect URI'));
167
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Finalize login session error', expect.any(ProtocolError));
168
+ });
169
+
170
+ describe('should reject with an error when the HTTP request fails', () => {
171
+ test('HTTP 400', async () => {
172
+ worker.use(http.get('https://brandtegrity.io/finalize', () => new HttpResponse(null, { status: 400 })));
173
+
174
+ await expect(finalizeSessionHandler(params)).rejects.toThrow(new ProtocolError('Failed to finalize login session'));
175
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Finalize login session error', expect.any(ProtocolError));
176
+ });
177
+
178
+ test('HTTP 429', async () => {
179
+ worker.use(http.get('https://brandtegrity.io/finalize', () => new HttpResponse(null, { status: 429 })));
180
+
181
+ await expect(finalizeSessionHandler(params)).rejects.toThrow(new ServerError('Failed to finalize login session', 429));
182
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Finalize login session error', expect.any(ServerError));
183
+ });
184
+
185
+ test('HTTP 500', async () => {
186
+ worker.use(http.get('https://brandtegrity.io/finalize', () => new HttpResponse(null, { status: 500 })));
187
+
188
+ await expect(finalizeSessionHandler(params)).rejects.toThrow(new ServerError('Failed to finalize login session', 500));
189
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Finalize login session error', expect.any(ServerError));
190
+ });
191
+
192
+ test('HTTP 503', async () => {
193
+ worker.use(http.get('https://brandtegrity.io/finalize', () => HttpResponse.error()));
194
+
195
+ await expect(finalizeSessionHandler(params)).rejects.toThrow(new NetworkError('Network request failed: https://brandtegrity.io/finalize'));
196
+ expect(params.options.logging?.error).toHaveBeenCalledWith('Finalize login session error', expect.any(NetworkError));
197
+ });
198
+ });
199
+ });
@@ -0,0 +1,8 @@
1
+ import { test, expect } from 'vitest';
2
+ import * as index from '../../../src/handlers';
3
+
4
+ test('should export the correct things', () => {
5
+ const expectedExports = ['popupUrlHandler', 'popupCallbackHandler', 'redirectUrlHandler', 'redirectCallbackHandler'];
6
+
7
+ expect(Object.keys(index)).toEqual(expectedExports);
8
+ });