@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,400 @@
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 { createPopupFlow } from '../../../src/flows/popup';
6
+
7
+ describe('createPopupFlow', () => {
8
+ let options: ReturnType<typeof createOptions>;
9
+ let flow: ReturnType<typeof createPopupFlow>;
10
+
11
+ beforeEach(() => {
12
+ options = createOptions({ urlHandler: vi.fn().mockResolvedValue({ code: 'abc123' }) });
13
+ flow = createPopupFlow(options);
14
+ });
15
+
16
+ describe('init', () => {
17
+ test('lazyLoad: true', async () => {
18
+ flow = createPopupFlow(createOptions({ lazyLoad: true }));
19
+
20
+ // 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.
21
+ await Promise.resolve();
22
+
23
+ expect(flow.initialized).toBe(false);
24
+
25
+ await flow.init();
26
+
27
+ expect(flow.initialized).toBe(true);
28
+ });
29
+
30
+ test('lazyLoad: false', async () => {
31
+ flow = createPopupFlow(createOptions({ lazyLoad: false }));
32
+
33
+ await vi.waitFor(() => expect(flow.initialized).toBe(true));
34
+ });
35
+ });
36
+
37
+ describe('login', () => {
38
+ describe('w/o serverSessionUri', () => {
39
+ test('w/o params', async () => {
40
+ await flow.login().catch(() => undefined);
41
+
42
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), { display: 'popup' });
43
+
44
+ const stateData = getMockStorage(options.stateStorage).getLastState();
45
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
46
+
47
+ expect(`${url.origin}${url.pathname}`).toBe('https://brandtegrity.io/oauth2/auth');
48
+ expect(url.searchParams.get('client_id')).toBe('client-id');
49
+ expect(url.searchParams.get('redirect_uri')).toBe('https://brandtegrity.io/callback');
50
+ expect(url.searchParams.get('response_type')).toBe('code');
51
+ expect(url.searchParams.get('response_mode')).toBe('query');
52
+ expect(url.searchParams.get('scope')).toBe('openid');
53
+ expect(url.searchParams.get('code_challenge_method')).toBe('S256');
54
+ expect(url.searchParams.get('state')).toBe(stateData?.id);
55
+ expect(url.searchParams.get('code_challenge')).toBe(stateData?.codeChallenge);
56
+ expect(url.searchParams.get('nonce')).toBe(stateData?.nonce);
57
+ expect(url.searchParams.has('prompt')).toBeFalsy();
58
+ expect(url.searchParams.get('display')).toBe('popup');
59
+ expect(url.searchParams.has('login_hint')).toBeFalsy();
60
+ expect(url.searchParams.has('acr_values')).toBeFalsy();
61
+ expect(url.searchParams.has('ui_locales')).toBeFalsy();
62
+ expect(url.searchParams.has('audience')).toBeFalsy();
63
+ expect(params).toEqual({ display: 'popup' });
64
+ });
65
+
66
+ test('w/ params', async () => {
67
+ const extraParams = {
68
+ prompt: 'login',
69
+ display: 'page',
70
+ loginHint: 'test@example.com',
71
+ acrValues: ['urn:mace:incommon:iap:silver', 'urn:mace:incommon:iap:bronze'],
72
+ uiLocales: ['en-US', 'hu-HU'],
73
+ audiences: ['https://api.example.com', 'https://api2.example.com'],
74
+ };
75
+
76
+ await flow.login(extraParams).catch(() => undefined);
77
+
78
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), extraParams);
79
+
80
+ const stateData = getMockStorage(options.stateStorage).getLastState();
81
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
82
+
83
+ expect(`${url.origin}${url.pathname}`).toBe('https://brandtegrity.io/oauth2/auth');
84
+ expect(url.searchParams.get('client_id')).toBe('client-id');
85
+ expect(url.searchParams.get('redirect_uri')).toBe('https://brandtegrity.io/callback');
86
+ expect(url.searchParams.get('response_type')).toBe('code');
87
+ expect(url.searchParams.get('response_mode')).toBe('query');
88
+ expect(url.searchParams.get('scope')).toBe('openid');
89
+ expect(url.searchParams.get('code_challenge_method')).toBe('S256');
90
+ expect(url.searchParams.get('state')).toBe(stateData?.id);
91
+ expect(url.searchParams.get('code_challenge')).toBe(stateData?.codeChallenge);
92
+ expect(url.searchParams.get('nonce')).toBe(stateData?.nonce);
93
+ expect(url.searchParams.get('prompt')).toBe('login');
94
+ expect(url.searchParams.get('display')).toBe('popup');
95
+ expect(url.searchParams.get('login_hint')).toBe('test@example.com');
96
+ expect(url.searchParams.get('acr_values')).toBe('urn:mace:incommon:iap:silver urn:mace:incommon:iap:bronze');
97
+ expect(url.searchParams.get('ui_locales')).toBe('en-US hu-HU');
98
+ expect(url.searchParams.get('audience')).toBe('https://api.example.com https://api2.example.com');
99
+ expect(params).toEqual(extraParams);
100
+ });
101
+ });
102
+
103
+ describe('w/ serverSessionUri', () => {
104
+ test('w/o params', async () => {
105
+ options = createOptions({ urlHandler: vi.fn().mockResolvedValue({}), serverSessionUri: 'https://app.example.com/login' });
106
+ flow = createPopupFlow(options);
107
+
108
+ await flow.login();
109
+
110
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), { display: 'popup' });
111
+
112
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
113
+
114
+ expect(`${url.origin}${url.pathname}`).toBe('https://app.example.com/login');
115
+ expect(url.searchParams.has('client_id')).toBeFalsy();
116
+ expect(url.searchParams.has('redirect_uri')).toBeFalsy();
117
+ expect(url.searchParams.has('response_type')).toBeFalsy();
118
+ expect(url.searchParams.has('response_mode')).toBeFalsy();
119
+ expect(url.searchParams.has('scope')).toBeFalsy();
120
+ expect(url.searchParams.has('code_challenge_method')).toBeFalsy();
121
+ expect(url.searchParams.has('state')).toBeFalsy();
122
+ expect(url.searchParams.has('code_challenge')).toBeFalsy();
123
+ expect(url.searchParams.has('nonce')).toBeFalsy();
124
+ expect(url.searchParams.has('prompt')).toBeFalsy();
125
+ expect(url.searchParams.get('display')).toBe('popup');
126
+ expect(url.searchParams.has('login_hint')).toBeFalsy();
127
+ expect(url.searchParams.has('acr_values')).toBeFalsy();
128
+ expect(url.searchParams.has('ui_locales')).toBeFalsy();
129
+ expect(url.searchParams.has('audience')).toBeFalsy();
130
+ expect(params).toEqual({ display: 'popup' });
131
+ });
132
+
133
+ test('w/ params', async () => {
134
+ options = createOptions({ urlHandler: vi.fn().mockResolvedValue({}), serverSessionUri: 'https://app.example.com/login' });
135
+ flow = createPopupFlow(options);
136
+
137
+ const extraParams = {
138
+ prompt: 'login',
139
+ display: 'page',
140
+ loginHint: 'test@example.com',
141
+ acrValues: ['urn:mace:incommon:iap:silver', 'urn:mace:incommon:iap:bronze'],
142
+ uiLocales: ['en-US', 'hu-HU'],
143
+ audiences: ['https://api.example.com', 'https://api2.example.com'],
144
+ };
145
+
146
+ await flow.login(extraParams);
147
+
148
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), extraParams);
149
+
150
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
151
+
152
+ expect(`${url.origin}${url.pathname}`).toBe('https://app.example.com/login');
153
+ expect(url.searchParams.has('client_id')).toBeFalsy();
154
+ expect(url.searchParams.has('redirect_uri')).toBeFalsy();
155
+ expect(url.searchParams.has('response_type')).toBeFalsy();
156
+ expect(url.searchParams.has('response_mode')).toBeFalsy();
157
+ expect(url.searchParams.has('scope')).toBeFalsy();
158
+ expect(url.searchParams.has('code_challenge_method')).toBeFalsy();
159
+ expect(url.searchParams.has('state')).toBeFalsy();
160
+ expect(url.searchParams.has('code_challenge')).toBeFalsy();
161
+ expect(url.searchParams.has('nonce')).toBeFalsy();
162
+ expect(url.searchParams.get('prompt')).toBe('login');
163
+ expect(url.searchParams.get('display')).toBe('popup');
164
+ expect(url.searchParams.get('login_hint')).toBe('test@example.com');
165
+ expect(url.searchParams.get('acr_values')).toBe('urn:mace:incommon:iap:silver urn:mace:incommon:iap:bronze');
166
+ expect(url.searchParams.get('ui_locales')).toBe('en-US hu-HU');
167
+ expect(url.searchParams.get('audience')).toBe('https://api.example.com https://api2.example.com');
168
+ expect(params).toEqual(extraParams);
169
+ });
170
+ });
171
+
172
+ test('should exchange the code for tokens when serverSessionUri is not configured', async () => {
173
+ worker.use(
174
+ http.post('https://brandtegrity.io/oauth2/token', () => HttpResponse.json({ access_token: 'access-token', token_type: 'Bearer', expires_in: 3600 })),
175
+ );
176
+
177
+ options = createOptions({
178
+ lazyLoad: true,
179
+ urlHandler: vi.fn((url) => Promise.resolve({ code: 'abc123', state: new URL(url).searchParams.get('state') })),
180
+ });
181
+ flow = createPopupFlow(options);
182
+
183
+ await flow.login();
184
+
185
+ expect(flow.accessToken).toBe('access-token');
186
+ });
187
+
188
+ test('should not exchange the code for tokens when serverSessionUri is configured', async () => {
189
+ options = createOptions({
190
+ lazyLoad: true,
191
+ serverSessionUri: 'https://app.example.com/login',
192
+ urlHandler: vi.fn().mockResolvedValue({ code: 'abc123' }),
193
+ });
194
+ flow = createPopupFlow(options);
195
+
196
+ await flow.login();
197
+
198
+ expect(flow.accessToken).toBeNull();
199
+ });
200
+ });
201
+
202
+ describe('register', () => {
203
+ describe('w/o serverSessionUri', () => {
204
+ test('w/o params', async () => {
205
+ await flow.register().catch(() => undefined);
206
+
207
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), { prompt: 'create', display: 'popup' });
208
+
209
+ const stateData = getMockStorage(options.stateStorage).getLastState();
210
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
211
+
212
+ expect(`${url.origin}${url.pathname}`).toBe('https://brandtegrity.io/oauth2/auth');
213
+ expect(url.searchParams.get('client_id')).toBe('client-id');
214
+ expect(url.searchParams.get('redirect_uri')).toBe('https://brandtegrity.io/callback');
215
+ expect(url.searchParams.get('response_type')).toBe('code');
216
+ expect(url.searchParams.get('response_mode')).toBe('query');
217
+ expect(url.searchParams.get('scope')).toBe('openid');
218
+ expect(url.searchParams.get('code_challenge_method')).toBe('S256');
219
+ expect(url.searchParams.get('state')).toBe(stateData?.id);
220
+ expect(url.searchParams.get('code_challenge')).toBe(stateData?.codeChallenge);
221
+ expect(url.searchParams.get('nonce')).toBe(stateData?.nonce);
222
+ expect(url.searchParams.get('prompt')).toBe('create');
223
+ expect(url.searchParams.get('display')).toBe('popup');
224
+ expect(url.searchParams.has('login_hint')).toBeFalsy();
225
+ expect(url.searchParams.has('acr_values')).toBeFalsy();
226
+ expect(url.searchParams.has('ui_locales')).toBeFalsy();
227
+ expect(url.searchParams.has('audience')).toBeFalsy();
228
+ expect(params).toEqual({ prompt: 'create', display: 'popup' });
229
+ });
230
+
231
+ test('w/ params', async () => {
232
+ const extraParams = {
233
+ prompt: 'create',
234
+ display: 'popup',
235
+ loginHint: 'test@example.com',
236
+ acrValues: ['urn:mace:incommon:iap:silver', 'urn:mace:incommon:iap:bronze'],
237
+ uiLocales: ['en-US', 'hu-HU'],
238
+ audiences: ['https://api.example.com', 'https://api2.example.com'],
239
+ };
240
+
241
+ await flow.register({ ...extraParams, prompt: undefined }).catch(() => undefined);
242
+
243
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), extraParams);
244
+
245
+ const stateData = getMockStorage(options.stateStorage).getLastState();
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://brandtegrity.io/oauth2/auth');
249
+ expect(url.searchParams.get('client_id')).toBe('client-id');
250
+ expect(url.searchParams.get('redirect_uri')).toBe('https://brandtegrity.io/callback');
251
+ expect(url.searchParams.get('response_type')).toBe('code');
252
+ expect(url.searchParams.get('response_mode')).toBe('query');
253
+ expect(url.searchParams.get('scope')).toBe('openid');
254
+ expect(url.searchParams.get('code_challenge_method')).toBe('S256');
255
+ expect(url.searchParams.get('state')).toBe(stateData?.id);
256
+ expect(url.searchParams.get('code_challenge')).toBe(stateData?.codeChallenge);
257
+ expect(url.searchParams.get('nonce')).toBe(stateData?.nonce);
258
+ expect(url.searchParams.get('prompt')).toBe('create');
259
+ expect(url.searchParams.get('display')).toBe('popup');
260
+ expect(url.searchParams.get('login_hint')).toBe('test@example.com');
261
+ expect(url.searchParams.get('acr_values')).toBe('urn:mace:incommon:iap:silver urn:mace:incommon:iap:bronze');
262
+ expect(url.searchParams.get('ui_locales')).toBe('en-US hu-HU');
263
+ expect(url.searchParams.get('audience')).toBe('https://api.example.com https://api2.example.com');
264
+ expect(params).toEqual(extraParams);
265
+ });
266
+ });
267
+
268
+ describe('w/ serverSessionUri', () => {
269
+ test('w/o params', async () => {
270
+ options = createOptions({ urlHandler: vi.fn().mockResolvedValue({}), serverSessionUri: 'https://app.example.com/login' });
271
+ flow = createPopupFlow(options);
272
+
273
+ await flow.register();
274
+
275
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), { prompt: 'create', display: 'popup' });
276
+
277
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
278
+
279
+ expect(`${url.origin}${url.pathname}`).toBe('https://app.example.com/login');
280
+ expect(url.searchParams.has('client_id')).toBeFalsy();
281
+ expect(url.searchParams.has('redirect_uri')).toBeFalsy();
282
+ expect(url.searchParams.has('response_type')).toBeFalsy();
283
+ expect(url.searchParams.has('response_mode')).toBeFalsy();
284
+ expect(url.searchParams.has('scope')).toBeFalsy();
285
+ expect(url.searchParams.has('code_challenge_method')).toBeFalsy();
286
+ expect(url.searchParams.has('state')).toBeFalsy();
287
+ expect(url.searchParams.has('code_challenge')).toBeFalsy();
288
+ expect(url.searchParams.has('nonce')).toBeFalsy();
289
+ expect(url.searchParams.get('prompt')).toBe('create');
290
+ expect(url.searchParams.get('display')).toBe('popup');
291
+ expect(url.searchParams.has('login_hint')).toBeFalsy();
292
+ expect(url.searchParams.has('acr_values')).toBeFalsy();
293
+ expect(url.searchParams.has('ui_locales')).toBeFalsy();
294
+ expect(url.searchParams.has('audience')).toBeFalsy();
295
+ expect(params).toEqual({ prompt: 'create', display: 'popup' });
296
+ });
297
+
298
+ test('w/ params', async () => {
299
+ options = createOptions({ urlHandler: vi.fn().mockResolvedValue({}), serverSessionUri: 'https://app.example.com/login' });
300
+ flow = createPopupFlow(options);
301
+
302
+ const extraParams = {
303
+ prompt: 'create',
304
+ display: 'popup',
305
+ loginHint: 'test@example.com',
306
+ acrValues: ['urn:mace:incommon:iap:silver', 'urn:mace:incommon:iap:bronze'],
307
+ uiLocales: ['en-US', 'hu-HU'],
308
+ audiences: ['https://api.example.com', 'https://api2.example.com'],
309
+ };
310
+
311
+ await flow.register({ ...extraParams, prompt: undefined });
312
+
313
+ expect(flow.options.urlHandler).toHaveBeenCalledWith(expect.any(URL), extraParams);
314
+
315
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0] as [URL, Record<string, unknown>];
316
+
317
+ expect(`${url.origin}${url.pathname}`).toBe('https://app.example.com/login');
318
+ expect(url.searchParams.has('client_id')).toBeFalsy();
319
+ expect(url.searchParams.has('redirect_uri')).toBeFalsy();
320
+ expect(url.searchParams.has('response_type')).toBeFalsy();
321
+ expect(url.searchParams.has('response_mode')).toBeFalsy();
322
+ expect(url.searchParams.has('scope')).toBeFalsy();
323
+ expect(url.searchParams.has('code_challenge_method')).toBeFalsy();
324
+ expect(url.searchParams.has('state')).toBeFalsy();
325
+ expect(url.searchParams.has('code_challenge')).toBeFalsy();
326
+ expect(url.searchParams.has('nonce')).toBeFalsy();
327
+ expect(url.searchParams.get('prompt')).toBe('create');
328
+ expect(url.searchParams.get('display')).toBe('popup');
329
+ expect(url.searchParams.get('login_hint')).toBe('test@example.com');
330
+ expect(url.searchParams.get('acr_values')).toBe('urn:mace:incommon:iap:silver urn:mace:incommon:iap:bronze');
331
+ expect(url.searchParams.get('ui_locales')).toBe('en-US hu-HU');
332
+ expect(url.searchParams.get('audience')).toBe('https://api.example.com https://api2.example.com');
333
+ expect(params).toEqual(extraParams);
334
+ });
335
+ });
336
+ });
337
+
338
+ describe('entry', () => {
339
+ test('should redirect to the provider entry endpoint preserving query parameters', async () => {
340
+ await flow.entry('https://brandtegrity.io/entry?challenge=abc123&foo=bar');
341
+
342
+ const [url, params] = vi.mocked(options.urlHandler).mock.calls[0];
343
+
344
+ expect((url as URL).toString()).toBe('https://brandtegrity.io/provider/entry?challenge=abc123&foo=bar');
345
+ expect(params).toBeUndefined();
346
+ });
347
+
348
+ test('should default to the current window location when no url is provided', async () => {
349
+ window.location.search = '?challenge=abc123&foo=bar';
350
+
351
+ await flow.entry();
352
+
353
+ const [url] = vi.mocked(options.urlHandler).mock.calls[0];
354
+
355
+ expect((url as URL).toString()).toBe('https://brandtegrity.io/provider/entry?challenge=abc123&foo=bar');
356
+ });
357
+ });
358
+
359
+ test('flow state accessors', async () => {
360
+ options = createOptions({ lazyLoad: true, urlHandler: vi.fn().mockResolvedValue({ code: 'abc123' }) });
361
+ flow = createPopupFlow(options);
362
+
363
+ expect(flow.options).toBe(options);
364
+ expect(flow.initialized).toBeFalsy();
365
+ expect(flow.storage).toBe(options.storage);
366
+ expect(flow.httpClient).toBe(options.httpClient);
367
+ expect(flow.logging).toBe(options.logging);
368
+ expect(flow.sessionId).toBeNull();
369
+ expect(flow.shortAppId).toBeNull();
370
+ expect(flow.language).toBe('en-US');
371
+ await expect(flow.isAuthenticated).resolves.toBeFalsy();
372
+ expect(flow.isAuthenticatedSync).toBeFalsy();
373
+ expect(flow.accessToken).toBeNull();
374
+ expect(flow.accessTokenExpired).toBeTruthy();
375
+ expect(flow.accessTokenExpirationDate).toBeNull();
376
+ expect(flow.refreshToken).toBeNull();
377
+ expect(flow.idToken).toBeNull();
378
+ expect(flow.idTokenClaims).toBeNull();
379
+
380
+ await flow.init();
381
+
382
+ flow.sessionId = 'abc123';
383
+ flow.shortAppId = 'app';
384
+ flow.language = 'hu-HU';
385
+ flow.session = getMockStorage(options.storage).generateSession();
386
+
387
+ expect(flow.initialized).toBeTruthy();
388
+ expect(flow.sessionId).toBe('abc123');
389
+ expect(flow.shortAppId).toBe('app');
390
+ expect(flow.language).toBe('hu-HU');
391
+ await expect(flow.isAuthenticated).resolves.toBeTruthy();
392
+ expect(flow.isAuthenticatedSync).toBeTruthy();
393
+ expect(flow.accessToken).toBe(flow.session.access_token);
394
+ expect(flow.accessTokenExpired).toBeFalsy();
395
+ expect(flow.accessTokenExpirationDate).toBe(flow.session.expires_at);
396
+ expect(flow.refreshToken).toBe(flow.session.refresh_token);
397
+ expect(flow.idToken).toBe(flow.session.id_token);
398
+ expect(flow.idTokenClaims).toBe(flow.session?.claims);
399
+ });
400
+ });