@ssoeasy-dev/react 1.0.0-beta.64cfe3a → 1.0.0-beta.8fb23c6

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 ADDED
@@ -0,0 +1,179 @@
1
+ # @ssoeasy-dev/react
2
+
3
+ React-адаптер для SSO Easy. Оборачивает `@ssoeasy-dev/core` в React Context и предоставляет хук `useAuth` и компонент `ProtectedRoute`.
4
+
5
+ ## Установка
6
+
7
+ ```bash
8
+ npm install @ssoeasy-dev/react @ssoeasy-dev/core
9
+ ```
10
+
11
+ ## Быстрый старт
12
+
13
+ ### 1. Оборачиваем приложение в `AuthProvider`
14
+
15
+ ```tsx
16
+ import { AuthProvider } from "@ssoeasy-dev/react";
17
+ import { AuthManager } from "@ssoeasy-dev/core";
18
+
19
+ const auth = new AuthManager({
20
+ serviceId: "your-service-id",
21
+ redirectUri: "https://yourapp.com",
22
+ loginPath: "/callback",
23
+ authPageUrl: "https://sso.example.com/login",
24
+ authServerConfig: {
25
+ baseURL: "https://api.example.com",
26
+ },
27
+ });
28
+
29
+ function App() {
30
+ return (
31
+ <AuthProvider auth={auth}>
32
+ <AppRouter />
33
+ </AuthProvider>
34
+ );
35
+ }
36
+ ```
37
+
38
+ ### 2. Защищаем роуты через `ProtectedRoute`
39
+
40
+ ```tsx
41
+ import { BrowserRouter, Route, Routes } from "react-router-dom";
42
+ import { ProtectedRoute } from "@ssoeasy-dev/react";
43
+
44
+ function AppRouter() {
45
+ return (
46
+ <BrowserRouter>
47
+ <Routes>
48
+ <Route path="/callback" element={<CallbackPage />} />
49
+ <Route path="/" element={<PublicPage />} />
50
+ <Route
51
+ path="/dashboard"
52
+ element={
53
+ <ProtectedRoute redirectTo="/dashboard">
54
+ <Dashboard />
55
+ </ProtectedRoute>
56
+ }
57
+ />
58
+ </Routes>
59
+ </BrowserRouter>
60
+ );
61
+ }
62
+ ```
63
+
64
+ ### 3. Используем `useAuth` в компонентах
65
+
66
+ ```tsx
67
+ import { useAuth } from "@ssoeasy-dev/react";
68
+
69
+ function Header() {
70
+ const auth = useAuth();
71
+
72
+ return (
73
+ <header>
74
+ {auth.getState().isAuthenticated ? (
75
+ <button onClick={() => auth.logout()}>Выйти</button>
76
+ ) : (
77
+ <button onClick={() => auth.login()}>Войти</button>
78
+ )}
79
+ </header>
80
+ );
81
+ }
82
+ ```
83
+
84
+ ### 4. Страница callback
85
+
86
+ ```tsx
87
+ import { useEffect } from "react";
88
+ import { useAuth } from "@ssoeasy-dev/react";
89
+ import { useNavigate } from "react-router-dom";
90
+
91
+ function CallbackPage() {
92
+ const auth = useAuth();
93
+ const navigate = useNavigate();
94
+
95
+ useEffect(() => {
96
+ auth.handleRedirectCallback().then(({ redirectTo }) => {
97
+ navigate(redirectTo, { replace: true });
98
+ });
99
+ }, []);
100
+
101
+ return <div>Авторизация...</div>;
102
+ }
103
+ ```
104
+
105
+ ## API
106
+
107
+ ### `AuthProvider`
108
+
109
+ ```tsx
110
+ <AuthProvider auth={AuthManager}>{children}</AuthProvider>
111
+ ```
112
+
113
+ Предоставляет `AuthManager` через React Context. Должен оборачивать всё приложение.
114
+
115
+ ### `useAuth`
116
+
117
+ ```tsx
118
+ const auth = useAuth(); // возвращает AuthManager
119
+ ```
120
+
121
+ Хук для доступа к `AuthManager` внутри компонентов. Выбрасывает ошибку если вызван вне `AuthProvider`.
122
+
123
+ ### `ProtectedRoute`
124
+
125
+ ```tsx
126
+ <ProtectedRoute
127
+ redirectTo="/target-path" // путь для redirectTo при логине (опционально)
128
+ fallback={<Spinner />} // отображается во время checkAuth (по умолчанию <div>Loading...</div>)
129
+ unauthenticatedElement={<Node />} // рендерится вместо редиректа на SSO, если пользователь не авторизован (опционально)
130
+ >
131
+ <ProtectedContent />
132
+ </ProtectedRoute>
133
+ ```
134
+
135
+ При монтировании вызывает `auth.checkAuth()`. Пока идёт проверка — рендерит `fallback`.
136
+
137
+ Если пользователь не авторизован:
138
+
139
+ - **`unauthenticatedElement` не передан** — вызывает `auth.login({ redirectTo })`, редирект на SSO (поведение по умолчанию)
140
+ - **`unauthenticatedElement` передан** — рендерит переданный элемент без редиректа
141
+
142
+ #### Пример: внутренняя страница выбора входа/регистрации
143
+
144
+ Если в приложении есть собственная страница `/auth` с кнопками «Войти» и «Зарегистрироваться», можно передать `<Navigate>` вместо того, чтобы сразу уходить на SSO:
145
+
146
+ ```tsx
147
+ import { Navigate } from "react-router-dom";
148
+ import { ProtectedRoute } from "@ssoeasy-dev/react";
149
+
150
+ const LayoutWrapper = () => {
151
+ return (
152
+ <ProtectedRoute unauthenticatedElement={<Navigate to="/auth" replace />}>
153
+ <Layout>
154
+ <Outlet />
155
+ </Layout>
156
+ </ProtectedRoute>
157
+ );
158
+ };
159
+ ```
160
+
161
+ Страница `/auth` при этом остаётся публичной — она не обёрнута в `ProtectedRoute`.
162
+
163
+ ## Экспорты
164
+
165
+ ```typescript
166
+ export { AuthProvider } from "./AuthProvider";
167
+ export { ProtectedRoute } from "./ProtectedRoute";
168
+ export { useAuth } from "./useAuth";
169
+ ```
170
+
171
+ ## Лицензия
172
+
173
+ MIT — см. [LICENSE](../LICENSE).
174
+
175
+ ## Контакты
176
+
177
+ - Email: morewiktor@yandex.ru
178
+ - Telegram: [@MoreWiktor](https://t.me/MoreWiktor)
179
+ - GitHub: [@MoreWiktor](https://github.com/MoreWiktor)
@@ -3,5 +3,6 @@ export declare const ProtectedRoute: React.FC<{
3
3
  children: React.ReactNode;
4
4
  redirectTo?: string;
5
5
  fallback?: React.ReactNode;
6
+ unauthenticatedElement?: React.ReactNode;
6
7
  }>;
7
8
  //# sourceMappingURL=ProtectedRoute.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProtectedRoute.d.ts","sourceRoot":"","sources":["../src/ProtectedRoute.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC;IACpC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAgBA,CAAC"}
1
+ {"version":3,"file":"ProtectedRoute.d.ts","sourceRoot":"","sources":["../src/ProtectedRoute.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC;IACpC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,sBAAsB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC1C,CA0BA,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
3
  import { useAuth } from "./useAuth";
4
- export const ProtectedRoute = ({ children, redirectTo, fallback = _jsx("div", { children: "Loading..." }) }) => {
4
+ export const ProtectedRoute = ({ children, redirectTo, fallback = _jsx("div", { children: "Loading..." }), unauthenticatedElement, }) => {
5
5
  const auth = useAuth();
6
6
  const [isAuthorized, setIsAuthorized] = useState(null);
7
7
  useEffect(() => {
@@ -12,6 +12,9 @@ export const ProtectedRoute = ({ children, redirectTo, fallback = _jsx("div", {
12
12
  if (isAuthorized === null)
13
13
  return fallback;
14
14
  if (!isAuthorized) {
15
+ if (unauthenticatedElement !== undefined) {
16
+ return _jsx(_Fragment, { children: unauthenticatedElement });
17
+ }
15
18
  auth.login({ redirectTo });
16
19
  return null;
17
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ProtectedRoute.js","sourceRoot":"","sources":["../src/ProtectedRoute.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,MAAM,cAAc,GAItB,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,GAAG,uCAAqB,EAAE,EAAE,EAAE;IAClE,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAiB,IAAI,CAAC,CAAC;IAEvE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YAC5B,eAAe,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,IAAI,YAAY,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IAC3C,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,4BAAG,QAAQ,GAAI,CAAC;AACzB,CAAC,CAAC"}
1
+ {"version":3,"file":"ProtectedRoute.js","sourceRoot":"","sources":["../src/ProtectedRoute.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,MAAM,cAAc,GAKtB,CAAC,EACJ,QAAQ,EACR,UAAU,EACV,QAAQ,GAAG,uCAAqB,EAChC,sBAAsB,GACvB,EAAE,EAAE;IACH,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAiB,IAAI,CAAC,CAAC;IAEvE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YAC5B,eAAe,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,IAAI,YAAY,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IAE3C,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,IAAI,sBAAsB,KAAK,SAAS,EAAE,CAAC;YACzC,OAAO,4BAAG,sBAAsB,GAAI,CAAC;QACvC,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,4BAAG,QAAQ,GAAI,CAAC;AACzB,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ssoeasy-dev/react",
3
- "version": "1.0.0-beta.64cfe3a",
3
+ "version": "1.0.0-beta.8fb23c6",
4
4
  "description": "",
5
5
  "main": "index.ts",
6
6
  "types": "index.ts",
@@ -15,9 +15,9 @@
15
15
  "README.md",
16
16
  "LICENSE"
17
17
  ],
18
- "dependencies": {
19
- "@ssoeasy-dev/core": "1.0.0-beta.2bdd988",
20
- "react": "^19.2.4"
18
+ "peerDependencies": {
19
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
20
+ "@ssoeasy-dev/core": "1.0.0-beta.89a173f"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/react": "^19.2.14",