@shane_donnelly/dsi-internal-react-utils 0.1.3 → 0.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.
- package/README.md +33 -81
- package/dist/keycloak/core/types.d.ts +22 -6
- package/dist/keycloak/index.d.ts +2 -4
- package/dist/keycloak/index.js +2 -3
- package/dist/keycloak/react/KeycloakProvider/index.js +29 -24
- package/dist/keycloak/react/hooks/{useAuth.d.ts → useKeycloakAuth.d.ts} +17 -4
- package/dist/keycloak/react/hooks/useKeycloakAuth.js +10 -0
- package/dist/main.d.ts +2 -2
- package/dist/main.js +2 -3
- package/package.json +1 -1
- package/dist/ProtectedRoute-_48urWWd.js +0 -55
- package/dist/keycloak/react/ProtectedRoute/index.d.ts +0 -69
- package/dist/keycloak/react/ProtectedRoute/index.js +0 -2
- package/dist/keycloak/react/hooks/useAuth.js +0 -10
- package/dist/style.css +0 -2
package/README.md
CHANGED
|
@@ -26,37 +26,21 @@ import '@shane_donnelly/dsi-internal-react-utils/style.css'
|
|
|
26
26
|
|
|
27
27
|
### Keycloak
|
|
28
28
|
|
|
29
|
-
Wrapper de simplification pour `keycloak-js`.
|
|
29
|
+
Wrapper de simplification pour `keycloak-js`. `KeycloakProvider` singleton à la racine, `useKeycloakAuth` pour accéder à l'état d'auth et déclencher login / logout depuis n'importe quel composant.
|
|
30
30
|
|
|
31
31
|
[Documentation complète du module Keycloak](docs/keycloak.md)
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
|
-
## Exemple complet avec React Router
|
|
35
|
+
## Exemple complet avec React Router v7 (framework mode)
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
// main.tsx
|
|
39
|
-
import React from 'react';
|
|
40
|
-
import ReactDOM from 'react-dom/client';
|
|
41
|
-
import { BrowserRouter } from 'react-router-dom';
|
|
42
|
-
import App from './App';
|
|
43
|
-
|
|
44
|
-
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
45
|
-
<React.StrictMode>
|
|
46
|
-
<BrowserRouter>
|
|
47
|
-
<App />
|
|
48
|
-
</BrowserRouter>
|
|
49
|
-
</React.StrictMode>,
|
|
50
|
-
);
|
|
51
|
-
```
|
|
37
|
+
React Router v7 framework mode utilise `app/root.tsx` comme layout racine — c'est là que le `KeycloakProvider` doit être placé.
|
|
52
38
|
|
|
53
39
|
```tsx
|
|
54
|
-
//
|
|
55
|
-
import {
|
|
56
|
-
import { KeycloakProvider
|
|
57
|
-
import
|
|
58
|
-
import Dashboard from './pages/Dashboard';
|
|
59
|
-
import Profile from './pages/Profile';
|
|
40
|
+
// app/root.tsx
|
|
41
|
+
import { Outlet } from 'react-router';
|
|
42
|
+
import { KeycloakProvider } from '@shane_donnelly/dsi-internal-react-utils';
|
|
43
|
+
import '@shane_donnelly/dsi-internal-react-utils/style.css';
|
|
60
44
|
|
|
61
45
|
const keycloakConfig = {
|
|
62
46
|
url: 'https://keycloak.example.com',
|
|
@@ -64,83 +48,51 @@ const keycloakConfig = {
|
|
|
64
48
|
clientId: 'my-frontend',
|
|
65
49
|
};
|
|
66
50
|
|
|
67
|
-
export default function
|
|
51
|
+
export default function Root() {
|
|
68
52
|
return (
|
|
69
53
|
<KeycloakProvider config={keycloakConfig} idpHint="oidc" refreshInterval={300}>
|
|
70
|
-
<
|
|
71
|
-
{/* Route publique — accessible sans authentification */}
|
|
72
|
-
<Route path="/" element={<PublicPage />} />
|
|
73
|
-
|
|
74
|
-
{/* Routes protégées — redirection automatique vers l'IDP */}
|
|
75
|
-
<Route
|
|
76
|
-
path="/dashboard"
|
|
77
|
-
element={
|
|
78
|
-
<ProtectedRoute>
|
|
79
|
-
<Dashboard />
|
|
80
|
-
</ProtectedRoute>
|
|
81
|
-
}
|
|
82
|
-
/>
|
|
83
|
-
<Route
|
|
84
|
-
path="/profile"
|
|
85
|
-
element={
|
|
86
|
-
<ProtectedRoute fallback={<p>Chargement du profil...</p>}>
|
|
87
|
-
<Profile />
|
|
88
|
-
</ProtectedRoute>
|
|
89
|
-
}
|
|
90
|
-
/>
|
|
91
|
-
</Routes>
|
|
54
|
+
<Outlet />
|
|
92
55
|
</KeycloakProvider>
|
|
93
56
|
);
|
|
94
57
|
}
|
|
95
58
|
```
|
|
96
59
|
|
|
97
60
|
```tsx
|
|
98
|
-
//
|
|
99
|
-
import {
|
|
61
|
+
// app/routes/profile.tsx
|
|
62
|
+
import { useEffect } from 'react';
|
|
63
|
+
import { useKeycloakAuth } from '@shane_donnelly/dsi-internal-react-utils';
|
|
64
|
+
|
|
65
|
+
export default function Profile() {
|
|
66
|
+
const { status, user, login, logout } = useKeycloakAuth();
|
|
67
|
+
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (status === 'unauthenticated') {
|
|
70
|
+
login({ idpHint: 'oidc' });
|
|
71
|
+
}
|
|
72
|
+
}, [status, login]);
|
|
100
73
|
|
|
101
|
-
|
|
102
|
-
|
|
74
|
+
if (status === 'loading' || status === 'unauthenticated') return <p>Chargement...</p>;
|
|
75
|
+
if (status === 'error') return <p>Erreur d'authentification.</p>;
|
|
103
76
|
|
|
104
77
|
return (
|
|
105
78
|
<div>
|
|
106
79
|
<h1>Bienvenue {user?.name}</h1>
|
|
107
80
|
<p>Email : {user?.email}</p>
|
|
108
|
-
<button onClick={() => logout()}>
|
|
81
|
+
<button onClick={() => logout({ redirectUri: window.location.origin })}>
|
|
82
|
+
Se déconnecter
|
|
83
|
+
</button>
|
|
109
84
|
</div>
|
|
110
85
|
);
|
|
111
86
|
}
|
|
112
87
|
```
|
|
113
88
|
|
|
114
|
-
### Mode plug & play (sans Provider)
|
|
115
|
-
|
|
116
|
-
Pour un usage rapide sans `KeycloakProvider`, passez `config` directement à `ProtectedRoute` :
|
|
117
|
-
|
|
118
|
-
```tsx
|
|
119
|
-
import { ProtectedRoute } from '@shane_donnelly/dsi-internal-react-utils';
|
|
120
|
-
|
|
121
|
-
function App() {
|
|
122
|
-
return (
|
|
123
|
-
<ProtectedRoute
|
|
124
|
-
config={{
|
|
125
|
-
url: 'https://keycloak.example.com',
|
|
126
|
-
realm: 'my-realm',
|
|
127
|
-
clientId: 'my-frontend',
|
|
128
|
-
}}
|
|
129
|
-
idpHint="oidc"
|
|
130
|
-
>
|
|
131
|
-
<Dashboard />
|
|
132
|
-
</ProtectedRoute>
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
89
|
### Utiliser le token pour les appels API
|
|
138
90
|
|
|
139
91
|
```tsx
|
|
140
|
-
import {
|
|
92
|
+
import { useKeycloakAuth } from '@shane_donnelly/dsi-internal-react-utils';
|
|
141
93
|
|
|
142
94
|
function useAuthFetch() {
|
|
143
|
-
const { token } =
|
|
95
|
+
const { token } = useKeycloakAuth();
|
|
144
96
|
|
|
145
97
|
return (url: string, options?: RequestInit) =>
|
|
146
98
|
fetch(url, {
|
|
@@ -156,10 +108,10 @@ function useAuthFetch() {
|
|
|
156
108
|
### Accéder à l'instance keycloak-js (usage avancé)
|
|
157
109
|
|
|
158
110
|
```tsx
|
|
159
|
-
import {
|
|
111
|
+
import { useKeycloakAuth } from '@shane_donnelly/dsi-internal-react-utils';
|
|
160
112
|
|
|
161
113
|
function AdvancedComponent() {
|
|
162
|
-
const { keycloak } =
|
|
114
|
+
const { keycloak } = useKeycloakAuth();
|
|
163
115
|
|
|
164
116
|
// Accès direct à l'instance keycloak-js pour des cas spécifiques
|
|
165
117
|
console.log(keycloak?.tokenParsed);
|
|
@@ -171,10 +123,10 @@ function AdvancedComponent() {
|
|
|
171
123
|
Deux fonctions sont disponibles pour manipuler l'instance Keycloak en dehors de React (intercepteurs HTTP, services, etc.) :
|
|
172
124
|
|
|
173
125
|
```tsx
|
|
174
|
-
import {
|
|
126
|
+
import { useKeycloakAuth, logoutKeycloak, refreshTokenKeycloak } from '@shane_donnelly/dsi-internal-react-utils';
|
|
175
127
|
|
|
176
|
-
// Récupérer l'instance keycloak via
|
|
177
|
-
const { keycloak } =
|
|
128
|
+
// Récupérer l'instance keycloak via useKeycloakAuth()
|
|
129
|
+
const { keycloak } = useKeycloakAuth();
|
|
178
130
|
|
|
179
131
|
// Rafraîchir le token (minValidity en secondes, défaut: 30)
|
|
180
132
|
const refreshed = await refreshTokenKeycloak(keycloak!, 60);
|
|
@@ -20,9 +20,25 @@ export type KeycloakAuthOptions = {
|
|
|
20
20
|
refreshInterval?: number;
|
|
21
21
|
/** Validité minimale du token en secondes avant rafraîchissement (défaut: 30) */
|
|
22
22
|
minTokenValidity?: number;
|
|
23
|
-
/** Comportement en cas d'erreur
|
|
23
|
+
/** Comportement en cas d'erreur de refresh token: 'login' pour rediriger, 'logout' pour déconnecter, ou un handler custom */
|
|
24
24
|
onAuthError?: 'login' | 'logout' | ((error: unknown) => void);
|
|
25
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* Options pour déclencher le login Keycloak.
|
|
28
|
+
*/
|
|
29
|
+
export type LoginOptions = {
|
|
30
|
+
/** Identity provider hint, override le `idpHint` du `KeycloakProvider` */
|
|
31
|
+
idpHint?: string;
|
|
32
|
+
/** URI vers laquelle Keycloak redirige après le login */
|
|
33
|
+
redirectUri?: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Options pour déclencher le logout.
|
|
37
|
+
*/
|
|
38
|
+
export type LogoutOptions = {
|
|
39
|
+
/** URI de redirection après déconnexion (défaut: `window.location.origin`) */
|
|
40
|
+
redirectUri?: string;
|
|
41
|
+
};
|
|
26
42
|
/**
|
|
27
43
|
* État de l'authentification.
|
|
28
44
|
* - `loading` : vérification en cours
|
|
@@ -45,7 +61,7 @@ export type AuthUser = {
|
|
|
45
61
|
[key: string]: unknown;
|
|
46
62
|
};
|
|
47
63
|
/**
|
|
48
|
-
* Valeur du contexte d'authentification exposée par `KeycloakProvider` et `
|
|
64
|
+
* Valeur du contexte d'authentification exposée par `KeycloakProvider` et `useKeycloakAuth`.
|
|
49
65
|
*/
|
|
50
66
|
export type AuthContextValue = {
|
|
51
67
|
/** État actuel de l'authentification */
|
|
@@ -58,10 +74,10 @@ export type AuthContextValue = {
|
|
|
58
74
|
isAuthenticated: boolean;
|
|
59
75
|
/** Instance keycloak-js sous-jacente (pour usage avancé) */
|
|
60
76
|
keycloak: Keycloak | null;
|
|
61
|
-
/** Déclencher une redirection
|
|
62
|
-
login: (
|
|
63
|
-
/** Déclencher
|
|
64
|
-
logout: (
|
|
77
|
+
/** Déclencher une redirection vers le login Keycloak */
|
|
78
|
+
login: (options?: LoginOptions) => Promise<void>;
|
|
79
|
+
/** Déclencher le logout. Redirige vers `redirectUri` après déconnexion (défaut: `window.location.origin`). */
|
|
80
|
+
logout: (options?: LogoutOptions) => Promise<void>;
|
|
65
81
|
/** Rafraîchir manuellement le token. Retourne true si réussi. */
|
|
66
82
|
refreshToken: () => Promise<boolean>;
|
|
67
83
|
};
|
package/dist/keycloak/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export { KeycloakProvider } from './react/KeycloakProvider';
|
|
2
2
|
export type { KeycloakProviderProps } from './react/KeycloakProvider';
|
|
3
|
-
export {
|
|
4
|
-
export type { ProtectedRouteProps } from './react/ProtectedRoute';
|
|
5
|
-
export { useAuth } from './react/hooks/useAuth';
|
|
3
|
+
export { useKeycloakAuth } from './react/hooks/useKeycloakAuth';
|
|
6
4
|
export { logoutKeycloak, refreshTokenKeycloak } from './core/client';
|
|
7
|
-
export type { KeycloakConfig, KeycloakAuthOptions, AuthStatus, AuthUser, AuthContextValue, } from './core/types';
|
|
5
|
+
export type { KeycloakConfig, KeycloakAuthOptions, AuthStatus, AuthUser, AuthContextValue, LoginOptions, LogoutOptions, } from './core/types';
|
package/dist/keycloak/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { logoutKeycloak as e, refreshTokenKeycloak as t } from "./core/client.js";
|
|
2
2
|
import { KeycloakProvider as n } from "./react/KeycloakProvider/index.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export { n as KeycloakProvider, i as ProtectedRoute, e as logoutKeycloak, t as refreshTokenKeycloak, r as useAuth };
|
|
3
|
+
import { useKeycloakAuth as r } from "./react/hooks/useKeycloakAuth.js";
|
|
4
|
+
export { n as KeycloakProvider, e as logoutKeycloak, t as refreshTokenKeycloak, r as useKeycloakAuth };
|
|
@@ -4,65 +4,70 @@ import { jsx as c } from "react/jsx-runtime";
|
|
|
4
4
|
//#region lib/keycloak/react/KeycloakProvider/index.tsx
|
|
5
5
|
var l = r(null);
|
|
6
6
|
function u({ config: r, children: u, idpHint: d, refreshInterval: f = 300, minTokenValidity: p = 30, onAuthError: m }) {
|
|
7
|
-
let [h, g] = s("loading"), [_, v] = s(null), [y, b] = s(null), x = o(null), S = o(!1), C = i((e) => {
|
|
7
|
+
let [h, g] = s("loading"), [_, v] = s(null), [y, b] = s(null), x = o(null), S = o(!1), C = o(d), w = i((e) => {
|
|
8
8
|
e.authenticated && e.token ? (v(e.token), b(n(e)), g("authenticated")) : (v(null), b(null), g("unauthenticated"));
|
|
9
|
-
}, []),
|
|
10
|
-
m === "
|
|
11
|
-
}, [m, d]),
|
|
9
|
+
}, []), T = i(async (e) => {
|
|
10
|
+
m === "logout" ? await x.current?.logout({ redirectUri: window.location.origin }) : typeof m == "function" ? m(e) : await x.current?.login({ idpHint: C.current });
|
|
11
|
+
}, [m, d]), E = i(async () => {
|
|
12
12
|
let e = x.current;
|
|
13
13
|
if (!e) return !1;
|
|
14
14
|
try {
|
|
15
|
-
return await e.updateToken(p) &&
|
|
15
|
+
return await e.updateToken(p) && w(e), !0;
|
|
16
16
|
} catch (e) {
|
|
17
|
-
return console.warn("[dsi-keycloak] Token refresh failed:", e), await
|
|
17
|
+
return console.warn("[dsi-keycloak] Token refresh failed:", e), await T(e), !1;
|
|
18
18
|
}
|
|
19
19
|
}, [
|
|
20
20
|
p,
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
w,
|
|
22
|
+
T
|
|
23
23
|
]);
|
|
24
24
|
a(() => {
|
|
25
25
|
if (S.current) return;
|
|
26
26
|
S.current = !0;
|
|
27
27
|
let n = e(r);
|
|
28
28
|
x.current = n, t(n).then((e) => {
|
|
29
|
-
|
|
29
|
+
w(n), e || g("unauthenticated");
|
|
30
30
|
}).catch((e) => {
|
|
31
31
|
console.error("[dsi-keycloak] Init error:", e), g("error");
|
|
32
32
|
});
|
|
33
33
|
}, []), a(() => {
|
|
34
34
|
if (h !== "authenticated") return;
|
|
35
35
|
let e = window.setInterval(() => {
|
|
36
|
-
|
|
36
|
+
E();
|
|
37
37
|
}, f * 1e3), t = () => {
|
|
38
|
-
document.visibilityState === "visible" &&
|
|
39
|
-
};
|
|
40
|
-
return document.addEventListener("visibilitychange", t), () => {
|
|
41
|
-
clearInterval(e), document.removeEventListener("visibilitychange", t);
|
|
38
|
+
document.visibilityState === "visible" && E();
|
|
39
|
+
}, n = () => E(), r = () => E();
|
|
40
|
+
return document.addEventListener("visibilitychange", t), window.addEventListener("focus", n), window.addEventListener("online", r), () => {
|
|
41
|
+
clearInterval(e), document.removeEventListener("visibilitychange", t), window.removeEventListener("focus", n), window.removeEventListener("online", r);
|
|
42
42
|
};
|
|
43
43
|
}, [
|
|
44
44
|
h,
|
|
45
45
|
f,
|
|
46
|
-
|
|
46
|
+
E
|
|
47
47
|
]);
|
|
48
|
-
let
|
|
48
|
+
let D = i(async (e) => {
|
|
49
49
|
let t = x.current;
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
if (!t) return;
|
|
51
|
+
let n = e?.idpHint ?? d;
|
|
52
|
+
C.current = n, await t.login({
|
|
53
|
+
idpHint: n,
|
|
54
|
+
redirectUri: e?.redirectUri
|
|
55
|
+
});
|
|
56
|
+
}, [d]), O = i(async (e) => {
|
|
52
57
|
let t = x.current;
|
|
53
|
-
t && await t.logout({ redirectUri: e ?? window.location.origin });
|
|
54
|
-
}, []),
|
|
58
|
+
t && await t.logout({ redirectUri: e?.redirectUri ?? window.location.origin });
|
|
59
|
+
}, []), k = {
|
|
55
60
|
status: h,
|
|
56
61
|
token: _,
|
|
57
62
|
user: y,
|
|
58
63
|
isAuthenticated: h === "authenticated",
|
|
59
64
|
keycloak: x.current,
|
|
60
|
-
login:
|
|
61
|
-
logout:
|
|
62
|
-
refreshToken:
|
|
65
|
+
login: D,
|
|
66
|
+
logout: O,
|
|
67
|
+
refreshToken: E
|
|
63
68
|
};
|
|
64
69
|
return /* @__PURE__ */ c(l.Provider, {
|
|
65
|
-
value:
|
|
70
|
+
value: k,
|
|
66
71
|
children: u
|
|
67
72
|
});
|
|
68
73
|
}
|
|
@@ -5,14 +5,14 @@ import { AuthContextValue } from '../../core/types';
|
|
|
5
5
|
* Donne accès à l'état d'authentification, au token, aux infos utilisateur,
|
|
6
6
|
* et aux actions (login, logout, refreshToken).
|
|
7
7
|
*
|
|
8
|
-
* Doit être utilisé à l'intérieur d'un `KeycloakProvider
|
|
8
|
+
* Doit être utilisé à l'intérieur d'un `KeycloakProvider`.
|
|
9
9
|
*
|
|
10
10
|
* @returns Objet d'authentification complet
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```tsx
|
|
14
14
|
* function UserProfile() {
|
|
15
|
-
* const { user, token, isAuthenticated, logout } =
|
|
15
|
+
* const { user, token, isAuthenticated, logout } = useKeycloakAuth();
|
|
16
16
|
*
|
|
17
17
|
* if (!isAuthenticated) return null;
|
|
18
18
|
*
|
|
@@ -25,10 +25,23 @@ import { AuthContextValue } from '../../core/types';
|
|
|
25
25
|
* }
|
|
26
26
|
* ```
|
|
27
27
|
*
|
|
28
|
+
* @example Déconnexion
|
|
29
|
+
* ```tsx
|
|
30
|
+
* function LogoutButton() {
|
|
31
|
+
* const { logout } = useKeycloakAuth();
|
|
32
|
+
*
|
|
33
|
+
* return (
|
|
34
|
+
* <button onClick={() => logout()}>
|
|
35
|
+
* Se déconnecter
|
|
36
|
+
* </button>
|
|
37
|
+
* );
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
28
41
|
* @example Utiliser le token pour des appels API
|
|
29
42
|
* ```tsx
|
|
30
43
|
* function useFetchWithAuth(url: string) {
|
|
31
|
-
* const { token } =
|
|
44
|
+
* const { token } = useKeycloakAuth();
|
|
32
45
|
*
|
|
33
46
|
* return fetch(url, {
|
|
34
47
|
* headers: { Authorization: `Bearer ${token}` },
|
|
@@ -36,4 +49,4 @@ import { AuthContextValue } from '../../core/types';
|
|
|
36
49
|
* }
|
|
37
50
|
* ```
|
|
38
51
|
*/
|
|
39
|
-
export declare function
|
|
52
|
+
export declare function useKeycloakAuth(): AuthContextValue;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { KeycloakAuthContext as e } from "../KeycloakProvider/index.js";
|
|
2
|
+
import { useContext as t } from "react";
|
|
3
|
+
//#region lib/keycloak/react/hooks/useKeycloakAuth.ts
|
|
4
|
+
function n() {
|
|
5
|
+
let n = t(e);
|
|
6
|
+
if (!n) throw Error("[dsi-keycloak] useKeycloakAuth() must be used within a <KeycloakProvider>.");
|
|
7
|
+
return n;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { n as useKeycloakAuth };
|
package/dist/main.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { KeycloakProvider,
|
|
2
|
-
export type { KeycloakProviderProps,
|
|
1
|
+
export { KeycloakProvider, useKeycloakAuth, logoutKeycloak, refreshTokenKeycloak, } from './keycloak';
|
|
2
|
+
export type { KeycloakProviderProps, KeycloakConfig, KeycloakAuthOptions, AuthStatus, AuthUser, AuthContextValue, LoginOptions, LogoutOptions, } from './keycloak';
|
package/dist/main.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { logoutKeycloak as e, refreshTokenKeycloak as t } from "./keycloak/core/client.js";
|
|
2
2
|
import { KeycloakProvider as n } from "./keycloak/react/KeycloakProvider/index.js";
|
|
3
|
-
import {
|
|
4
|
-
import { t as i } from "./ProtectedRoute-_48urWWd.js";
|
|
3
|
+
import { useKeycloakAuth as r } from "./keycloak/react/hooks/useKeycloakAuth.js";
|
|
5
4
|
import "./keycloak/index.js";
|
|
6
|
-
export { n as KeycloakProvider,
|
|
5
|
+
export { n as KeycloakProvider, e as logoutKeycloak, t as refreshTokenKeycloak, r as useKeycloakAuth };
|
package/package.json
CHANGED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { KeycloakProvider as e } from "./keycloak/react/KeycloakProvider/index.js";
|
|
2
|
-
import { useAuth as t } from "./keycloak/react/hooks/useAuth.js";
|
|
3
|
-
import { useEffect as n, useRef as r } from "react";
|
|
4
|
-
import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
5
|
-
var s = {
|
|
6
|
-
loadingContainer: "_loadingContainer_1th3z_1",
|
|
7
|
-
spinner: "_spinner_1th3z_11",
|
|
8
|
-
spin: "_spin_1th3z_11",
|
|
9
|
-
errorContainer: "_errorContainer_1th3z_26",
|
|
10
|
-
errorMessage: "_errorMessage_1th3z_36"
|
|
11
|
-
};
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region lib/keycloak/react/ProtectedRoute/index.tsx
|
|
14
|
-
function c() {
|
|
15
|
-
return /* @__PURE__ */ o("div", {
|
|
16
|
-
className: s.loadingContainer,
|
|
17
|
-
children: [/* @__PURE__ */ a("div", { className: s.spinner }), /* @__PURE__ */ a("p", { children: "Authentication in progress..." })]
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
function l() {
|
|
21
|
-
return /* @__PURE__ */ a("div", {
|
|
22
|
-
className: s.errorContainer,
|
|
23
|
-
children: /* @__PURE__ */ a("p", {
|
|
24
|
-
className: s.errorMessage,
|
|
25
|
-
children: "Authentication error. Please try again later."
|
|
26
|
-
})
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
function u({ children: e, fallback: o, errorFallback: s }) {
|
|
30
|
-
let { status: u, login: d } = t(), f = r(!1);
|
|
31
|
-
return n(() => {
|
|
32
|
-
u === "unauthenticated" && !f.current && (f.current = !0, d().catch((e) => {
|
|
33
|
-
console.error("[dsi-keycloak] Login redirect failed:", e);
|
|
34
|
-
}));
|
|
35
|
-
}, [u, d]), u === "loading" || u === "unauthenticated" ? /* @__PURE__ */ a(i, { children: o ?? /* @__PURE__ */ a(c, {}) }) : u === "error" ? /* @__PURE__ */ a(i, { children: s ?? /* @__PURE__ */ a(l, {}) }) : /* @__PURE__ */ a(i, { children: e });
|
|
36
|
-
}
|
|
37
|
-
function d(e) {
|
|
38
|
-
return "config" in e && e.config != null;
|
|
39
|
-
}
|
|
40
|
-
function f(t) {
|
|
41
|
-
if (d(t)) {
|
|
42
|
-
let { config: n, idpHint: r, refreshInterval: i, minTokenValidity: o, onAuthError: s, ...c } = t;
|
|
43
|
-
return /* @__PURE__ */ a(e, {
|
|
44
|
-
config: n,
|
|
45
|
-
idpHint: r,
|
|
46
|
-
refreshInterval: i,
|
|
47
|
-
minTokenValidity: o,
|
|
48
|
-
onAuthError: s,
|
|
49
|
-
children: /* @__PURE__ */ a(u, { ...c })
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
return /* @__PURE__ */ a(u, { ...t });
|
|
53
|
-
}
|
|
54
|
-
//#endregion
|
|
55
|
-
export { f as t };
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
import { KeycloakConfig, KeycloakAuthOptions } from '../../core/types';
|
|
3
|
-
/**
|
|
4
|
-
* Props de base du composant `ProtectedRoute`.
|
|
5
|
-
*/
|
|
6
|
-
interface BaseProtectedRouteProps {
|
|
7
|
-
/** Contenu protégé, affiché une fois authentifié */
|
|
8
|
-
children: ReactNode;
|
|
9
|
-
/** Composant affiché pendant le chargement / la redirection (défaut: spinner) */
|
|
10
|
-
fallback?: ReactNode;
|
|
11
|
-
/** Composant affiché en cas d'erreur d'authentification (défaut: message d'erreur) */
|
|
12
|
-
errorFallback?: ReactNode;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Props pour le mode standalone (sans `KeycloakProvider` parent).
|
|
16
|
-
* Inclut la configuration Keycloak et les options d'authentification.
|
|
17
|
-
*/
|
|
18
|
-
interface StandaloneProtectedRouteProps extends BaseProtectedRouteProps, KeycloakAuthOptions {
|
|
19
|
-
/** Configuration du serveur Keycloak */
|
|
20
|
-
config: KeycloakConfig;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Props pour le mode contexte (à l'intérieur d'un `KeycloakProvider`).
|
|
24
|
-
*/
|
|
25
|
-
type ContextProtectedRouteProps = BaseProtectedRouteProps;
|
|
26
|
-
/**
|
|
27
|
-
* Union des props acceptées par `ProtectedRoute`.
|
|
28
|
-
*/
|
|
29
|
-
export type ProtectedRouteProps = StandaloneProtectedRouteProps | ContextProtectedRouteProps;
|
|
30
|
-
/**
|
|
31
|
-
* Composant de protection de route / zone authentifiée.
|
|
32
|
-
*
|
|
33
|
-
* Fonctionne en deux modes :
|
|
34
|
-
*
|
|
35
|
-
* **Mode standalone** (plug & play) : fournir `config` et les options directement.
|
|
36
|
-
* Crée automatiquement un `KeycloakProvider` interne.
|
|
37
|
-
*
|
|
38
|
-
* **Mode contexte** : utiliser à l'intérieur d'un `KeycloakProvider` parent.
|
|
39
|
-
* Ne nécessite que `children` et optionnellement `fallback` / `errorFallback`.
|
|
40
|
-
*
|
|
41
|
-
* Dans les deux cas, le composant :
|
|
42
|
-
* - Vérifie l'état d'authentification via `check-sso`
|
|
43
|
-
* - Redirige vers le login Keycloak (via l'IDP configuré) si non authentifié
|
|
44
|
-
* - Affiche un fallback pendant le chargement
|
|
45
|
-
* - Rend les enfants une fois authentifié
|
|
46
|
-
*
|
|
47
|
-
* Basé sur `keycloak-js`.
|
|
48
|
-
*
|
|
49
|
-
* @example Mode standalone (plug & play)
|
|
50
|
-
* ```tsx
|
|
51
|
-
* <ProtectedRoute
|
|
52
|
-
* config={{ url: 'https://keycloak.example.com', realm: 'my-realm', clientId: 'my-client' }}
|
|
53
|
-
* idpHint="oidc"
|
|
54
|
-
* >
|
|
55
|
-
* <Dashboard />
|
|
56
|
-
* </ProtectedRoute>
|
|
57
|
-
* ```
|
|
58
|
-
*
|
|
59
|
-
* @example Mode contexte (avec KeycloakProvider parent)
|
|
60
|
-
* ```tsx
|
|
61
|
-
* <KeycloakProvider config={...} idpHint="oidc">
|
|
62
|
-
* <ProtectedRoute>
|
|
63
|
-
* <Dashboard />
|
|
64
|
-
* </ProtectedRoute>
|
|
65
|
-
* </KeycloakProvider>
|
|
66
|
-
* ```
|
|
67
|
-
*/
|
|
68
|
-
export declare function ProtectedRoute(props: ProtectedRouteProps): import("react/jsx-runtime").JSX.Element;
|
|
69
|
-
export {};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { KeycloakAuthContext as e } from "../KeycloakProvider/index.js";
|
|
2
|
-
import { useContext as t } from "react";
|
|
3
|
-
//#region lib/keycloak/react/hooks/useAuth.ts
|
|
4
|
-
function n() {
|
|
5
|
-
let n = t(e);
|
|
6
|
-
if (!n) throw Error("[dsi-keycloak] useAuth() must be used within a <KeycloakProvider> or a <ProtectedRoute config={...}>.");
|
|
7
|
-
return n;
|
|
8
|
-
}
|
|
9
|
-
//#endregion
|
|
10
|
-
export { n as useAuth };
|
package/dist/style.css
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
._loadingContainer_1th3z_1{flex-direction:column;justify-content:center;align-items:center;gap:1rem;min-height:100vh;font-family:system-ui,-apple-system,sans-serif;display:flex}._spinner_1th3z_11{border:3px solid #e5e7eb;border-top-color:#3b82f6;border-radius:50%;width:40px;height:40px;animation:1s linear infinite _spin_1th3z_11}@keyframes _spin_1th3z_11{to{transform:rotate(360deg)}}._errorContainer_1th3z_26{flex-direction:column;justify-content:center;align-items:center;gap:1rem;min-height:100vh;font-family:system-ui,-apple-system,sans-serif;display:flex}._errorMessage_1th3z_36{color:#dc2626;text-align:center;background:#fef2f2;border-radius:12px;max-width:400px;padding:1.5rem}
|
|
2
|
-
/*$vite$:1*/
|