oidc-spa 8.1.14 → 8.2.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 (111) hide show
  1. package/core/BASE_URL.d.ts +4 -0
  2. package/core/BASE_URL.js +12 -0
  3. package/core/BASE_URL.js.map +1 -0
  4. package/core/createOidc.d.ts +14 -7
  5. package/core/createOidc.js +38 -3
  6. package/core/createOidc.js.map +1 -1
  7. package/core/earlyInit.d.ts +1 -0
  8. package/core/earlyInit.js +8 -8
  9. package/core/earlyInit.js.map +1 -1
  10. package/core/prShouldLoadApp.d.ts +4 -0
  11. package/core/prShouldLoadApp.js +13 -0
  12. package/core/prShouldLoadApp.js.map +1 -0
  13. package/esm/core/BASE_URL.d.ts +4 -0
  14. package/esm/core/BASE_URL.js +8 -0
  15. package/esm/core/BASE_URL.js.map +1 -0
  16. package/esm/core/createOidc.d.ts +14 -7
  17. package/esm/core/createOidc.js +38 -3
  18. package/esm/core/createOidc.js.map +1 -1
  19. package/esm/core/earlyInit.d.ts +1 -0
  20. package/esm/core/earlyInit.js +8 -8
  21. package/esm/core/earlyInit.js.map +1 -1
  22. package/esm/core/prShouldLoadApp.d.ts +4 -0
  23. package/esm/core/prShouldLoadApp.js +9 -0
  24. package/esm/core/prShouldLoadApp.js.map +1 -0
  25. package/esm/keycloak/keycloak-js/Keycloak.d.ts +1 -1
  26. package/esm/keycloak/keycloak-js/Keycloak.js +1 -1
  27. package/esm/keycloak/keycloak-js/Keycloak.js.map +1 -1
  28. package/esm/mock/oidc.d.ts +3 -1
  29. package/esm/mock/oidc.js +4 -2
  30. package/esm/mock/oidc.js.map +1 -1
  31. package/esm/react-spa/apiBuilder.d.ts +12 -0
  32. package/esm/react-spa/apiBuilder.js +26 -0
  33. package/esm/react-spa/apiBuilder.js.map +1 -0
  34. package/esm/react-spa/createOidcSpaApi.d.ts +8 -0
  35. package/esm/react-spa/createOidcSpaApi.js +387 -0
  36. package/esm/react-spa/createOidcSpaApi.js.map +1 -0
  37. package/esm/react-spa/index.d.ts +2 -0
  38. package/esm/react-spa/index.js +3 -0
  39. package/esm/react-spa/index.js.map +1 -0
  40. package/esm/react-spa/types.d.ts +279 -0
  41. package/esm/react-spa/types.js +2 -0
  42. package/esm/react-spa/types.js.map +1 -0
  43. package/esm/tanstack-start/react/apiBuilder.js.map +1 -1
  44. package/esm/tanstack-start/react/createOidcSpaApi.js +13 -9
  45. package/esm/tanstack-start/react/createOidcSpaApi.js.map +1 -1
  46. package/esm/tanstack-start/react/types.d.ts +5 -4
  47. package/keycloak/keycloak-js/Keycloak.d.ts +1 -1
  48. package/keycloak/keycloak-js/Keycloak.js +1 -1
  49. package/keycloak/keycloak-js/Keycloak.js.map +1 -1
  50. package/mock/oidc.d.ts +3 -1
  51. package/mock/oidc.js +4 -2
  52. package/mock/oidc.js.map +1 -1
  53. package/package.json +5 -1
  54. package/react-spa/apiBuilder.d.ts +12 -0
  55. package/react-spa/apiBuilder.js +29 -0
  56. package/react-spa/apiBuilder.js.map +1 -0
  57. package/react-spa/createOidcSpaApi.d.ts +8 -0
  58. package/react-spa/createOidcSpaApi.js +423 -0
  59. package/react-spa/createOidcSpaApi.js.map +1 -0
  60. package/react-spa/index.d.ts +2 -0
  61. package/react-spa/index.js +6 -0
  62. package/react-spa/index.js.map +1 -0
  63. package/react-spa/types.d.ts +279 -0
  64. package/react-spa/types.js +3 -0
  65. package/react-spa/types.js.map +1 -0
  66. package/src/angular.ts +1 -1
  67. package/src/core/BASE_URL.ts +9 -0
  68. package/src/core/createOidc.ts +64 -10
  69. package/src/core/earlyInit.ts +14 -11
  70. package/src/core/prShouldLoadApp.ts +11 -0
  71. package/src/keycloak/keycloak-js/Keycloak.ts +2 -2
  72. package/src/mock/oidc.ts +9 -3
  73. package/src/react-spa/apiBuilder.ts +70 -0
  74. package/src/react-spa/createOidcSpaApi.tsx +527 -0
  75. package/src/react-spa/index.ts +4 -0
  76. package/src/react-spa/types.tsx +308 -0
  77. package/src/tanstack-start/react/apiBuilder.ts +0 -1
  78. package/src/tanstack-start/react/createOidcSpaApi.tsx +24 -20
  79. package/src/tanstack-start/react/types.tsx +3 -4
  80. package/src/vite-plugin/handleClientEntrypoint.ts +5 -5
  81. package/src/vite-plugin/manageOptimizedDeps.ts +64 -0
  82. package/src/vite-plugin/projectType.ts +18 -0
  83. package/src/vite-plugin/vite-plugin.ts +47 -11
  84. package/vite-plugin/handleClientEntrypoint.d.ts +2 -0
  85. package/vite-plugin/handleClientEntrypoint.js +3 -4
  86. package/vite-plugin/handleClientEntrypoint.js.map +1 -1
  87. package/vite-plugin/manageOptimizedDeps.d.ts +6 -0
  88. package/vite-plugin/{excludeModuleExportFromOptimizedDeps.js → manageOptimizedDeps.js} +42 -7
  89. package/vite-plugin/manageOptimizedDeps.js.map +1 -0
  90. package/vite-plugin/projectType.d.ts +4 -0
  91. package/vite-plugin/projectType.js +15 -0
  92. package/vite-plugin/projectType.js.map +1 -0
  93. package/vite-plugin/{transformCreateFileRoute.js → transformTanstackRouterCreateFileRoute.js} +1 -1
  94. package/vite-plugin/transformTanstackRouterCreateFileRoute.js.map +1 -0
  95. package/vite-plugin/vite-plugin.d.ts +2 -2
  96. package/vite-plugin/vite-plugin.js +33 -9
  97. package/vite-plugin/vite-plugin.js.map +1 -1
  98. package/esm/tools/infer_import_meta_env_BASE_URL.d.ts +0 -1
  99. package/esm/tools/infer_import_meta_env_BASE_URL.js +0 -15
  100. package/esm/tools/infer_import_meta_env_BASE_URL.js.map +0 -1
  101. package/src/tools/infer_import_meta_env_BASE_URL.ts +0 -19
  102. package/src/vite-plugin/detectProjectType.ts +0 -20
  103. package/src/vite-plugin/excludeModuleExportFromOptimizedDeps.ts +0 -20
  104. package/vite-plugin/detectProjectType.d.ts +0 -10
  105. package/vite-plugin/detectProjectType.js +0 -15
  106. package/vite-plugin/detectProjectType.js.map +0 -1
  107. package/vite-plugin/excludeModuleExportFromOptimizedDeps.d.ts +0 -4
  108. package/vite-plugin/excludeModuleExportFromOptimizedDeps.js.map +0 -1
  109. package/vite-plugin/transformCreateFileRoute.js.map +0 -1
  110. /package/src/vite-plugin/{transformCreateFileRoute.ts → transformTanstackRouterCreateFileRoute.ts} +0 -0
  111. /package/vite-plugin/{transformCreateFileRoute.d.ts → transformTanstackRouterCreateFileRoute.d.ts} +0 -0
@@ -0,0 +1,527 @@
1
+ import { useState, useEffect, type ReactNode, type ComponentType } from "react";
2
+ import type { UseOidc, OidcSpaApi, GetOidc, ParamsOfBootstrap } from "./types";
3
+ import type { ZodSchemaLike } from "../tools/ZodSchemaLike";
4
+ import type { Oidc as Oidc_core } from "../core";
5
+ import { OidcInitializationError } from "../core/OidcInitializationError";
6
+ import { Deferred } from "../tools/Deferred";
7
+ import { isBrowser } from "../tools/isBrowser";
8
+ import { assert, type Equals } from "../tools/tsafe/assert";
9
+ import { createObjectThatThrowsIfAccessed } from "../tools/createObjectThatThrowsIfAccessed";
10
+ import { createStatefulEvt } from "../tools/StatefulEvt";
11
+ import { id } from "../tools/tsafe/id";
12
+ import { toFullyQualifiedUrl } from "../tools/toFullyQualifiedUrl";
13
+
14
+ export function createOidcSpaApi<
15
+ AutoLogin extends boolean,
16
+ DecodedIdToken extends Record<string, unknown>
17
+ >(params: {
18
+ autoLogin: AutoLogin;
19
+ decodedIdTokenSchema:
20
+ | ZodSchemaLike<Oidc_core.Tokens.DecodedIdToken_OidcCoreSpec, DecodedIdToken>
21
+ | undefined;
22
+ decodedIdToken_mock: DecodedIdToken | undefined;
23
+ }): OidcSpaApi<AutoLogin, DecodedIdToken> {
24
+ const { autoLogin, decodedIdTokenSchema, decodedIdToken_mock } = params;
25
+
26
+ const dParamsOfBootstrap = new Deferred<ParamsOfBootstrap<AutoLogin, DecodedIdToken>>();
27
+
28
+ const dOidcCoreOrInitializationError = new Deferred<
29
+ Oidc_core<DecodedIdToken> | OidcInitializationError
30
+ >();
31
+
32
+ const evtAutoLogoutState = createStatefulEvt<UseOidc.Oidc.LoggedIn<unknown>["autoLogoutState"]>(
33
+ () => ({
34
+ shouldDisplayWarning: false
35
+ })
36
+ );
37
+
38
+ dOidcCoreOrInitializationError.pr.then(oidcCoreOrInitializationError => {
39
+ const { hasResolved, value: paramsOfBootstrap } = dParamsOfBootstrap.getState();
40
+
41
+ assert(hasResolved);
42
+
43
+ if (paramsOfBootstrap.implementation === "mock") {
44
+ return;
45
+ }
46
+ assert<Equals<typeof paramsOfBootstrap.implementation, "real">>;
47
+
48
+ const { startCountdownSecondsBeforeAutoLogout = 45 } = paramsOfBootstrap;
49
+
50
+ if (
51
+ oidcCoreOrInitializationError === undefined ||
52
+ oidcCoreOrInitializationError instanceof OidcInitializationError
53
+ ) {
54
+ return;
55
+ }
56
+
57
+ const oidcCore = oidcCoreOrInitializationError;
58
+
59
+ if (!oidcCore.isUserLoggedIn) {
60
+ return;
61
+ }
62
+
63
+ oidcCore.subscribeToAutoLogoutCountdown(({ secondsLeft }) => {
64
+ const newState: UseOidc.Oidc.LoggedIn<unknown>["autoLogoutState"] = (() => {
65
+ if (secondsLeft === undefined) {
66
+ return {
67
+ shouldDisplayWarning: false
68
+ };
69
+ }
70
+
71
+ if (secondsLeft > startCountdownSecondsBeforeAutoLogout) {
72
+ return {
73
+ shouldDisplayWarning: false
74
+ };
75
+ }
76
+
77
+ return {
78
+ shouldDisplayWarning: true,
79
+ secondsLeftBeforeAutoLogout: secondsLeft
80
+ };
81
+ })();
82
+
83
+ if (!newState.shouldDisplayWarning && !evtAutoLogoutState.current.shouldDisplayWarning) {
84
+ return;
85
+ }
86
+
87
+ evtAutoLogoutState.current = newState;
88
+ });
89
+ });
90
+
91
+ function useOidc(params?: {
92
+ assert?: "user logged in" | "user not logged in";
93
+ }): UseOidc.Oidc<DecodedIdToken> {
94
+ const { assert: assert_params } = params ?? {};
95
+
96
+ if (!isBrowser) {
97
+ throw new Error("oidc-spa useOidc() can't be used on the server");
98
+ }
99
+
100
+ const { hasResolved, value: oidcCore } = dOidcCoreOrInitializationError.getState();
101
+
102
+ if (!hasResolved) {
103
+ throw dOidcCoreOrInitializationError.pr;
104
+ }
105
+
106
+ if (oidcCore instanceof OidcInitializationError) {
107
+ throw oidcCore;
108
+ }
109
+
110
+ check_assertion: {
111
+ if (assert_params === undefined) {
112
+ break check_assertion;
113
+ }
114
+
115
+ const getMessage = (v: string) =>
116
+ [
117
+ "There is a logic error in the application.",
118
+ `If this component is mounted the user is supposed ${v}.`,
119
+ "An explicit assertion was made in this sense."
120
+ ].join(" ");
121
+
122
+ switch (assert_params) {
123
+ case "user logged in":
124
+ if (!oidcCore.isUserLoggedIn) {
125
+ throw new Error(getMessage("to be logged in but currently they arn't"));
126
+ }
127
+ break;
128
+ case "user not logged in":
129
+ if (oidcCore.isUserLoggedIn) {
130
+ throw new Error(getMessage("not to be logged in but currently they are"));
131
+ }
132
+ break;
133
+ default:
134
+ assert<Equals<typeof assert_params, never>>(false);
135
+ }
136
+ }
137
+
138
+ const [, reRenderIfDecodedIdTokenChanged] = useState<DecodedIdToken | undefined>(() => {
139
+ if (!oidcCore.isUserLoggedIn) {
140
+ return undefined;
141
+ }
142
+ return oidcCore.getDecodedIdToken();
143
+ });
144
+
145
+ const [evtIsDecodedIdTokenUsed] = useState(() => createStatefulEvt<boolean>(() => false));
146
+
147
+ useEffect(() => {
148
+ if (!oidcCore.isUserLoggedIn) {
149
+ return;
150
+ }
151
+
152
+ let isActive = true;
153
+
154
+ let unsubscribe: (() => void) | undefined = undefined;
155
+
156
+ (async () => {
157
+ if (!evtIsDecodedIdTokenUsed.current) {
158
+ const dDecodedIdTokenUsed = new Deferred<void>();
159
+
160
+ const { unsubscribe: unsubscribe_scope } = evtIsDecodedIdTokenUsed.subscribe(() => {
161
+ unsubscribe_scope();
162
+ dDecodedIdTokenUsed.resolve();
163
+ });
164
+ unsubscribe = unsubscribe_scope;
165
+
166
+ await dDecodedIdTokenUsed.pr;
167
+
168
+ if (!isActive) {
169
+ return;
170
+ }
171
+ }
172
+
173
+ reRenderIfDecodedIdTokenChanged(oidcCore.getDecodedIdToken());
174
+
175
+ unsubscribe = oidcCore.subscribeToTokensChange(() => {
176
+ reRenderIfDecodedIdTokenChanged(oidcCore.getDecodedIdToken());
177
+ }).unsubscribe;
178
+ })();
179
+
180
+ return () => {
181
+ isActive = false;
182
+ unsubscribe?.();
183
+ };
184
+ }, []);
185
+
186
+ const [evtIsAutoLogoutStateUsed] = useState(() => createStatefulEvt<boolean>(() => false));
187
+
188
+ const [, reRenderIfAutoLogoutStateChanged] = useState(() => evtAutoLogoutState.current);
189
+
190
+ useEffect(() => {
191
+ let isActive = true;
192
+ let unsubscribe: (() => void) | undefined = undefined;
193
+
194
+ (async () => {
195
+ if (!evtIsAutoLogoutStateUsed.current) {
196
+ const dAutoLogoutStateUsed = new Deferred<void>();
197
+
198
+ const { unsubscribe: unsubscribe_scope } = evtIsAutoLogoutStateUsed.subscribe(() => {
199
+ unsubscribe_scope();
200
+ dAutoLogoutStateUsed.resolve();
201
+ });
202
+ unsubscribe = unsubscribe_scope;
203
+
204
+ await dAutoLogoutStateUsed.pr;
205
+
206
+ if (!isActive) {
207
+ return;
208
+ }
209
+ }
210
+
211
+ reRenderIfAutoLogoutStateChanged(evtAutoLogoutState.current);
212
+
213
+ unsubscribe = evtAutoLogoutState.subscribe(reRenderIfAutoLogoutStateChanged).unsubscribe;
214
+ })();
215
+
216
+ return () => {
217
+ isActive = false;
218
+ unsubscribe?.();
219
+ };
220
+ }, []);
221
+
222
+ if (!oidcCore.isUserLoggedIn) {
223
+ return id<UseOidc.Oidc.NotLoggedIn>({
224
+ isUserLoggedIn: false,
225
+ initializationError: oidcCore.initializationError,
226
+ issuerUri: oidcCore.params.issuerUri,
227
+ clientId: oidcCore.params.clientId,
228
+ autoLogoutState: { shouldDisplayWarning: false },
229
+ login: params =>
230
+ oidcCore.login({
231
+ doesCurrentHrefRequiresAuth: false,
232
+ ...params
233
+ })
234
+ });
235
+ }
236
+
237
+ return id<UseOidc.Oidc.LoggedIn<DecodedIdToken>>({
238
+ isUserLoggedIn: true,
239
+ get decodedIdToken() {
240
+ evtIsDecodedIdTokenUsed.current = true;
241
+ return oidcCore.getDecodedIdToken();
242
+ },
243
+ logout: oidcCore.logout,
244
+ renewTokens: oidcCore.renewTokens,
245
+ goToAuthServer: oidcCore.goToAuthServer,
246
+ backFromAuthServer: oidcCore.backFromAuthServer,
247
+ isNewBrowserSession: oidcCore.isNewBrowserSession,
248
+ get autoLogoutState() {
249
+ evtIsAutoLogoutStateUsed.current = true;
250
+ return evtAutoLogoutState.current;
251
+ },
252
+ issuerUri: oidcCore.params.issuerUri,
253
+ clientId: oidcCore.params.clientId
254
+ });
255
+ }
256
+
257
+ async function getOidc(params?: {
258
+ assert?: "user logged in" | "user not logged in";
259
+ }): Promise<GetOidc.Oidc<DecodedIdToken>> {
260
+ if (!isBrowser) {
261
+ throw new Error("oidc-spa: getOidc() can't be used on the server");
262
+ }
263
+
264
+ const oidcCore = await dOidcCoreOrInitializationError.pr;
265
+
266
+ if (oidcCore instanceof OidcInitializationError) {
267
+ return new Promise<never>(() => {});
268
+ }
269
+
270
+ if (params?.assert === "user logged in" && !oidcCore.isUserLoggedIn) {
271
+ throw new Error(
272
+ [
273
+ "oidc-spa: Called getOidc({ assert: 'user logged in' })",
274
+ "but the user is not currently logged in."
275
+ ].join(" ")
276
+ );
277
+ }
278
+ if (params?.assert === "user not logged in" && oidcCore.isUserLoggedIn) {
279
+ throw new Error(
280
+ [
281
+ "oidc-spa: Called getOidc({ assert: 'user not logged in' })",
282
+ "but the user is currently logged in."
283
+ ].join(" ")
284
+ );
285
+ }
286
+
287
+ return oidcCore.isUserLoggedIn
288
+ ? id<GetOidc.Oidc.LoggedIn<DecodedIdToken>>({
289
+ issuerUri: oidcCore.params.issuerUri,
290
+ clientId: oidcCore.params.clientId,
291
+ isUserLoggedIn: true,
292
+ getAccessToken: async () => {
293
+ const { accessToken } = await oidcCore.getTokens();
294
+ return accessToken;
295
+ },
296
+ getDecodedIdToken: oidcCore.getDecodedIdToken,
297
+ logout: oidcCore.logout,
298
+ renewTokens: oidcCore.renewTokens,
299
+ goToAuthServer: oidcCore.goToAuthServer,
300
+ backFromAuthServer: oidcCore.backFromAuthServer,
301
+ isNewBrowserSession: oidcCore.isNewBrowserSession,
302
+ subscribeToAutoLogoutState: next => {
303
+ next(evtAutoLogoutState.current);
304
+
305
+ const { unsubscribe } = evtAutoLogoutState.subscribe(next);
306
+
307
+ return { unsubscribeFromAutoLogoutState: unsubscribe };
308
+ }
309
+ })
310
+ : id<GetOidc.Oidc.NotLoggedIn>({
311
+ issuerUri: oidcCore.params.issuerUri,
312
+ clientId: oidcCore.params.clientId,
313
+ isUserLoggedIn: false,
314
+ initializationError: oidcCore.initializationError,
315
+ login: oidcCore.login
316
+ });
317
+ }
318
+
319
+ let hasBootstrapBeenCalled = false;
320
+
321
+ const prModuleCore = !isBrowser ? undefined : import("../core");
322
+
323
+ const bootstrapOidc = (paramsOfBootstrap: ParamsOfBootstrap<AutoLogin, DecodedIdToken>) => {
324
+ if (hasBootstrapBeenCalled) {
325
+ return;
326
+ }
327
+
328
+ hasBootstrapBeenCalled = true;
329
+
330
+ (async () => {
331
+ if (!isBrowser) {
332
+ return;
333
+ }
334
+
335
+ assert(prModuleCore !== undefined);
336
+
337
+ dParamsOfBootstrap.resolve(paramsOfBootstrap);
338
+
339
+ switch (paramsOfBootstrap.implementation) {
340
+ case "mock":
341
+ {
342
+ const { createMockOidc } = await import("../mock/oidc");
343
+
344
+ const oidcCore = await createMockOidc({
345
+ BASE_URL: paramsOfBootstrap.BASE_URL,
346
+ // NOTE: The `as false` is lying here, it's just to preserve some level of type-safety.
347
+ autoLogin: autoLogin as false,
348
+ // NOTE: Same here, the nullish coalescing is lying.
349
+ isUserInitiallyLoggedIn: paramsOfBootstrap.isUserInitiallyLoggedIn!,
350
+ mockedParams: {
351
+ clientId: paramsOfBootstrap.clientId_mock,
352
+ issuerUri: paramsOfBootstrap.issuerUri_mock
353
+ },
354
+ mockedTokens: {
355
+ decodedIdToken:
356
+ paramsOfBootstrap.decodedIdToken_mock ??
357
+ decodedIdToken_mock ??
358
+ createObjectThatThrowsIfAccessed<DecodedIdToken>({
359
+ debugMessage: [
360
+ "oidc-spa: You didn't provide any mock for the decodedIdToken",
361
+ "Either provide a default one by specifying decodedIdToken_mock",
362
+ "as parameter of .withExpectedDecodedIdTokenShape() or",
363
+ "specify decodedIdToken_mock when calling bootstrapOidc()"
364
+ ].join(" ")
365
+ })
366
+ }
367
+ });
368
+
369
+ dOidcCoreOrInitializationError.resolve(oidcCore);
370
+ }
371
+ break;
372
+ case "real":
373
+ {
374
+ const { createOidc } = await prModuleCore;
375
+
376
+ let oidcCoreOrInitializationError:
377
+ | Oidc_core<DecodedIdToken>
378
+ | OidcInitializationError;
379
+
380
+ try {
381
+ oidcCoreOrInitializationError = await createOidc({
382
+ BASE_URL: paramsOfBootstrap.BASE_URL,
383
+ autoLogin,
384
+ decodedIdTokenSchema,
385
+ issuerUri: paramsOfBootstrap.issuerUri,
386
+ clientId: paramsOfBootstrap.clientId,
387
+ idleSessionLifetimeInSeconds:
388
+ paramsOfBootstrap.idleSessionLifetimeInSeconds,
389
+ scopes: paramsOfBootstrap.scopes,
390
+ transformUrlBeforeRedirect: paramsOfBootstrap.transformUrlBeforeRedirect,
391
+ extraQueryParams: paramsOfBootstrap.extraQueryParams,
392
+ extraTokenParams: paramsOfBootstrap.extraTokenParams,
393
+ noIframe: paramsOfBootstrap.noIframe,
394
+ debugLogs: paramsOfBootstrap.debugLogs,
395
+ __unsafe_clientSecret: paramsOfBootstrap.__unsafe_clientSecret,
396
+ __metadata: paramsOfBootstrap.__metadata,
397
+ __unsafe_useIdTokenAsAccessToken:
398
+ paramsOfBootstrap.__unsafe_useIdTokenAsAccessToken
399
+ });
400
+ } catch (error) {
401
+ if (!(error instanceof OidcInitializationError)) {
402
+ throw error;
403
+ }
404
+ dOidcCoreOrInitializationError.resolve(error);
405
+ return;
406
+ }
407
+
408
+ dOidcCoreOrInitializationError.resolve(oidcCoreOrInitializationError);
409
+ }
410
+ break;
411
+ }
412
+ })();
413
+ };
414
+
415
+ async function enforceLogin(loaderContext: {
416
+ request?: { url?: string };
417
+ cause?: "preload" | string;
418
+ location?: {
419
+ href?: string;
420
+ };
421
+ }): Promise<void | never> {
422
+ if (!isBrowser) {
423
+ throw new Error("oidc-spa: getOidc() can't be used on the server");
424
+ }
425
+
426
+ const { cause } = loaderContext;
427
+
428
+ const redirectUrl = (() => {
429
+ if (loaderContext.request?.url !== undefined) {
430
+ return toFullyQualifiedUrl({
431
+ urlish: loaderContext.request.url,
432
+ doAssertNoQueryParams: false
433
+ });
434
+ }
435
+
436
+ if (loaderContext.location?.href !== undefined) {
437
+ return toFullyQualifiedUrl({
438
+ urlish: loaderContext.location.href,
439
+ doAssertNoQueryParams: false
440
+ });
441
+ }
442
+
443
+ return location.href;
444
+ })();
445
+
446
+ const oidc = await getOidc();
447
+
448
+ if (!oidc.isUserLoggedIn) {
449
+ if (cause === "preload") {
450
+ throw new Error(
451
+ "oidc-spa: User is not yet logged in. This is an expected error, nothing to be addressed."
452
+ );
453
+ }
454
+ const doesCurrentHrefRequiresAuth =
455
+ location.href.replace(/\/$/, "") === redirectUrl.replace(/\/$/, "");
456
+
457
+ await oidc.login({
458
+ redirectUrl,
459
+ doesCurrentHrefRequiresAuth
460
+ });
461
+ }
462
+ }
463
+
464
+ function OidcInitializationErrorGate(props: {
465
+ errorComponent: ComponentType<{
466
+ oidcInitializationError: OidcInitializationError;
467
+ }>;
468
+ children: ReactNode;
469
+ }): ReactNode {
470
+ const { errorComponent: ErrorComponent, children } = props;
471
+
472
+ const { hasResolved, value: oidcCoreOrOidcInitializationError } =
473
+ dOidcCoreOrInitializationError.getState();
474
+
475
+ if (!hasResolved) {
476
+ throw dOidcCoreOrInitializationError.pr;
477
+ }
478
+
479
+ if (oidcCoreOrOidcInitializationError instanceof OidcInitializationError) {
480
+ const oidcInitializationError = oidcCoreOrOidcInitializationError;
481
+
482
+ return <ErrorComponent oidcInitializationError={oidcInitializationError} />;
483
+ }
484
+
485
+ return children;
486
+ }
487
+
488
+ function withLoginEnforced<Props extends Record<string, unknown>>(
489
+ component: ComponentType<Props>
490
+ ): (props: Props) => ReactNode {
491
+ const Component = component;
492
+
493
+ function ComponentWithLoginEnforced(props: Props) {
494
+ const { hasResolved, value: oidcCore } = dOidcCoreOrInitializationError.getState();
495
+
496
+ if (!hasResolved) {
497
+ throw dOidcCoreOrInitializationError.pr;
498
+ }
499
+
500
+ if (oidcCore instanceof OidcInitializationError) {
501
+ throw oidcCore;
502
+ }
503
+
504
+ if (!oidcCore.isUserLoggedIn) {
505
+ throw oidcCore.login({ doesCurrentHrefRequiresAuth: true });
506
+ }
507
+
508
+ return <Component {...props} />;
509
+ }
510
+
511
+ ComponentWithLoginEnforced.displayName = `${
512
+ Component.displayName ?? Component.name ?? "Component"
513
+ }WithLoginEnforced`;
514
+
515
+ return ComponentWithLoginEnforced;
516
+ }
517
+
518
+ // @ts-expect-error
519
+ return {
520
+ bootstrapOidc,
521
+ useOidc,
522
+ getOidc,
523
+ OidcInitializationErrorGate,
524
+ enforceLogin,
525
+ withLoginEnforced
526
+ };
527
+ }
@@ -0,0 +1,4 @@
1
+ export type * from "./types";
2
+ import { oidcSpaApiBuilder } from "./apiBuilder";
3
+
4
+ export const oidcSpa = oidcSpaApiBuilder;