antaeus.keycloak.react 2.6.0 → 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -165,6 +165,20 @@ var isTransientNetworkError = (error) => {
165
165
  }
166
166
  return message.includes("network") || message.includes("failed to fetch") || message.includes("fetch") || message.includes("timeout") || message.includes("temporarily unavailable") || message.includes("dns") || message.includes("offline");
167
167
  };
168
+ var isInvalidRefreshTokenError = (error) => {
169
+ if (error instanceof oidcClientTs.ErrorResponse) {
170
+ const code = error.error?.toLowerCase?.();
171
+ if (code === "invalid_grant" || code === "invalid_token") {
172
+ return true;
173
+ }
174
+ const description = error.error_description?.toLowerCase?.();
175
+ if (description?.includes("refresh token")) {
176
+ return true;
177
+ }
178
+ }
179
+ const message = error.message?.toLowerCase?.() ?? "";
180
+ return message.includes("invalid_grant") || message.includes("refresh token") || message.includes("invalid token");
181
+ };
168
182
  function useTokenLifecycle(options) {
169
183
  const {
170
184
  config,
@@ -255,6 +269,20 @@ function useTokenLifecycle(options) {
255
269
  onRefreshSuccess?.();
256
270
  } catch (error) {
257
271
  const err = error instanceof Error ? error : new Error(String(error));
272
+ if (isInvalidRefreshTokenError(err)) {
273
+ log("Refresh token rejected by the identity provider, clearing stored session");
274
+ setIsRefreshTokenExpired(true);
275
+ onRefreshTokenExpired?.();
276
+ const key = getStorageKey();
277
+ localStorage.removeItem(key);
278
+ try {
279
+ await auth.removeUser();
280
+ } catch (removeError) {
281
+ log("Error removing user after invalid refresh token:", removeError);
282
+ }
283
+ onRefreshError?.(err);
284
+ throw err;
285
+ }
258
286
  if (isTransientNetworkError(err)) {
259
287
  const reason = isNavigatorOffline() ? "offline" : "network";
260
288
  deferredRefreshReasonRef.current = reason;
@@ -285,6 +313,7 @@ function useTokenLifecycle(options) {
285
313
  onRefreshError,
286
314
  onRefreshTokenExpired,
287
315
  enqueueRetry,
316
+ getStorageKey,
288
317
  log
289
318
  ]);
290
319
  const checkAndRefreshTokens = react.useCallback(async () => {
@@ -438,16 +467,21 @@ function useTokenLifecycle(options) {
438
467
  if (hasCheckedOnStartup.current) {
439
468
  const hasUser = Boolean(auth.user);
440
469
  const hasRefreshToken = Boolean(auth.user?.refresh_token);
441
- if (auth.isLoading || !auth.isAuthenticated && (!supportsRefreshTokens || !hasUser || !hasRefreshToken)) {
470
+ if (!auth.isAuthenticated && (!supportsRefreshTokens || !hasUser || !hasRefreshToken)) {
442
471
  hasCheckedOnStartup.current = false;
443
472
  }
444
473
  }
445
- }, [auth.isAuthenticated, auth.isLoading, auth.user, supportsRefreshTokens]);
474
+ }, [auth.isAuthenticated, auth.user, supportsRefreshTokens]);
446
475
  react.useEffect(() => {
447
476
  if (auth.user && !auth.isLoading) {
448
477
  storeFirstLoginTime();
449
478
  }
450
479
  }, [auth.user, auth.isLoading, storeFirstLoginTime]);
480
+ react.useEffect(() => {
481
+ if (auth.isAuthenticated && isRefreshTokenExpired) {
482
+ setIsRefreshTokenExpired(false);
483
+ }
484
+ }, [auth.isAuthenticated, isRefreshTokenExpired]);
451
485
  react.useEffect(() => {
452
486
  if (!auth.isAuthenticated || auth.isLoading) {
453
487
  return;