@tidecloak/react 0.13.11 → 0.13.14

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.
@@ -1,28 +1,133 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from "react";
3
- import { IAMService } from '@tidecloak/js';
3
+ import { IAMService, AdminAPI } from '@tidecloak/js';
4
4
  const TideCloakContext = React.createContext(undefined);
5
- export function TideCloakContextProvider({ config, children }) {
5
+ export function TideCloakContextProvider({ config: configProp, configUrl = '/adapter.json', authMode, adapter, children, onAuthSuccess, onAuthError, onLogout, onReauthRequired, onActionNotification }) {
6
+ // Bootstrap state
6
7
  const [isInitializing, setIsInitializing] = React.useState(true);
7
8
  const [initError, setInitError] = React.useState(null);
9
+ // Session state
8
10
  const [authenticated, setAuthenticated] = React.useState(false);
9
11
  const [sessionExpired, setSessionExpired] = React.useState(false);
10
12
  const [isRefreshing, setIsRefreshing] = React.useState(false);
13
+ const [isLoading, setIsLoading] = React.useState(false);
14
+ // Network state
15
+ const [isOffline, setIsOffline] = React.useState(typeof navigator !== 'undefined' ? !navigator.onLine : false);
16
+ const [wasOffline, setWasOffline] = React.useState(false);
17
+ // Re-auth state
18
+ const [needsReauth, setNeedsReauth] = React.useState(false);
19
+ // Token state
11
20
  const [token, setToken] = React.useState(null);
12
21
  const [idToken, setIdToken] = React.useState(null);
13
22
  const [tokenExp, setTokenExp] = React.useState(null);
23
+ // Config state
14
24
  const [baseURL, setBaseURL] = React.useState("");
15
25
  const [reloadKey, setReloadKey] = React.useState(0);
26
+ const [resolvedConfig, setResolvedConfig] = React.useState(null);
27
+ // Store callbacks in refs to avoid re-subscriptions
28
+ const onAuthSuccessRef = React.useRef(onAuthSuccess);
29
+ const onAuthErrorRef = React.useRef(onAuthError);
30
+ const onLogoutRef = React.useRef(onLogout);
31
+ const onReauthRequiredRef = React.useRef(onReauthRequired);
32
+ const onActionNotificationRef = React.useRef(onActionNotification);
16
33
  React.useEffect(() => {
34
+ onAuthSuccessRef.current = onAuthSuccess;
35
+ onAuthErrorRef.current = onAuthError;
36
+ onLogoutRef.current = onLogout;
37
+ onReauthRequiredRef.current = onReauthRequired;
38
+ onActionNotificationRef.current = onActionNotification;
39
+ }, [onAuthSuccess, onAuthError, onLogout, onReauthRequired, onActionNotification]);
40
+ // Network status tracking
41
+ React.useEffect(() => {
42
+ if (typeof window === 'undefined')
43
+ return;
44
+ const handleOnline = () => setIsOffline(false);
45
+ const handleOffline = () => {
46
+ setIsOffline(true);
47
+ setWasOffline(true);
48
+ };
49
+ window.addEventListener('online', handleOnline);
50
+ window.addEventListener('offline', handleOffline);
51
+ return () => {
52
+ window.removeEventListener('online', handleOnline);
53
+ window.removeEventListener('offline', handleOffline);
54
+ };
55
+ }, []);
56
+ // Resolve config - either use provided config or fetch from URL
57
+ React.useEffect(() => {
58
+ let mounted = true;
59
+ const resolveConfig = async () => {
60
+ // Validate native mode requirements
61
+ if (authMode === 'native' && !adapter) {
62
+ const err = new Error('[TideCloak] authMode="native" requires an adapter prop');
63
+ if (mounted) {
64
+ setInitError(err);
65
+ setIsInitializing(false);
66
+ }
67
+ return;
68
+ }
69
+ // If config is provided directly, use it
70
+ if (configProp) {
71
+ const finalConfig = {
72
+ ...configProp,
73
+ ...(authMode && { authMode }),
74
+ ...(adapter && { adapter }),
75
+ };
76
+ if (mounted) {
77
+ setResolvedConfig(finalConfig);
78
+ }
79
+ return;
80
+ }
81
+ // Otherwise, fetch from configUrl
82
+ try {
83
+ console.debug(`[TideCloak] Fetching config from ${configUrl}`);
84
+ const response = await fetch(configUrl);
85
+ if (!response.ok) {
86
+ throw new Error(`Failed to fetch config from ${configUrl}: ${response.status}`);
87
+ }
88
+ const fetchedConfig = await response.json();
89
+ const finalConfig = {
90
+ ...fetchedConfig,
91
+ ...(authMode && { authMode }),
92
+ ...(adapter && { adapter }),
93
+ };
94
+ if (mounted) {
95
+ console.debug('[TideCloak] Config loaded:', {
96
+ realm: finalConfig.realm,
97
+ authMode: finalConfig.authMode || 'frontchannel',
98
+ hasAdapter: !!finalConfig.adapter,
99
+ });
100
+ setResolvedConfig(finalConfig);
101
+ }
102
+ }
103
+ catch (err) {
104
+ if (mounted) {
105
+ console.error('[TideCloak] Failed to load config:', err);
106
+ setInitError(err instanceof Error ? err : new Error(String(err)));
107
+ setIsInitializing(false);
108
+ }
109
+ }
110
+ };
111
+ resolveConfig();
112
+ return () => {
113
+ mounted = false;
114
+ };
115
+ }, [configProp, configUrl, authMode, adapter]);
116
+ // Main IAMService initialization and event handling
117
+ React.useEffect(() => {
118
+ // Wait for config to be resolved
119
+ if (!resolvedConfig)
120
+ return;
17
121
  let mounted = true;
18
122
  const updateAuthState = async (eventName) => {
19
- console.debug(`[TideCloak Provider] Updating auth state. Triggered by the ${eventName} event`);
123
+ console.debug(`[TideCloak] Auth state update from: ${eventName}`);
20
124
  if (!mounted)
21
125
  return;
22
126
  const logged = IAMService.isLoggedIn();
23
127
  setAuthenticated(logged);
24
128
  if (logged) {
25
129
  setSessionExpired(false);
130
+ setNeedsReauth(false);
26
131
  try {
27
132
  const t = await IAMService.getToken();
28
133
  const idt = IAMService.getIDToken();
@@ -31,7 +136,7 @@ export function TideCloakContextProvider({ config, children }) {
31
136
  setTokenExp(IAMService.getTokenExp());
32
137
  }
33
138
  catch (e) {
34
- console.error("[TideCloak Provider] Failed to update auth state", e);
139
+ console.error("[TideCloak] Failed to get tokens:", e);
35
140
  }
36
141
  }
37
142
  else {
@@ -40,106 +145,462 @@ export function TideCloakContextProvider({ config, children }) {
40
145
  setIdToken(null);
41
146
  }
42
147
  };
43
- const onInitError = (err) => {
44
- if (mounted) {
45
- setInitError(err);
46
- setIsInitializing(false);
148
+ const handleAuthSuccess = async () => {
149
+ var _a;
150
+ if (!mounted)
151
+ return;
152
+ setIsLoading(false);
153
+ await updateAuthState('authSuccess');
154
+ // Send notification
155
+ (_a = onActionNotificationRef.current) === null || _a === void 0 ? void 0 : _a.call(onActionNotificationRef, {
156
+ type: 'success',
157
+ title: 'Logged In',
158
+ message: 'Successfully authenticated',
159
+ action: 'login'
160
+ });
161
+ // Call user's callback
162
+ if (onAuthSuccessRef.current) {
163
+ try {
164
+ await onAuthSuccessRef.current();
165
+ }
166
+ catch (e) {
167
+ console.error("[TideCloak] onAuthSuccess callback error:", e);
168
+ }
169
+ }
170
+ };
171
+ const handleAuthError = (_event, error) => {
172
+ var _a;
173
+ if (!mounted)
174
+ return;
175
+ setIsLoading(false);
176
+ const err = error instanceof Error ? error : new Error(String(error));
177
+ // Send notification
178
+ (_a = onActionNotificationRef.current) === null || _a === void 0 ? void 0 : _a.call(onActionNotificationRef, {
179
+ type: 'error',
180
+ title: 'Login Failed',
181
+ message: err.message,
182
+ action: 'login'
183
+ });
184
+ // Call user's callback
185
+ if (onAuthErrorRef.current) {
186
+ onAuthErrorRef.current(err);
187
+ }
188
+ };
189
+ const handleLogout = () => {
190
+ var _a;
191
+ if (!mounted)
192
+ return;
193
+ setIsLoading(false);
194
+ setAuthenticated(false);
195
+ setToken(null);
196
+ setIdToken(null);
197
+ setNeedsReauth(false);
198
+ // Send notification
199
+ (_a = onActionNotificationRef.current) === null || _a === void 0 ? void 0 : _a.call(onActionNotificationRef, {
200
+ type: 'info',
201
+ title: 'Logged Out',
202
+ message: 'You have been logged out',
203
+ action: 'logout'
204
+ });
205
+ // Call user's callback
206
+ if (onLogoutRef.current) {
207
+ onLogoutRef.current();
47
208
  }
48
209
  };
49
- const onTokenExpired = async () => {
210
+ const handleInitError = (err) => {
211
+ var _a;
212
+ if (!mounted)
213
+ return;
214
+ setInitError(err);
215
+ setIsInitializing(false);
216
+ // Send notification
217
+ (_a = onActionNotificationRef.current) === null || _a === void 0 ? void 0 : _a.call(onActionNotificationRef, {
218
+ type: 'error',
219
+ title: 'Initialization Failed',
220
+ message: err.message,
221
+ action: 'init'
222
+ });
223
+ };
224
+ const handleTokenExpired = async () => {
225
+ var _a;
50
226
  if (!mounted)
51
227
  return;
52
- console.debug("[TideCloak Provider] Token expired, attempting refresh...");
228
+ console.debug("[TideCloak] Token expired, attempting refresh...");
53
229
  setSessionExpired(true);
230
+ // Send notification
231
+ (_a = onActionNotificationRef.current) === null || _a === void 0 ? void 0 : _a.call(onActionNotificationRef, {
232
+ type: 'warning',
233
+ title: 'Session Expiring',
234
+ message: 'Refreshing your session...',
235
+ action: 'token'
236
+ });
54
237
  try {
55
238
  await IAMService.updateIAMToken();
56
239
  }
57
240
  catch (refreshError) {
58
- console.error("[TideCloak Provider] Failed to refresh token:", refreshError);
241
+ console.error("[TideCloak] Token refresh failed:", refreshError);
59
242
  }
60
243
  };
244
+ const handleAuthRefreshSuccess = async () => {
245
+ var _a;
246
+ if (!mounted)
247
+ return;
248
+ await updateAuthState('authRefreshSuccess');
249
+ // Send notification
250
+ (_a = onActionNotificationRef.current) === null || _a === void 0 ? void 0 : _a.call(onActionNotificationRef, {
251
+ type: 'success',
252
+ title: 'Session Refreshed',
253
+ message: 'Your session has been refreshed',
254
+ action: 'token'
255
+ });
256
+ };
257
+ const handleAuthRefreshError = async () => {
258
+ var _a;
259
+ if (!mounted)
260
+ return;
261
+ await updateAuthState('authRefreshError');
262
+ // Send notification
263
+ (_a = onActionNotificationRef.current) === null || _a === void 0 ? void 0 : _a.call(onActionNotificationRef, {
264
+ type: 'error',
265
+ title: 'Session Refresh Failed',
266
+ message: 'Please log in again',
267
+ action: 'token'
268
+ });
269
+ };
270
+ // Subscribe to IAMService events
61
271
  IAMService
62
- .on('authSuccess', updateAuthState)
63
- .on('authError', updateAuthState)
64
- .on('authRefreshSuccess', updateAuthState)
65
- .on('authRefreshError', updateAuthState)
66
- .on('logout', updateAuthState)
67
- .on('tokenExpired', onTokenExpired)
68
- .on('initError', onInitError);
272
+ .on('authSuccess', handleAuthSuccess)
273
+ .on('authError', handleAuthError)
274
+ .on('authRefreshSuccess', handleAuthRefreshSuccess)
275
+ .on('authRefreshError', handleAuthRefreshError)
276
+ .on('logout', handleLogout)
277
+ .on('tokenExpired', handleTokenExpired)
278
+ .on('initError', handleInitError);
69
279
  setIsInitializing(true);
280
+ // Initialize
70
281
  (async () => {
71
282
  try {
72
- const loaded = await IAMService.loadConfig(config);
73
- ;
283
+ const loaded = await IAMService.loadConfig(resolvedConfig);
74
284
  if (!loaded)
75
285
  throw new Error("Invalid config");
76
- setBaseURL(loaded['auth-server-url'].replace(/\/+$/, ''));
77
- await IAMService.initIAM(config, updateAuthState);
286
+ setBaseURL((loaded['auth-server-url'] || '').replace(/\/+$/, ''));
287
+ await IAMService.initIAM(resolvedConfig, updateAuthState);
78
288
  if (!mounted)
79
289
  return;
80
290
  setIsInitializing(false);
81
291
  }
82
292
  catch (err) {
83
- onInitError(err);
293
+ handleInitError(err);
84
294
  }
85
295
  })();
86
296
  return () => {
87
297
  mounted = false;
88
- IAMService.off('ready', updateAuthState)
89
- .off('authSuccess', updateAuthState)
90
- .off('authError', updateAuthState)
91
- .off('authRefreshSuccess', updateAuthState)
92
- .off('authRefreshError', updateAuthState)
93
- .off('logout', updateAuthState)
94
- .off('tokenExpired', onTokenExpired)
95
- .off('initError', onInitError);
298
+ IAMService
299
+ .off('authSuccess', handleAuthSuccess)
300
+ .off('authError', handleAuthError)
301
+ .off('authRefreshSuccess', handleAuthRefreshSuccess)
302
+ .off('authRefreshError', handleAuthRefreshError)
303
+ .off('logout', handleLogout)
304
+ .off('tokenExpired', handleTokenExpired)
305
+ .off('initError', handleInitError);
96
306
  };
97
- }, [config, reloadKey]);
98
- if (isInitializing)
99
- return null;
100
- return (_jsx(TideCloakContext.Provider, { value: {
101
- isInitializing,
102
- initError,
103
- authenticated,
104
- sessionExpired,
105
- isRefreshing,
106
- token,
107
- idToken,
108
- tokenExp,
109
- baseURL,
110
- getConfig: () => IAMService.getConfig(),
111
- reload: () => setReloadKey(k => k + 1),
112
- login: () => IAMService.doLogin(),
113
- logout: () => IAMService.doLogout(),
114
- refreshToken: async () => {
115
- setIsRefreshing(true);
116
- try {
117
- return await IAMService.updateIAMToken();
118
- }
119
- finally {
120
- setIsRefreshing(false);
121
- }
122
- },
123
- forceRefreshToken: async () => {
124
- setIsRefreshing(true);
125
- try {
126
- return await IAMService.forceUpdateToken();
307
+ }, [resolvedConfig, reloadKey]);
308
+ // Actions
309
+ const login = React.useCallback(async () => {
310
+ setIsLoading(true);
311
+ try {
312
+ await IAMService.doLogin();
313
+ }
314
+ catch (error) {
315
+ setIsLoading(false);
316
+ throw error;
317
+ }
318
+ }, []);
319
+ const logout = React.useCallback(async () => {
320
+ setIsLoading(true);
321
+ try {
322
+ await IAMService.doLogout();
323
+ }
324
+ catch (error) {
325
+ setIsLoading(false);
326
+ throw error;
327
+ }
328
+ }, []);
329
+ const getToken = React.useCallback(async () => {
330
+ try {
331
+ return await IAMService.getToken();
332
+ }
333
+ catch (error) {
334
+ console.error("[TideCloak] getToken error:", error);
335
+ return null;
336
+ }
337
+ }, []);
338
+ const refreshToken = React.useCallback(async () => {
339
+ setIsRefreshing(true);
340
+ try {
341
+ return await IAMService.updateIAMToken();
342
+ }
343
+ finally {
344
+ setIsRefreshing(false);
345
+ }
346
+ }, []);
347
+ const forceRefreshToken = React.useCallback(async () => {
348
+ setIsRefreshing(true);
349
+ try {
350
+ return await IAMService.forceUpdateToken();
351
+ }
352
+ finally {
353
+ setIsRefreshing(false);
354
+ }
355
+ }, []);
356
+ // Re-auth actions
357
+ const triggerReauth = React.useCallback(() => {
358
+ var _a;
359
+ setNeedsReauth(true);
360
+ // Send notification
361
+ (_a = onActionNotificationRef.current) === null || _a === void 0 ? void 0 : _a.call(onActionNotificationRef, {
362
+ type: 'warning',
363
+ title: 'Re-authentication Required',
364
+ message: 'Please log in again to continue',
365
+ action: 'reauth'
366
+ });
367
+ if (onReauthRequiredRef.current) {
368
+ onReauthRequiredRef.current();
369
+ }
370
+ }, []);
371
+ const clearReauth = React.useCallback(() => {
372
+ setNeedsReauth(false);
373
+ }, []);
374
+ const resetWasOffline = React.useCallback(() => {
375
+ setWasOffline(false);
376
+ }, []);
377
+ // Context value - provide safe defaults during initialization
378
+ const contextValue = {
379
+ // Bootstrap
380
+ isInitializing,
381
+ initError,
382
+ // Session
383
+ authenticated,
384
+ sessionExpired,
385
+ isRefreshing,
386
+ isLoading,
387
+ // Network
388
+ isOffline,
389
+ wasOffline,
390
+ // Re-auth
391
+ needsReauth,
392
+ // Tokens
393
+ token,
394
+ idToken,
395
+ tokenExp,
396
+ // Config
397
+ baseURL,
398
+ // Direct access to service instances
399
+ IAMService,
400
+ AdminAPI,
401
+ getConfig: () => isInitializing ? {} : IAMService.getConfig(),
402
+ reload: () => setReloadKey((k) => k + 1),
403
+ // Auth actions
404
+ login,
405
+ logout,
406
+ getToken,
407
+ refreshToken,
408
+ forceRefreshToken,
409
+ // Role checks - return false during initialization
410
+ hasRealmRole: (role) => isInitializing ? false : IAMService.hasRealmRole(role),
411
+ hasClientRole: (role, resource) => isInitializing ? false : IAMService.hasClientRole(role, resource),
412
+ // Token claims - return undefined during initialization or when not authenticated
413
+ getValueFromToken: (key) => (isInitializing || !authenticated) ? undefined : IAMService.getValueFromToken(key),
414
+ getValueFromIdToken: (key) => (isInitializing || !authenticated) ? undefined : IAMService.getValueFromIDToken(key),
415
+ // Re-auth actions
416
+ triggerReauth,
417
+ clearReauth,
418
+ resetWasOffline,
419
+ // Tide encryption - return null during initialization
420
+ doEncrypt: async (data) => {
421
+ var _a, _b;
422
+ if (isInitializing)
423
+ return null;
424
+ try {
425
+ const result = await IAMService.doEncrypt(data);
426
+ (_a = onActionNotificationRef.current) === null || _a === void 0 ? void 0 : _a.call(onActionNotificationRef, {
427
+ type: 'success',
428
+ title: 'Encrypted',
429
+ message: `${Array.isArray(data) ? data.length : 1} item(s) encrypted`,
430
+ action: 'encrypt'
431
+ });
432
+ return result;
433
+ }
434
+ catch (error) {
435
+ const err = error instanceof Error ? error : new Error(String(error));
436
+ (_b = onActionNotificationRef.current) === null || _b === void 0 ? void 0 : _b.call(onActionNotificationRef, {
437
+ type: 'error',
438
+ title: 'Encryption Failed',
439
+ message: err.message,
440
+ action: 'encrypt'
441
+ });
442
+ throw error;
443
+ }
444
+ },
445
+ doDecrypt: async (data) => {
446
+ var _a, _b;
447
+ if (isInitializing)
448
+ return null;
449
+ try {
450
+ const result = await IAMService.doDecrypt(data);
451
+ (_a = onActionNotificationRef.current) === null || _a === void 0 ? void 0 : _a.call(onActionNotificationRef, {
452
+ type: 'success',
453
+ title: 'Decrypted',
454
+ message: `${Array.isArray(data) ? data.length : 1} item(s) decrypted`,
455
+ action: 'decrypt'
456
+ });
457
+ return result;
458
+ }
459
+ catch (error) {
460
+ const err = error instanceof Error ? error : new Error(String(error));
461
+ (_b = onActionNotificationRef.current) === null || _b === void 0 ? void 0 : _b.call(onActionNotificationRef, {
462
+ type: 'error',
463
+ title: 'Decryption Failed',
464
+ message: err.message,
465
+ action: 'decrypt'
466
+ });
467
+ throw error;
468
+ }
469
+ },
470
+ // Tide request signing (for policy creation)
471
+ initializeTideRequest: async (request) => {
472
+ const tc = IAMService._tc;
473
+ if (!(tc === null || tc === void 0 ? void 0 : tc.createTideRequest)) {
474
+ throw new Error("TideCloak createTideRequest not available");
475
+ }
476
+ const encodedRequest = request.encode();
477
+ const initializedBytes = await tc.createTideRequest(encodedRequest);
478
+ const RequestClass = request.constructor;
479
+ if (typeof RequestClass.decode === "function") {
480
+ return RequestClass.decode(initializedBytes);
481
+ }
482
+ return request;
483
+ },
484
+ // Get vendor ID from config
485
+ getVendorId: () => {
486
+ const cfg = IAMService.getConfig();
487
+ return (cfg === null || cfg === void 0 ? void 0 : cfg.vendorId) || (cfg === null || cfg === void 0 ? void 0 : cfg["vendor-id"]) || "";
488
+ },
489
+ // Get resource (client ID) from config
490
+ getResource: () => {
491
+ const cfg = IAMService.getConfig();
492
+ return (cfg === null || cfg === void 0 ? void 0 : cfg.resource) || (cfg === null || cfg === void 0 ? void 0 : cfg.clientId) || "";
493
+ },
494
+ // Tide approval enclave (for change set approvals)
495
+ approveTideRequests: async (requests) => {
496
+ var _a, _b, _c, _d;
497
+ const tc = IAMService._tc;
498
+ if (!(tc === null || tc === void 0 ? void 0 : tc.requestTideOperatorApproval)) {
499
+ const error = new Error("TideCloak approval enclave not available");
500
+ (_a = onActionNotificationRef.current) === null || _a === void 0 ? void 0 : _a.call(onActionNotificationRef, {
501
+ type: 'error',
502
+ title: 'Approval Failed',
503
+ message: error.message,
504
+ action: 'approval'
505
+ });
506
+ throw error;
507
+ }
508
+ try {
509
+ const response = await tc.requestTideOperatorApproval(requests);
510
+ const results = response.map((res) => {
511
+ if (res.status === "approved") {
512
+ return { id: res.id, approved: { request: res.request } };
513
+ }
514
+ else if (res.status === "denied") {
515
+ return { id: res.id, denied: true };
516
+ }
517
+ else {
518
+ return { id: res.id, pending: true };
519
+ }
520
+ });
521
+ // Send notifications for each result
522
+ const approved = results.filter((r) => r.approved).length;
523
+ const denied = results.filter((r) => r.denied).length;
524
+ if (approved > 0) {
525
+ (_b = onActionNotificationRef.current) === null || _b === void 0 ? void 0 : _b.call(onActionNotificationRef, {
526
+ type: 'success',
527
+ title: 'Approved',
528
+ message: `${approved} request${approved > 1 ? 's' : ''} approved successfully`,
529
+ action: 'approval'
530
+ });
127
531
  }
128
- finally {
129
- setIsRefreshing(false);
532
+ if (denied > 0) {
533
+ (_c = onActionNotificationRef.current) === null || _c === void 0 ? void 0 : _c.call(onActionNotificationRef, {
534
+ type: 'warning',
535
+ title: 'Denied',
536
+ message: `${denied} request${denied > 1 ? 's' : ''} denied`,
537
+ action: 'approval'
538
+ });
130
539
  }
131
- },
132
- hasRealmRole: (role) => IAMService.hasRealmRole(role),
133
- hasClientRole: (role, resource) => IAMService.hasClientRole(role, resource),
134
- getValueFromToken: (key) => IAMService.getValueFromToken(key),
135
- getValueFromIdToken: (key) => IAMService.getValueFromIDToken(key),
136
- doEncrypt: async (data) => await IAMService.doEncrypt(data),
137
- doDecrypt: async (data) => await IAMService.doDecrypt(data)
138
- }, children: children }));
540
+ // Trigger reload to refresh data after approvals
541
+ setReloadKey(k => k + 1);
542
+ return results;
543
+ }
544
+ catch (error) {
545
+ const err = error instanceof Error ? error : new Error(String(error));
546
+ (_d = onActionNotificationRef.current) === null || _d === void 0 ? void 0 : _d.call(onActionNotificationRef, {
547
+ type: 'error',
548
+ title: 'Approval Failed',
549
+ message: err.message,
550
+ action: 'approval'
551
+ });
552
+ throw error;
553
+ }
554
+ },
555
+ };
556
+ return (_jsx(TideCloakContext.Provider, { value: contextValue, children: children }));
139
557
  }
558
+ // Default context value for when provider is not available
559
+ // This allows graceful degradation in iframes or other contexts
560
+ const defaultContextValue = {
561
+ isInitializing: true,
562
+ initError: null,
563
+ authenticated: false,
564
+ sessionExpired: false,
565
+ isRefreshing: false,
566
+ isLoading: false,
567
+ isOffline: false,
568
+ wasOffline: false,
569
+ needsReauth: false,
570
+ token: null,
571
+ idToken: null,
572
+ tokenExp: null,
573
+ baseURL: '',
574
+ IAMService,
575
+ AdminAPI,
576
+ getConfig: () => ({}),
577
+ reload: () => { },
578
+ login: async () => { },
579
+ logout: async () => { },
580
+ getToken: async () => null,
581
+ refreshToken: async () => false,
582
+ forceRefreshToken: async () => false,
583
+ hasRealmRole: () => false,
584
+ hasClientRole: () => false,
585
+ getValueFromToken: () => undefined,
586
+ getValueFromIdToken: () => undefined,
587
+ triggerReauth: () => { },
588
+ clearReauth: () => { },
589
+ resetWasOffline: () => { },
590
+ doEncrypt: async () => null,
591
+ doDecrypt: async () => null,
592
+ initializeTideRequest: async () => { throw new Error("TideCloakContextProvider not available"); },
593
+ getVendorId: () => "",
594
+ getResource: () => "",
595
+ approveTideRequests: async () => { throw new Error("TideCloakContextProvider not available"); },
596
+ };
140
597
  export function useTideCloakContext() {
141
598
  const ctx = React.useContext(TideCloakContext);
142
- if (!ctx)
143
- throw new Error("useTideCloakContext must be used within <TideCloakContextProvider>");
599
+ // Return default context if not within provider (e.g., in iframes)
600
+ // This provides graceful degradation instead of throwing
601
+ if (!ctx) {
602
+ console.warn("useTideCloakContext called outside TideCloakContextProvider - returning defaults");
603
+ return defaultContextValue;
604
+ }
144
605
  return ctx;
145
606
  }