antaeus.keycloak.react 2.2.3 → 2.4.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.
- package/README.md +95 -1
- package/dist/index.d.mts +117 -23
- package/dist/index.d.ts +117 -23
- package/dist/index.js +194 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +191 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createContext, useState, useRef, useCallback, useEffect, useMemo
|
|
2
|
-
import { useAuth as useAuth$1, AuthProvider } from 'react-oidc-context';
|
|
1
|
+
import { createContext, useContext, useState, useRef, useCallback, useEffect, useMemo } from 'react';
|
|
2
|
+
import { useAuth as useAuth$1, AuthContext, AuthProvider } from 'react-oidc-context';
|
|
3
3
|
import { WebStorageStateStore, User } from 'oidc-client-ts';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import { useLocation } from 'react-router-dom';
|
|
@@ -27,7 +27,8 @@ var defaultConfig = {
|
|
|
27
27
|
maxRefreshTokenLifetime: 2592e3,
|
|
28
28
|
// 30 days in seconds
|
|
29
29
|
showLoadingIndicator: false,
|
|
30
|
-
loadingMessage: "Refreshing session..."
|
|
30
|
+
loadingMessage: "Refreshing session...",
|
|
31
|
+
useRefreshToken: true
|
|
31
32
|
},
|
|
32
33
|
advanced: {
|
|
33
34
|
responseType: "code",
|
|
@@ -53,6 +54,7 @@ var KeycloakConfigBuilder = class {
|
|
|
53
54
|
const config = this.mergeWithDefaults(userConfig);
|
|
54
55
|
const storageType = config.advanced.storageType || "session";
|
|
55
56
|
const storage = storageType === "local" ? window.localStorage : window.sessionStorage;
|
|
57
|
+
const { useRefreshToken } = config.silentRenew;
|
|
56
58
|
const oidcSettings = {
|
|
57
59
|
authority: config.authority,
|
|
58
60
|
client_id: config.clientId,
|
|
@@ -70,6 +72,9 @@ var KeycloakConfigBuilder = class {
|
|
|
70
72
|
response_mode: "query"
|
|
71
73
|
}
|
|
72
74
|
};
|
|
75
|
+
if (useRefreshToken !== void 0) {
|
|
76
|
+
oidcSettings.useRefreshToken = useRefreshToken;
|
|
77
|
+
}
|
|
73
78
|
return oidcSettings;
|
|
74
79
|
}
|
|
75
80
|
/**
|
|
@@ -157,6 +162,7 @@ function useTokenLifecycle(options) {
|
|
|
157
162
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
158
163
|
const [isRefreshTokenExpired, setIsRefreshTokenExpired] = useState(false);
|
|
159
164
|
const hasCheckedOnStartup = useRef(false);
|
|
165
|
+
const supportsRefreshTokens = config.silentRenew?.useRefreshToken !== false;
|
|
160
166
|
const log = useCallback(
|
|
161
167
|
(message, ...args) => {
|
|
162
168
|
if (debug) {
|
|
@@ -293,24 +299,49 @@ function useTokenLifecycle(options) {
|
|
|
293
299
|
useEffect(() => {
|
|
294
300
|
const strategy = config.silentRenew?.refreshStrategy || "both";
|
|
295
301
|
const shouldCheckOnStartup = strategy === "startup" || strategy === "both";
|
|
302
|
+
const hasRefreshToken = Boolean(auth.user?.refresh_token);
|
|
296
303
|
if (!shouldCheckOnStartup || hasCheckedOnStartup.current) {
|
|
297
304
|
return;
|
|
298
305
|
}
|
|
299
|
-
if (
|
|
306
|
+
if (auth.isLoading) {
|
|
300
307
|
return;
|
|
301
308
|
}
|
|
309
|
+
if (!auth.isAuthenticated) {
|
|
310
|
+
if (!supportsRefreshTokens) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (!auth.user || !hasRefreshToken) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
302
317
|
hasCheckedOnStartup.current = true;
|
|
303
|
-
log(
|
|
318
|
+
log(
|
|
319
|
+
"Running startup token check with strategy:",
|
|
320
|
+
strategy,
|
|
321
|
+
"using refresh token:",
|
|
322
|
+
!auth.isAuthenticated && supportsRefreshTokens
|
|
323
|
+
);
|
|
304
324
|
checkAndRefreshTokens().catch((error) => {
|
|
305
325
|
log("Startup token check failed:", error);
|
|
306
326
|
});
|
|
307
327
|
}, [
|
|
308
328
|
auth.isAuthenticated,
|
|
309
329
|
auth.isLoading,
|
|
330
|
+
auth.user,
|
|
310
331
|
config.silentRenew?.refreshStrategy,
|
|
332
|
+
supportsRefreshTokens,
|
|
311
333
|
checkAndRefreshTokens,
|
|
312
334
|
log
|
|
313
335
|
]);
|
|
336
|
+
useEffect(() => {
|
|
337
|
+
if (hasCheckedOnStartup.current) {
|
|
338
|
+
const hasUser = Boolean(auth.user);
|
|
339
|
+
const hasRefreshToken = Boolean(auth.user?.refresh_token);
|
|
340
|
+
if (auth.isLoading || !auth.isAuthenticated && (!supportsRefreshTokens || !hasUser || !hasRefreshToken)) {
|
|
341
|
+
hasCheckedOnStartup.current = false;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}, [auth.isAuthenticated, auth.isLoading, auth.user, supportsRefreshTokens]);
|
|
314
345
|
useEffect(() => {
|
|
315
346
|
if (auth.user && !auth.isLoading) {
|
|
316
347
|
storeFirstLoginTime();
|
|
@@ -625,6 +656,71 @@ var SessionMonitor = ({
|
|
|
625
656
|
] });
|
|
626
657
|
};
|
|
627
658
|
|
|
659
|
+
// src/utils/tokenBridge.ts
|
|
660
|
+
var TokenBridge = class {
|
|
661
|
+
constructor() {
|
|
662
|
+
this.currentToken = null;
|
|
663
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Update the tracked token and notify subscribers when it changes.
|
|
667
|
+
*/
|
|
668
|
+
setToken(token) {
|
|
669
|
+
if (this.currentToken === token) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
this.currentToken = token;
|
|
673
|
+
for (const listener of this.listeners) {
|
|
674
|
+
try {
|
|
675
|
+
listener(token);
|
|
676
|
+
} catch (error) {
|
|
677
|
+
console.error("[TokenBridge] Listener threw error:", error);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Read the most recent token value.
|
|
683
|
+
*/
|
|
684
|
+
getToken() {
|
|
685
|
+
return this.currentToken;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Convenience helper for async callers.
|
|
689
|
+
*/
|
|
690
|
+
async getTokenAsync() {
|
|
691
|
+
return this.currentToken;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Subscribe to token changes. Returns an unsubscribe callback.
|
|
695
|
+
*/
|
|
696
|
+
subscribe(listener) {
|
|
697
|
+
this.listeners.add(listener);
|
|
698
|
+
listener(this.currentToken);
|
|
699
|
+
return () => {
|
|
700
|
+
this.listeners.delete(listener);
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
var createTokenBridge = () => new TokenBridge();
|
|
705
|
+
var defaultTokenBridge = createTokenBridge();
|
|
706
|
+
|
|
707
|
+
// src/components/TokenBridge.tsx
|
|
708
|
+
var TokenBridgeSync = ({
|
|
709
|
+
bridge = defaultTokenBridge
|
|
710
|
+
}) => {
|
|
711
|
+
const auth = useAuth();
|
|
712
|
+
useEffect(() => {
|
|
713
|
+
const token = auth.user?.access_token ?? null;
|
|
714
|
+
bridge.setToken(token);
|
|
715
|
+
}, [auth.user, bridge]);
|
|
716
|
+
useEffect(() => {
|
|
717
|
+
if (!auth.isAuthenticated) {
|
|
718
|
+
bridge.setToken(null);
|
|
719
|
+
}
|
|
720
|
+
}, [auth.isAuthenticated, bridge]);
|
|
721
|
+
return null;
|
|
722
|
+
};
|
|
723
|
+
|
|
628
724
|
// src/config/presets.ts
|
|
629
725
|
var CONFIG_PRESETS = {
|
|
630
726
|
/**
|
|
@@ -843,11 +939,97 @@ var StorageConfigProvider = ({ storageType, children }) => {
|
|
|
843
939
|
return /* @__PURE__ */ jsx(StorageConfigContext.Provider, { value: { storageType, storage }, children });
|
|
844
940
|
};
|
|
845
941
|
var useStorageConfig = () => useContext(StorageConfigContext);
|
|
942
|
+
var defaultStatus = {
|
|
943
|
+
isEnabled: true,
|
|
944
|
+
isConfigured: true
|
|
945
|
+
};
|
|
946
|
+
var KeycloakStatusContext = createContext(defaultStatus);
|
|
947
|
+
var KeycloakStatusProvider = KeycloakStatusContext.Provider;
|
|
948
|
+
var useKeycloakStatus = () => useContext(KeycloakStatusContext);
|
|
949
|
+
var createDisabledEvents = () => {
|
|
950
|
+
const noop = () => void 0;
|
|
951
|
+
const noopSubscription = () => () => void 0;
|
|
952
|
+
const noopAsync = async () => void 0;
|
|
953
|
+
const events = {
|
|
954
|
+
addAccessTokenExpiring: noopSubscription,
|
|
955
|
+
removeAccessTokenExpiring: noop,
|
|
956
|
+
addAccessTokenExpired: noopSubscription,
|
|
957
|
+
removeAccessTokenExpired: noop,
|
|
958
|
+
addSilentRenewError: noopSubscription,
|
|
959
|
+
removeSilentRenewError: noop,
|
|
960
|
+
addUserLoaded: noopSubscription,
|
|
961
|
+
removeUserLoaded: noop,
|
|
962
|
+
addUserUnloaded: noopSubscription,
|
|
963
|
+
removeUserUnloaded: noop,
|
|
964
|
+
addUserSignedIn: noopSubscription,
|
|
965
|
+
removeUserSignedIn: noop,
|
|
966
|
+
addUserSignedOut: noopSubscription,
|
|
967
|
+
removeUserSignedOut: noop,
|
|
968
|
+
addUserSessionChanged: noopSubscription,
|
|
969
|
+
removeUserSessionChanged: noop,
|
|
970
|
+
load: noopAsync,
|
|
971
|
+
unload: noopAsync
|
|
972
|
+
};
|
|
973
|
+
return events;
|
|
974
|
+
};
|
|
975
|
+
var disabledEvents = createDisabledEvents();
|
|
976
|
+
var disabledSettings = {
|
|
977
|
+
authority: "",
|
|
978
|
+
client_id: "",
|
|
979
|
+
redirect_uri: "http://localhost/disabled",
|
|
980
|
+
response_type: "code",
|
|
981
|
+
scope: ""
|
|
982
|
+
};
|
|
983
|
+
var disabledError = () => new Error("Keycloak integration is disabled for this environment.");
|
|
984
|
+
var disabledAsync = async () => {
|
|
985
|
+
throw disabledError();
|
|
986
|
+
};
|
|
987
|
+
var disabledAuthContext = {
|
|
988
|
+
settings: disabledSettings,
|
|
989
|
+
events: disabledEvents,
|
|
990
|
+
clearStaleState: async () => void 0,
|
|
991
|
+
removeUser: async () => void 0,
|
|
992
|
+
signinPopup: (_args) => disabledAsync(),
|
|
993
|
+
signinSilent: async (_args) => null,
|
|
994
|
+
signinRedirect: (_args) => disabledAsync(),
|
|
995
|
+
signinResourceOwnerCredentials: (_args) => disabledAsync(),
|
|
996
|
+
signoutRedirect: async (_args) => void 0,
|
|
997
|
+
signoutPopup: async (_args) => void 0,
|
|
998
|
+
signoutSilent: async (_args) => void 0,
|
|
999
|
+
querySessionStatus: async (_args) => null,
|
|
1000
|
+
revokeTokens: async (_types) => void 0,
|
|
1001
|
+
startSilentRenew: () => void 0,
|
|
1002
|
+
stopSilentRenew: () => void 0,
|
|
1003
|
+
user: null,
|
|
1004
|
+
isLoading: false,
|
|
1005
|
+
isAuthenticated: false,
|
|
1006
|
+
activeNavigator: void 0,
|
|
1007
|
+
error: void 0
|
|
1008
|
+
};
|
|
846
1009
|
function isZeroConfig(props) {
|
|
847
1010
|
return "authority" in props && "clientId" in props;
|
|
848
1011
|
}
|
|
849
1012
|
var KeycloakProvider = (props) => {
|
|
850
1013
|
const { children, debug = false } = props;
|
|
1014
|
+
const status = useMemo(() => {
|
|
1015
|
+
const enabledFlag = props.enabled ?? true;
|
|
1016
|
+
const hasAuthority = isZeroConfig(props) ? Boolean(props.authority) : Boolean(props.config.authority);
|
|
1017
|
+
const hasClientId = isZeroConfig(props) ? Boolean(props.clientId) : Boolean(props.config.clientId);
|
|
1018
|
+
return {
|
|
1019
|
+
isEnabled: enabledFlag,
|
|
1020
|
+
isConfigured: hasAuthority && hasClientId,
|
|
1021
|
+
reason: !enabledFlag ? "disabled" : !hasAuthority || !hasClientId ? "missing-config" : void 0
|
|
1022
|
+
};
|
|
1023
|
+
}, [props]);
|
|
1024
|
+
const effectiveEnabled = status.isEnabled && status.isConfigured;
|
|
1025
|
+
useEffect(() => {
|
|
1026
|
+
if (!effectiveEnabled && props.tokenBridge) {
|
|
1027
|
+
props.tokenBridge.setToken(null);
|
|
1028
|
+
}
|
|
1029
|
+
}, [effectiveEnabled, props.tokenBridge]);
|
|
1030
|
+
if (!effectiveEnabled) {
|
|
1031
|
+
return /* @__PURE__ */ jsx(KeycloakStatusProvider, { value: status, children: /* @__PURE__ */ jsx(AuthContext.Provider, { value: disabledAuthContext, children: /* @__PURE__ */ jsx(StorageConfigProvider, { storageType: "session", children }) }) });
|
|
1032
|
+
}
|
|
851
1033
|
const config = isZeroConfig(props) ? buildZeroConfig(props) : props.config;
|
|
852
1034
|
const oidcConfig = KeycloakConfigBuilder.build(config);
|
|
853
1035
|
const log = useCallback(
|
|
@@ -889,7 +1071,7 @@ var KeycloakProvider = (props) => {
|
|
|
889
1071
|
}, [log]);
|
|
890
1072
|
const showSessionMonitor = config.features?.sessionMonitor !== false;
|
|
891
1073
|
const storageType = config.advanced?.storageType || "session";
|
|
892
|
-
return /* @__PURE__ */ jsx(
|
|
1074
|
+
return /* @__PURE__ */ jsx(KeycloakStatusProvider, { value: status, children: /* @__PURE__ */ jsx(
|
|
893
1075
|
AuthProvider,
|
|
894
1076
|
{
|
|
895
1077
|
...oidcConfig,
|
|
@@ -897,11 +1079,12 @@ var KeycloakProvider = (props) => {
|
|
|
897
1079
|
onSignoutCallback,
|
|
898
1080
|
onRemoveUser,
|
|
899
1081
|
children: /* @__PURE__ */ jsx(StorageConfigProvider, { storageType, children: /* @__PURE__ */ jsxs(TokenLifecycleManager, { config, debug, children: [
|
|
1082
|
+
props.tokenBridge && /* @__PURE__ */ jsx(TokenBridgeSync, { bridge: props.tokenBridge }),
|
|
900
1083
|
showSessionMonitor && /* @__PURE__ */ jsx(SessionMonitor, {}),
|
|
901
1084
|
children
|
|
902
1085
|
] }) })
|
|
903
1086
|
}
|
|
904
|
-
);
|
|
1087
|
+
) });
|
|
905
1088
|
};
|
|
906
1089
|
function buildZeroConfig(props) {
|
|
907
1090
|
const { authority, clientId, preset = "default", config: overrides = {} } = props;
|
|
@@ -1605,6 +1788,6 @@ function useProtectedFetch(options = {}) {
|
|
|
1605
1788
|
};
|
|
1606
1789
|
}
|
|
1607
1790
|
|
|
1608
|
-
export { CONFIG_PRESETS, DeviceLogin, KeycloakConfigBuilder, KeycloakProvider, LoginButton, LogoutButton, ProtectedRoute, SessionMonitor, TokenLifecycleManager, UserProfile, defaultConfig, fetchUserInfo, getConfigFromEnv, getConfigPreset, mergeWithPreset, pollForToken, requestDeviceCode, useAuth, useProtectedFetch, useRoles, useScopes, useTokenLifecycle };
|
|
1791
|
+
export { CONFIG_PRESETS, DeviceLogin, KeycloakConfigBuilder, KeycloakProvider, KeycloakStatusProvider, LoginButton, LogoutButton, ProtectedRoute, SessionMonitor, TokenBridge, TokenBridgeSync, TokenLifecycleManager, UserProfile, createTokenBridge, defaultConfig, defaultTokenBridge, fetchUserInfo, getConfigFromEnv, getConfigPreset, mergeWithPreset, pollForToken, requestDeviceCode, useAuth, useKeycloakStatus, useProtectedFetch, useRoles, useScopes, useTokenLifecycle };
|
|
1609
1792
|
//# sourceMappingURL=index.mjs.map
|
|
1610
1793
|
//# sourceMappingURL=index.mjs.map
|