@bmc-soft/keycloak-auth 1.0.11 → 2.0.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.
Files changed (105) hide show
  1. package/README.md +123 -60
  2. package/dist/_lib/types/confirmAuthPhase.d.ts +1 -2
  3. package/dist/_lib/types/confirmAuthPhase.d.ts.map +1 -1
  4. package/dist/_lib/types/confirmAuthPhase.js +1 -2
  5. package/dist/_lib/types/confirmAuthPhase.js.map +1 -1
  6. package/dist/axios/adapters/KeycloakTokenProvider.js +8 -8
  7. package/dist/axios/adapters/KeycloakTokenProvider.js.map +1 -1
  8. package/dist/axios/interceptors.js +2 -2
  9. package/dist/axios/interceptors.js.map +1 -1
  10. package/dist/context/KeycloakConfigContext.d.ts +20 -1
  11. package/dist/context/KeycloakConfigContext.d.ts.map +1 -1
  12. package/dist/context/KeycloakConfigContext.js +52 -3
  13. package/dist/context/KeycloakConfigContext.js.map +1 -1
  14. package/dist/context/KeycloakInstanceContext.d.ts +2 -0
  15. package/dist/context/KeycloakInstanceContext.d.ts.map +1 -1
  16. package/dist/context/KeycloakInstanceContext.js +93 -18
  17. package/dist/context/KeycloakInstanceContext.js.map +1 -1
  18. package/dist/context/KeycloakProvider.d.ts +8 -1
  19. package/dist/context/KeycloakProvider.d.ts.map +1 -1
  20. package/dist/context/KeycloakProvider.js +3 -3
  21. package/dist/context/KeycloakProvider.js.map +1 -1
  22. package/dist/context/ReauthContext.d.ts +3 -1
  23. package/dist/context/ReauthContext.d.ts.map +1 -1
  24. package/dist/context/ReauthContext.js +6 -2
  25. package/dist/context/ReauthContext.js.map +1 -1
  26. package/dist/context/TokenContext.d.ts +2 -0
  27. package/dist/context/TokenContext.d.ts.map +1 -1
  28. package/dist/context/TokenContext.js +32 -10
  29. package/dist/context/TokenContext.js.map +1 -1
  30. package/dist/context/index.d.ts +2 -2
  31. package/dist/context/index.d.ts.map +1 -1
  32. package/dist/context/index.js +1 -1
  33. package/dist/context/index.js.map +1 -1
  34. package/dist/context/reauthRequiredRef.d.ts +2 -1
  35. package/dist/context/reauthRequiredRef.d.ts.map +1 -1
  36. package/dist/context/reauthRequiredRef.js.map +1 -1
  37. package/dist/core/adapter.d.ts.map +1 -1
  38. package/dist/core/adapter.js +37 -4
  39. package/dist/core/adapter.js.map +1 -1
  40. package/dist/core/index.d.ts +3 -1
  41. package/dist/core/index.d.ts.map +1 -1
  42. package/dist/core/index.js +1 -0
  43. package/dist/core/index.js.map +1 -1
  44. package/dist/core/sessionPolicy.d.ts +21 -0
  45. package/dist/core/sessionPolicy.d.ts.map +1 -0
  46. package/dist/core/sessionPolicy.js +63 -0
  47. package/dist/core/sessionPolicy.js.map +1 -0
  48. package/dist/core/types.d.ts +17 -0
  49. package/dist/core/types.d.ts.map +1 -1
  50. package/dist/hooks/useKeycloakAuth.d.ts +3 -0
  51. package/dist/hooks/useKeycloakAuth.d.ts.map +1 -1
  52. package/dist/hooks/useKeycloakAuth.js +12 -7
  53. package/dist/hooks/useKeycloakAuth.js.map +1 -1
  54. package/dist/hooks/useKeycloakAuthScreen.d.ts +1 -1
  55. package/dist/hooks/useKeycloakAuthScreen.d.ts.map +1 -1
  56. package/dist/hooks/useKeycloakAuthScreen.js +3 -2
  57. package/dist/hooks/useKeycloakAuthScreen.js.map +1 -1
  58. package/dist/index.d.ts +1 -1
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/screens/AuthPage/AuthPage.d.ts +2 -0
  62. package/dist/screens/AuthPage/AuthPage.d.ts.map +1 -1
  63. package/dist/screens/AuthPage/AuthPage.js +12 -15
  64. package/dist/screens/AuthPage/AuthPage.js.map +1 -1
  65. package/dist/screens/ConfirmAuthPage/ConfirmAuthPage.d.ts +3 -1
  66. package/dist/screens/ConfirmAuthPage/ConfirmAuthPage.d.ts.map +1 -1
  67. package/dist/screens/ConfirmAuthPage/ConfirmAuthPage.js +132 -57
  68. package/dist/screens/ConfirmAuthPage/ConfirmAuthPage.js.map +1 -1
  69. package/dist/screens/ConfirmAuthPage/index.d.ts +1 -0
  70. package/dist/screens/ConfirmAuthPage/index.d.ts.map +1 -1
  71. package/dist/screens/index.d.ts +1 -0
  72. package/dist/screens/index.d.ts.map +1 -1
  73. package/dist/storage/credentialStorage.d.ts +8 -0
  74. package/dist/storage/credentialStorage.d.ts.map +1 -1
  75. package/dist/storage/credentialStorage.js +99 -1
  76. package/dist/storage/credentialStorage.js.map +1 -1
  77. package/dist/storage/tokenStorage.d.ts +10 -0
  78. package/dist/storage/tokenStorage.d.ts.map +1 -1
  79. package/dist/storage/tokenStorage.js +30 -8
  80. package/dist/storage/tokenStorage.js.map +1 -1
  81. package/dist/ui/LogoutConfirmSheet/LogoutConfirmSheet.d.ts.map +1 -1
  82. package/dist/ui/LogoutConfirmSheet/LogoutConfirmSheet.js +10 -7
  83. package/dist/ui/LogoutConfirmSheet/LogoutConfirmSheet.js.map +1 -1
  84. package/dist/ui/NumberPad/NumberPad.d.ts.map +1 -1
  85. package/dist/ui/NumberPad/NumberPad.js +16 -9
  86. package/dist/ui/NumberPad/NumberPad.js.map +1 -1
  87. package/dist/ui/PINConfirm/PINConfirm.d.ts +1 -0
  88. package/dist/ui/PINConfirm/PINConfirm.d.ts.map +1 -1
  89. package/dist/ui/PINConfirm/PINConfirm.js +40 -12
  90. package/dist/ui/PINConfirm/PINConfirm.js.map +1 -1
  91. package/dist/ui/PINSetup/stages/BiometryStage.d.ts.map +1 -1
  92. package/dist/ui/PINSetup/stages/BiometryStage.js +6 -2
  93. package/dist/ui/PINSetup/stages/BiometryStage.js.map +1 -1
  94. package/dist/ui/PINSetup/styles.d.ts +0 -1
  95. package/dist/ui/PINSetup/styles.d.ts.map +1 -1
  96. package/dist/ui/PINSetup/styles.js +0 -1
  97. package/dist/ui/PINSetup/styles.js.map +1 -1
  98. package/dist/ui/WebViewLogin/WebViewLogin.d.ts.map +1 -1
  99. package/dist/ui/WebViewLogin/WebViewLogin.js +2 -2
  100. package/dist/ui/WebViewLogin/WebViewLogin.js.map +1 -1
  101. package/dist/widgets/ReauthBottomSheet/ReauthBottomSheet.d.ts +2 -0
  102. package/dist/widgets/ReauthBottomSheet/ReauthBottomSheet.d.ts.map +1 -1
  103. package/dist/widgets/ReauthBottomSheet/ReauthBottomSheet.js +19 -8
  104. package/dist/widgets/ReauthBottomSheet/ReauthBottomSheet.js.map +1 -1
  105. package/package.json +2 -1
package/README.md CHANGED
@@ -60,9 +60,9 @@ import { AuthPage } from '@bmc-soft/keycloak-auth';
60
60
  const LoginScreen = () => (
61
61
  <AuthPage
62
62
  logo={require('./logo.png')}
63
- onSuccess={(token) => {
64
- // Сохраняем токен в сессию приложения и переходим
65
- saveToken(token);
63
+ onSuccess={(accessToken) => {
64
+ // Сохраняем access token в сессию приложения и переходим
65
+ Session.events.onLogin(accessToken);
66
66
  navigation.replace('Home');
67
67
  }}
68
68
  onError={(err) => console.error(err)}
@@ -74,6 +74,18 @@ const LoginScreen = () => (
74
74
 
75
75
  Если используете axios, задайте провайдер токенов (см. [Axios](#axios)) и вызовите `setupAxiosInterceptors` с `tokenProvider` и `onReauthRequired`. Провайдер обычно настраивается внутри KeycloakProvider после инициализации keycloak (см. [Интеграция](#интеграция)).
76
76
 
77
+ ### Требования к Keycloak client
78
+
79
+ Для нового mobile flow настройте Keycloak client как:
80
+
81
+ - **Client type**: public
82
+ - **Flow**: Authorization Code / Standard Flow
83
+ - **PKCE**: required, `S256`
84
+ - **Scope**: разрешён `offline_access`
85
+ - **Redirect URI**: только URI приложения, например `myapp://callback`
86
+
87
+ `client_secret` не передаётся и не хранится в React Native приложении.
88
+
77
89
  ---
78
90
 
79
91
  ## Провайдер и конфигурация
@@ -83,14 +95,22 @@ const LoginScreen = () => (
83
95
  | Проп | Тип | Обязательный | Описание |
84
96
  |------|-----|--------------|----------|
85
97
  | `children` | ReactNode | да | Дерево приложения |
86
- | `config` | KeycloakConfigWith2FA | да | `url`, `realm`, `clientId`; опционально `clientId2fa` для первого входа с 2FA |
98
+ | `config` | KeycloakConfigWith2FA | да | `url`, `realm`, `clientId`; `clientId2fa` оставлен для legacy-сценариев, новый мобильный flow использует один public client + PKCE |
87
99
  | `redirectUri` | string | да | URI OAuth callback (например `myapp://callback`) |
88
100
  | `theme` | KeycloakTheme | нет | Цвета, шрифты, LoaderComponent, компоненты кнопок |
89
101
  | `onTokens` | (tokens: KeycloakTokens) => void | нет | Вызывается при изменении токенов (логин, refresh, logout) |
102
+ | `offlineAccessEnabled` | boolean | нет | Запрашивать `offline_access` в login URL; по умолчанию `true` |
103
+ | `backgroundReauth` | `{ enabled?: boolean; thresholdMs?: number; internalNetworkMode?: 'ip-host-is-internal' }` | нет | Настройки PIN/биометрии после возврата из background; по умолчанию `enabled: true`, `thresholdMs: 60000` |
104
+ | `clientSecret` | string | нет | Секрет confidential client для legacy-конфигураций; для новых мобильных flows предпочтителен public client + PKCE |
105
+ | `getClientSecret` | () => string \| undefined \| Promise<string \| undefined> | нет | Ленивый резолвер секрета; имеет приоритет над `clientSecret` |
106
+ | `initRecoveryStrategy` | `'none' \| 'clear-tokens-and-retry'` | нет | Что делать, если init со stored tokens упал; по умолчанию `none` |
107
+ | `deferStoredTokensUntilRefresh` | boolean | нет | Не передавать persisted tokens в `keycloak.init()` до локального PIN/биометрии; по умолчанию `false` |
90
108
  | `autoRefreshToken` | boolean | нет | По умолчанию `true` |
91
109
  | `autoRefreshTokenMinValidity` | number | нет | За сколько секунд до истечения вызывать refresh; по умолчанию `5` |
92
110
  | `onReauthRequired` | () => void | нет | Вызывается при ошибке обновления токена (например показать экран реавторизации) |
93
111
 
112
+ `deferStoredTokensUntilRefresh` полезен для mobile unlock flow: приложение может сначала показать PIN/биометрию, а уже после успешного локального подтверждения выполнить refresh через сохранённый offline token. Это предотвращает преждевременный расход refresh/offline token при старте приложения.
113
+
94
114
  ### KeycloakTheme
95
115
 
96
116
  Все поля опциональны; недостающие подставляются из дефолтной темы. Передаётся в KeycloakProvider как `theme`.
@@ -124,20 +144,27 @@ const LoginScreen = () => (
124
144
 
125
145
  #### ConfirmAuthPage
126
146
 
127
- Подтверждение сессии: фазы webview_detect pin webview_with_credentials (если сохранены учётные данные). Используется на экране «Подтверждение PIN» и внутри BottomSheet реавторизации.
147
+ Подтверждение сессии: сначала PIN/биометрия, затем продолжение с текущим access token или refresh через offline token. Если repeat-login не может восстановить токены через offline token, открывается обычный Keycloak login. Скрытый ввод username/password в новом flow не используется.
148
+
149
+ Режимы:
150
+
151
+ - `login` — повторный вход в приложение; при невозможности refresh открывает обычный Keycloak login.
152
+ - `unlock` — локальная разблокировка после background; refresh выполняется только если access token уже истёк.
153
+ - `reauth` — повторная авторизация после 401; refresh через offline token обязателен.
128
154
 
129
155
  | Проп | Тип | По умолчанию | Описание |
130
156
  |------|-----|--------------|----------|
131
- | `onSuccess` | () => void | — | Успех (редирект или PIN без credentials) |
157
+ | `onSuccess` | () => void | — | Успешное локальное подтверждение и восстановление/проверка access token |
132
158
  | `onError` | (error: Error) => void | — | Например неверный PIN |
133
159
  | `onLogout` | () => void | — | После очистки хранилища пакета; в приложении — очистить сессию и перейти на экран входа |
160
+ | `mode` | `'login' \| 'unlock' \| 'reauth'` | `"login"` | Сценарий: повторный вход, unlock после background или reauth после 401 |
134
161
  | `logoutText` | string | "Выйти из аккаунта" | Текст ссылки «Выйти» |
135
162
  | `logo`, `logoHeight`, `logoWidth` | — | 80 | Логотип |
136
163
  | `pinLength` | number | 4 | Длина PIN |
137
164
  | `allowBiometry`, `autoShowBiometry` | boolean | true | Биометрия |
138
165
  | `title`, `description` | string | — | Заголовок и описание блока PIN |
166
+ | `layout` | `'screen' \| 'sheet'` | `"screen"` | Компактная раскладка `sheet` для встраивания в BottomSheet |
139
167
  | `style` | ViewStyle | — | Стиль контейнера |
140
- | `webViewTimeoutMs` | number | 3000 | Задержка (мс) в фазе webview_detect перед переходом на PIN |
141
168
 
142
169
  ### Виджеты
143
170
 
@@ -152,10 +179,9 @@ BottomSheet с подтверждением PIN для реавторизаци
152
179
  | `onError` | (error: Error) => void | — | Ошибка ввода PIN |
153
180
  | `pinLength` | number | 4 | Длина PIN |
154
181
  | `allowBiometry`, `autoShowBiometry` | boolean | true | Биометрия |
155
- | `title` | string | "Подтвердите вход" | Заголовок |
182
+ | `mode` | `'login' \| 'unlock' \| 'reauth'` | авто | Если не передан, `background` открывается как `unlock`, остальные причины как `reauth` |
183
+ | `title` | string | "Введите PIN" | Заголовок |
156
184
  | `description` | string | "Введите PIN-код" | Описание |
157
- | `footer` | ReactNode | — | Опциональный футер |
158
- | `snapPointPercentage` | number | 50 | Высота sheet в % |
159
185
 
160
186
  ### Кнопки выхода
161
187
 
@@ -187,15 +213,15 @@ BottomSheet с подтверждением PIN для реавторизаци
187
213
 
188
214
  Полный API авторизации: инстанс keycloak, токен, login, logout, URL, состояние реавторизации.
189
215
 
190
- Возвращает: `keycloak`, `isInitialized`, `isLoading`, `error`, `token`, `isExpired`, `isAuthenticated`, `updateToken`, `login`, `logout`, `loadUserProfile`, `createLoginUrl`, `createLogoutUrl`, `clearTokens`, `isReauthRequired`, `showReauth`, `hideReauth`.
216
+ Возвращает: `keycloak`, `isInitialized`, `isLoading`, `error`, `accessToken`, `token`, `offlineToken`, `refreshToken`, `isExpired`, `isAuthenticated`, `updateToken`, `login`, `logout`, `loadUserProfile`, `createLoginUrl`, `createLogoutUrl`, `clearTokens`, `isReauthRequired`, `showReauth`, `hideReauth`.
191
217
 
192
218
  ### useToken()
193
219
 
194
- Доступ только к токенам (меньше ре-рендеров). Возвращает: `token`, `refreshToken`, `idToken`, `isExpired`, `updateToken`, `clearTokens`.
220
+ Доступ только к токенам (меньше ре-рендеров). Возвращает: `accessToken`, `token`, `offlineToken`, `refreshToken`, `idToken`, `isExpired`, `updateToken`, `clearTokens`.
195
221
 
196
222
  ### useReauth()
197
223
 
198
- Состояние UI реавторизации. Возвращает: `isReauthRequired`, `showReauth`, `hideReauth`.
224
+ Состояние UI реавторизации. Возвращает: `isReauthRequired`, `reauthReason`, `showReauth`, `hideReauth`.
199
225
 
200
226
  **Состояние isReauthRequired:**
201
227
 
@@ -209,7 +235,7 @@ BottomSheet с подтверждением PIN для реавторизаци
209
235
 
210
236
  Возвращает: `screen: 'login' | 'confirm' | null`, `isLoading`, `error`.
211
237
 
212
- Опции: `shouldShowConfirm?: () => Promise<boolean>` — переопределить стандартную проверку (наличие токенов и PIN).
238
+ Опции: `shouldShowConfirm?: () => Promise<boolean>` — переопределить стандартную проверку (наличие offline token и PIN).
213
239
 
214
240
  ### useKeycloakTheme()
215
241
 
@@ -221,15 +247,15 @@ BottomSheet с подтверждением PIN для реавторизаци
221
247
 
222
248
  ### tokenStorage
223
249
 
224
- Хранение токенов в Keychain. API: `getToken`, `saveToken`, `getRefreshToken`, `saveRefreshToken`, `getIdToken`, `saveIdToken`, `getTokens`, `saveTokens`, `clearTokens`, `hasTokens`.
250
+ Хранение токенов в Keychain. API: `getToken`/`getAccessToken`, `saveToken`/`saveAccessToken`, `getRefreshToken`/`getOfflineToken`, `saveRefreshToken`/`saveOfflineToken`, `getIdToken`, `saveIdToken`, `getTokens`, `saveTokens`, `clearTokens`, `hasTokens`.
225
251
 
226
- После успешного ConfirmAuthPage (например при реавторизации) вызовите `tokenStorage.getToken()`, чтобы получить текущий access token и обновить сессию приложения.
252
+ `refresh_token` из Keycloak при `offline_access` хранится как offline token. Он не отправляется в backend; backend получает только `Authorization: Bearer <accessToken>`. После успешного ConfirmAuthPage вызовите `tokenStorage.getAccessToken()`, чтобы получить текущий access token и обновить сессию приложения.
227
253
 
228
254
  > **Важно**: не храните токены в AsyncStorage. Используйте только Keychain через пакет.
229
255
 
230
256
  ### credentialStorage
231
257
 
232
- Используется внутри пакета для зашифрованных учётных данных и биометрии. Экспортируется для продвинутых сценариев (например очистка PIN/credentials при выходе).
258
+ Используется внутри пакета для PIN-verifier и биометрии. Legacy-хранение зашифрованных credentials оставлено для совместимости, но новый offline_access flow не использует username/password для повторного входа.
233
259
 
234
260
  ---
235
261
 
@@ -264,7 +290,7 @@ BottomSheet с подтверждением PIN для реавторизаци
264
290
 
265
291
  ### 1. Оборачивание приложения в KeycloakProvider
266
292
 
267
- Конфиг и `redirectUri` берут из настроек приложения. Тему (Loader, кнопки, цвета) передайте из темы приложения. В `onTokens` сохраняйте токен в хранилище сессии; в `onReauthRequired` показывайте экран реавторизации.
293
+ Конфиг и `redirectUri` берут из настроек приложения. Тему (Loader, кнопки, цвета) передайте из темы приложения. В `onTokens` сохраняйте access token в хранилище сессии; в `onReauthRequired` показывайте экран реавторизации.
268
294
 
269
295
  **Порядок провайдеров**: если используется `BottomSheetModalProvider` из `@gorhom/bottom-sheet` (например для реавторизации или других модалок), он должен находиться **внутри** KeycloakProvider. Иначе ReauthBottomSheet или ConfirmAuthPage внутри модалки не получат контекст Keycloak. Порядок: `KeycloakProvider` → `BottomSheetModalProvider` → остальное дерево приложения.
270
296
 
@@ -297,8 +323,8 @@ export const App = () => (
297
323
  }}
298
324
  redirectUri="myapp://callback"
299
325
  theme={theme}
300
- onTokens={({ token }) => {
301
- Session.events.onChangeAuthToken(token);
326
+ onTokens={({ accessToken }) => {
327
+ Session.events.onChangeAuthToken(accessToken);
302
328
  }}
303
329
  onReauthRequired={() => {
304
330
  Session.events.onRequireReauth();
@@ -313,26 +339,34 @@ export const App = () => (
313
339
 
314
340
  ### 2. Установка TokenProvider для axios
315
341
 
316
- Провайдер токенов настраивается один раз внутри KeycloakProvider после инициализации keycloak (например во вспомогательном компоненте). Не дублируйте настройку на экране логина.
342
+ Провайдер токенов настраивается один раз внутри KeycloakProvider после инициализации keycloak. Он отдаёт backend только access token, а refresh выполняет через offline token.
317
343
 
318
344
  ```tsx
319
- import { useKeycloakAuth } from '@bmc-soft/keycloak-auth';
320
- import { setKeycloakTokenProvider, resetKeycloakTokenProvider } from './axios';
345
+ import {
346
+ KeycloakTokenProvider,
347
+ setupAxiosInterceptors,
348
+ useKeycloakAuth,
349
+ } from '@bmc-soft/keycloak-auth';
350
+
351
+ const api = axios.create({ baseURL: 'https://api.example.com' });
321
352
 
322
353
  const KeycloakAxiosTokenProviderSetup = ({ children }) => {
323
354
  const { keycloak, isInitialized } = useKeycloakAuth();
324
355
 
325
356
  useEffect(() => {
326
- if (isInitialized && keycloak) {
327
- setKeycloakTokenProvider({
328
- getToken: () => keycloak.token || null,
329
- updateToken: async (minValidity) => {
330
- const result = await keycloak.updateToken(minValidity);
331
- return result !== null;
332
- },
333
- });
334
- }
335
- return () => resetKeycloakTokenProvider();
357
+ if (!isInitialized || !keycloak) return undefined;
358
+
359
+ const { cleanup } = setupAxiosInterceptors(api, {
360
+ tokenProvider: new KeycloakTokenProvider(keycloak),
361
+ onReauthRequired: () => {
362
+ Session.events.onRequireReauth();
363
+ },
364
+ onTokenRefreshed: (accessToken) => {
365
+ Session.events.onChangeAuthToken(accessToken);
366
+ },
367
+ });
368
+
369
+ return cleanup;
336
370
  }, [isInitialized, keycloak]);
337
371
 
338
372
  return <>{children}</>;
@@ -348,29 +382,27 @@ const KeycloakAxiosTokenProviderSetup = ({ children }) => {
348
382
  </KeycloakProvider>
349
383
  ```
350
384
 
351
- Подключение интерцепторов к инстансу axios (там, где он создаётся):
385
+ Если у приложения уже есть свой `tokenProvider`, его методы должны соблюдать тот же контракт:
352
386
 
353
387
  ```tsx
354
- import { setupAxiosInterceptors } from '@bmc-soft/keycloak-auth';
355
- import { getKeycloakTokenProvider } from './keycloakTokenProvider';
356
-
357
- const api = axios.create({ baseURL: 'https://api.example.com' });
358
-
359
388
  setupAxiosInterceptors(api, {
360
389
  tokenProvider: {
361
- getToken: () => getKeycloakTokenProvider()?.getToken() ?? null,
362
- refreshToken: () =>
363
- getKeycloakTokenProvider()
364
- ?.updateToken(120)
365
- .then((ok) => (ok ? getKeycloakTokenProvider()?.getToken() ?? null : null)),
390
+ getToken: () => keycloak.token || null,
391
+ refreshToken: async () => {
392
+ await keycloak.updateToken(-1);
393
+ return keycloak.token || null;
394
+ },
395
+ hasRefreshToken: () => Boolean(keycloak.refreshToken),
396
+ },
397
+ onReauthRequired: () => {
398
+ Session.events.onRequireReauth();
366
399
  },
367
- onReauthRequired: () => Session.events.onRequireReauth(),
368
400
  });
369
401
  ```
370
402
 
371
403
  ### 3. Стек авторизации (Login + Confirm)
372
404
 
373
- Два экрана: Login (AuthPage) и Confirm (ConfirmAuthPage). Начальный маршрут задаётся через `useKeycloakAuthScreen()`: показывать Confirm, если есть токены и PIN, иначе Login.
405
+ Два экрана: Login (AuthPage) и Confirm (ConfirmAuthPage). Начальный маршрут задаётся через `useKeycloakAuthScreen()`: показывать Confirm, если есть offline token и PIN, иначе Login.
374
406
 
375
407
  ```tsx
376
408
  import { useKeycloakAuthScreen, AuthPage, ConfirmAuthPage, tokenStorage } from '@bmc-soft/keycloak-auth';
@@ -395,7 +427,7 @@ export const AuthStack = () => {
395
427
  const LoginScreen = () => (
396
428
  <AuthPage
397
429
  logo={require('./logo.png')}
398
- onSuccess={(token) => Session.events.onLogin(token)}
430
+ onSuccess={(accessToken) => Session.events.onLogin(accessToken)}
399
431
  onError={(err) => console.error(err)}
400
432
  />
401
433
  );
@@ -404,8 +436,8 @@ const ConfirmScreen = () => {
404
436
  const navigation = useNavigation();
405
437
 
406
438
  const onSuccess = useCallback(async () => {
407
- const token = await tokenStorage.getToken();
408
- if (token) Session.events.onLogin(token);
439
+ const accessToken = await tokenStorage.getAccessToken();
440
+ if (accessToken) Session.events.onLogin(accessToken);
409
441
  }, []);
410
442
 
411
443
  return (
@@ -423,7 +455,7 @@ const ConfirmScreen = () => {
423
455
 
424
456
  ### 4. Реавторизация при 401
425
457
 
426
- При 401 интерцепторы вызывают `onReauthRequired`. Можно использовать `isReauthRequired` из `useKeycloakAuth()` как единственный источник правды для отображения реавторизации. Если используете готовый **ReauthBottomSheet** из пакета, он сам открывается и закрывается по `isReauthRequired` — достаточно смонтировать компонент и при необходимости передать коллбэки (`onSuccess`, `onDismiss`). Для кастомной обёртки (свой BottomSheet и контент) можно использовать **ConfirmAuthPage** и вручную управлять видимостью по `isReauthRequired`. После успешного ввода PIN получите токен из пакета, обновите сессию и закройте реавторизацию. Флаг `isReauthRequired` сбрасывается пакетом автоматически при успехе в ReauthBottomSheet/ConfirmAuthPage; в `onSuccess` достаточно своей логики (обновление сессии, закрытие sheet).
458
+ При 401 интерцепторы вызывают `onReauthRequired`. Можно использовать `isReauthRequired` из `useKeycloakAuth()` как единственный источник правды для отображения реавторизации. Если используете готовый **ReauthBottomSheet** из пакета, он сам открывается и закрывается по `isReauthRequired` — достаточно смонтировать компонент и при необходимости передать коллбэки (`onSuccess`, `onDismiss`). Для кастомной обёртки (свой BottomSheet и контент) можно использовать **ConfirmAuthPage** и вручную управлять видимостью по `isReauthRequired`. После успешного ввода PIN получите access token из пакета, обновите сессию и закройте реавторизацию. Флаг `isReauthRequired` сбрасывается пакетом автоматически при успехе в ReauthBottomSheet/ConfirmAuthPage; в `onSuccess` достаточно своей логики (обновление сессии, закрытие sheet).
427
459
 
428
460
  Пример с кастомным BottomSheet и ConfirmAuthPage (управление видимостью вручную):
429
461
 
@@ -440,9 +472,9 @@ export const CustomReauthSheet = () => {
440
472
  }, [showReauth]);
441
473
 
442
474
  const handleSuccess = useCallback(async () => {
443
- const token = await tokenStorage.getToken();
444
- if (token) {
445
- Session.events.onLogin(token);
475
+ const accessToken = await tokenStorage.getAccessToken();
476
+ if (accessToken) {
477
+ Session.events.onLogin(accessToken);
446
478
  Session.events.onReauthCompleted();
447
479
  }
448
480
  sheetRef.current?.close();
@@ -450,13 +482,35 @@ export const CustomReauthSheet = () => {
450
482
 
451
483
  return (
452
484
  <BottomSheet ref={sheetRef} snapPoints={['50%']} enablePanDownToClose>
453
- <ConfirmAuthPage onSuccess={handleSuccess} pinLength={4} />
485
+ <ConfirmAuthPage mode="reauth" onSuccess={handleSuccess} pinLength={4} />
454
486
  </BottomSheet>
455
487
  );
456
488
  };
457
489
  ```
458
490
 
459
- ### 5. Выход
491
+ ### 5. PIN/биометрия после возврата из background
492
+
493
+ Если `backgroundReauth.enabled !== false`, библиотека отслеживает `AppState`: при возврате из background/inactive через более чем `thresholdMs` миллисекунд открывается `ReauthBottomSheet`.
494
+
495
+ По умолчанию:
496
+
497
+ ```tsx
498
+ <KeycloakProvider
499
+ config={{ url: 'https://sso.example.com', realm: 'my-realm', clientId: 'my-app' }}
500
+ redirectUri="myapp://callback"
501
+ backgroundReauth={{
502
+ enabled: true,
503
+ thresholdMs: 60000,
504
+ internalNetworkMode: 'ip-host-is-internal',
505
+ }}
506
+ >
507
+ <App />
508
+ </KeycloakProvider>
509
+ ```
510
+
511
+ Правило внутренней сети: если host в `config.url` является IP-адресом, сеть считается внутренней и background sheet не показывается; если host является DNS-именем, сеть считается внешней.
512
+
513
+ ### 6. Выход
460
514
 
461
515
  Используйте LogoutButtonIcon или LogoutButtonText. По нажатию открывается Modal с WebViewLogout; при успешном выходе вызывается `onLogoutSuccess` — там очищайте сессию приложения.
462
516
 
@@ -494,17 +548,25 @@ KeycloakProvider → инициализация Keycloak → установка
494
548
 
495
549
  useKeycloakAuthScreen → Login (AuthPage) или Confirm (ConfirmAuthPage)
496
550
 
497
- onSuccessSession.onLogin(token) | onLogoutSession.onLogout + навигация
551
+ ConfirmPIN/биометриятекущий access token или refresh через offline token
552
+
553
+ onSuccess → Session.onLogin(accessToken) | onLogout → Session.onLogout + навигация
498
554
 
499
- axios 401 onReauthRequired → ReauthBottomSheet с ConfirmAuthPage
555
+ axios 401 / background > 60с вне внутренней сети → ReauthBottomSheet с ConfirmAuthPage
500
556
 
501
- onSuccess → tokenStorage.getToken() → Session.onLogin + onReauthCompleted → закрыть sheet
557
+ PIN/биометрияrefresh при необходимости → tokenStorage.getAccessToken() → закрыть sheet
502
558
  ```
503
559
 
504
560
  ---
505
561
 
506
562
  ## Архитектура и безопасность
507
563
 
564
+ ### Mobile client и offline_access
565
+
566
+ Для мобильного приложения используйте Keycloak public client + Authorization Code Flow with PKCE (`S256`). `client_secret` не должен попадать в React Native приложение; confidential client допустим только через backend/BFF.
567
+
568
+ При `offlineAccessEnabled=true` библиотека добавляет `offline_access` в login scope. Keycloak возвращает offline token в поле `refresh_token`; библиотека хранит его в Keychain как `offlineToken`/`refreshToken` и использует только для получения новых access token.
569
+
508
570
  ### Иерархия контекстов
509
571
 
510
572
  ```
@@ -520,9 +582,10 @@ KeycloakConfigProvider ← конфиг (редко меняется)
520
582
  ### Безопасность
521
583
 
522
584
  - Токены в OS Keychain (не AsyncStorage)
523
- - PIN шифруется AES перед сохранением
585
+ - PIN хранится как verifier в Keychain; legacy-хранилище encrypted credentials сохранено только для обратной совместимости
586
+ - Offline token не отправляется в backend и используется только для refresh в Keycloak
524
587
  - Запросы по HTTPS
525
- - Учётные данные не хранятся в открытом виде
588
+ - Username/password не используются для repeat-login в новом offline_access flow
526
589
 
527
590
 
528
591
  ## Экспорты
@@ -1,7 +1,6 @@
1
1
  export declare const CONFIRM_AUTH_PHASE: {
2
- readonly WEBVIEW_DETECT: "webview_detect";
3
2
  readonly PIN: "pin";
4
- readonly WEBVIEW_WITH_CREDENTIALS: "webview_with_credentials";
3
+ readonly WEBVIEW_LOGIN: "webview_login";
5
4
  };
6
5
  export type ConfirmAuthPhase = (typeof CONFIRM_AUTH_PHASE)[keyof typeof CONFIRM_AUTH_PHASE];
7
6
  //# sourceMappingURL=confirmAuthPhase.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"confirmAuthPhase.d.ts","sourceRoot":"","sources":["../../../src/_lib/types/confirmAuthPhase.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,kBAAkB;;;;CAIrB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC"}
1
+ {"version":3,"file":"confirmAuthPhase.d.ts","sourceRoot":"","sources":["../../../src/_lib/types/confirmAuthPhase.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,kBAAkB;;;CAGrB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC"}
@@ -1,6 +1,5 @@
1
1
  export const CONFIRM_AUTH_PHASE = {
2
- WEBVIEW_DETECT: 'webview_detect',
3
2
  PIN: 'pin',
4
- WEBVIEW_WITH_CREDENTIALS: 'webview_with_credentials',
3
+ WEBVIEW_LOGIN: 'webview_login',
5
4
  };
6
5
  //# sourceMappingURL=confirmAuthPhase.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"confirmAuthPhase.js","sourceRoot":"","sources":["../../../src/_lib/types/confirmAuthPhase.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,cAAc,EAAE,gBAAgB;IAChC,GAAG,EAAE,KAAK;IACV,wBAAwB,EAAE,0BAA0B;CAC5C,CAAC"}
1
+ {"version":3,"file":"confirmAuthPhase.js","sourceRoot":"","sources":["../../../src/_lib/types/confirmAuthPhase.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,GAAG,EAAE,KAAK;IACV,aAAa,EAAE,eAAe;CACtB,CAAC"}
@@ -10,7 +10,7 @@ export class KeycloakTokenProvider {
10
10
  return token;
11
11
  }
12
12
  const tokens = await tokenStorage.getTokens();
13
- return tokens?.token || null;
13
+ return tokens?.accessToken || tokens?.token || null;
14
14
  }
15
15
  catch (error) {
16
16
  console.error('[KeycloakTokenProvider] Get token error:', error);
@@ -19,15 +19,15 @@ export class KeycloakTokenProvider {
19
19
  }
20
20
  async refreshToken() {
21
21
  try {
22
- const updated = await this.keycloakClient.updateToken(120);
23
- if (!updated) {
24
- return null;
25
- }
22
+ const updated = await this.keycloakClient.updateToken(-1);
26
23
  const { token, refreshToken, idToken } = this.keycloakClient;
27
24
  if (token) {
25
+ if (!updated) {
26
+ return token;
27
+ }
28
28
  await tokenStorage.saveTokens({
29
- token,
30
- refreshToken: refreshToken ?? undefined,
29
+ accessToken: token,
30
+ offlineToken: refreshToken ?? undefined,
31
31
  idToken: idToken ?? undefined,
32
32
  });
33
33
  return token;
@@ -45,7 +45,7 @@ export class KeycloakTokenProvider {
45
45
  return true;
46
46
  }
47
47
  const tokens = await tokenStorage.getTokens();
48
- return !!tokens?.refreshToken;
48
+ return !!(tokens?.offlineToken || tokens?.refreshToken);
49
49
  }
50
50
  catch (error) {
51
51
  console.error('[KeycloakTokenProvider] Has refresh token error:', error);
@@ -1 +1 @@
1
- {"version":3,"file":"KeycloakTokenProvider.js","sourceRoot":"","sources":["../../../src/axios/adapters/KeycloakTokenProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAuBxD,MAAM,OAAO,qBAAqB;IAGhC,YAAY,cAAyC;QACnD,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACvC,CAAC;IAMD,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC;YAEH,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;YACxC,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,KAAK,CAAC;YACf,CAAC;YAGD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,SAAS,EAAE,CAAC;YAC9C,OAAO,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,KAAK,CAAC,CAAC;YACjE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IASD,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YAE3D,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC;YACd,CAAC;YAGD,MAAM,EAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAC,GAAG,IAAI,CAAC,cAAc,CAAC;YAC3D,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,YAAY,CAAC,UAAU,CAAC;oBAC5B,KAAK;oBACL,YAAY,EAAE,YAAY,IAAI,SAAS;oBACvC,OAAO,EAAE,OAAO,IAAI,SAAS;iBAC9B,CAAC,CAAC;gBACH,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8CAA8C,EAAE,KAAK,CAAC,CAAC;YACrE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAKD,KAAK,CAAC,eAAe;QACnB,IAAI,CAAC;YAEH,IAAI,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC;gBACrC,OAAO,IAAI,CAAC;YACd,CAAC;YAGD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,SAAS,EAAE,CAAC;YAC9C,OAAO,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,kDAAkD,EAAE,KAAK,CAAC,CAAC;YACzE,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CAOF"}
1
+ {"version":3,"file":"KeycloakTokenProvider.js","sourceRoot":"","sources":["../../../src/axios/adapters/KeycloakTokenProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAuBxD,MAAM,OAAO,qBAAqB;IAGhC,YAAY,cAAyC;QACnD,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACvC,CAAC;IAMD,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC;YAEH,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;YACxC,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,KAAK,CAAC;YACf,CAAC;YAGD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,SAAS,EAAE,CAAC;YAC9C,OAAO,MAAM,EAAE,WAAW,IAAI,MAAM,EAAE,KAAK,IAAI,IAAI,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,KAAK,CAAC,CAAC;YACjE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IASD,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;YAG1D,MAAM,EAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAC,GAAG,IAAI,CAAC,cAAc,CAAC;YAC3D,IAAI,KAAK,EAAE,CAAC;gBACV,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,KAAK,CAAC;gBACf,CAAC;gBAED,MAAM,YAAY,CAAC,UAAU,CAAC;oBAC5B,WAAW,EAAE,KAAK;oBAClB,YAAY,EAAE,YAAY,IAAI,SAAS;oBACvC,OAAO,EAAE,OAAO,IAAI,SAAS;iBAC9B,CAAC,CAAC;gBACH,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8CAA8C,EAAE,KAAK,CAAC,CAAC;YACrE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAKD,KAAK,CAAC,eAAe;QACnB,IAAI,CAAC;YAEH,IAAI,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC;gBACrC,OAAO,IAAI,CAAC;YACd,CAAC;YAGD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,SAAS,EAAE,CAAC;YAC9C,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,YAAY,IAAI,MAAM,EAAE,YAAY,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,kDAAkD,EAAE,KAAK,CAAC,CAAC;YACzE,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CAOF"}
@@ -35,7 +35,7 @@ export const setupAxiosInterceptors = (axiosInstance, config = {}) => {
35
35
  }
36
36
  else {
37
37
  const tokens = await tokenStorage.getTokens();
38
- token = tokens?.token || null;
38
+ token = tokens?.accessToken || tokens?.token || null;
39
39
  }
40
40
  if (token) {
41
41
  const formattedToken = tokenProvider?.formatToken?.(token) || `Bearer ${token}`;
@@ -96,7 +96,7 @@ export const setupAxiosInterceptors = (axiosInstance, config = {}) => {
96
96
  }
97
97
  else {
98
98
  const tokens = await tokenStorage.getTokens();
99
- if (!tokens?.refreshToken) {
99
+ if (!tokens?.offlineToken && !tokens?.refreshToken) {
100
100
  throw new Error('No refresh token available');
101
101
  }
102
102
  console.warn('[AxiosInterceptors] Using deprecated built-in refresh logic. Consider using tokenProvider instead.');
@@ -1 +1 @@
1
- {"version":3,"file":"interceptors.js","sourceRoot":"","sources":["../../src/axios/interceptors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"interceptors.js","sourceRoot":"","sources":["../../src/axios/interceptors.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,29 @@
1
1
  import React, { type ReactNode } from 'react';
2
- import type { KeycloakConfig, KeycloakConfigWith2FA } from '../core/types';
2
+ import type { BackgroundReauthConfig, InitRecoveryStrategy, KeycloakConfig, KeycloakConfigWith2FA } from '../core';
3
+ interface KeycloakConfigContextValue {
4
+ config: KeycloakConfig;
5
+ isConfigReady: boolean;
6
+ offlineAccessEnabled: boolean;
7
+ backgroundReauth: BackgroundReauthConfig;
8
+ clientSecret?: string;
9
+ getClientSecret?: () => string | undefined | Promise<string | undefined>;
10
+ initRecoveryStrategy: InitRecoveryStrategy;
11
+ deferStoredTokensUntilRefresh: boolean;
12
+ refreshPinCheck: () => Promise<void>;
13
+ }
3
14
  export interface KeycloakConfigProviderProps {
4
15
  children: ReactNode;
5
16
  config: KeycloakConfigWith2FA;
17
+ offlineAccessEnabled?: boolean;
18
+ backgroundReauth?: BackgroundReauthConfig;
19
+ clientSecret?: string;
20
+ getClientSecret?: () => string | undefined | Promise<string | undefined>;
21
+ initRecoveryStrategy?: InitRecoveryStrategy;
22
+ deferStoredTokensUntilRefresh?: boolean;
6
23
  }
7
24
  export declare const KeycloakConfigProvider: React.NamedExoticComponent<KeycloakConfigProviderProps>;
8
25
  export declare const useKeycloakConfig: () => KeycloakConfig;
26
+ export declare const useKeycloakSessionOptions: () => Pick<KeycloakConfigContextValue, "offlineAccessEnabled" | "isConfigReady" | "backgroundReauth" | "clientSecret" | "getClientSecret" | "initRecoveryStrategy" | "deferStoredTokensUntilRefresh">;
9
27
  export declare const useKeycloakConfigRefresh: () => (() => Promise<void>);
28
+ export {};
10
29
  //# sourceMappingURL=KeycloakConfigContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"KeycloakConfigContext.d.ts","sourceRoot":"","sources":["../../src/context/KeycloakConfigContext.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,EAEZ,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,cAAc,EAAE,qBAAqB,EAAC,MAAM,eAAe,CAAC;AAWzE,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,SAAS,CAAC;IAEpB,MAAM,EAAE,qBAAqB,CAAC;CAC/B;AAiBD,eAAO,MAAM,sBAAsB,yDAmClC,CAAC;AASF,eAAO,MAAM,iBAAiB,QAAO,cAQpC,CAAC;AAKF,eAAO,MAAM,wBAAwB,QAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAM/D,CAAC"}
1
+ {"version":3,"file":"KeycloakConfigContext.d.ts","sourceRoot":"","sources":["../../src/context/KeycloakConfigContext.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,EAEZ,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EACV,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAGjB,UAAU,0BAA0B;IAClC,MAAM,EAAE,cAAc,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,EAAE,sBAAsB,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACzE,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,6BAA6B,EAAE,OAAO,CAAC;IAEvC,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAID,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,SAAS,CAAC;IAEpB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACzE,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,6BAA6B,CAAC,EAAE,OAAO,CAAC;CACzC;AAiBD,eAAO,MAAM,sBAAsB,yDAkFlC,CAAC;AASF,eAAO,MAAM,iBAAiB,QAAO,cAQpC,CAAC;AAEF,eAAO,MAAM,yBAAyB,QAAO,IAAI,CAC/C,0BAA0B,EACxB,sBAAsB,GACtB,eAAe,GACf,kBAAkB,GAClB,cAAc,GACd,iBAAiB,GACjB,sBAAsB,GACtB,+BAA+B,CAiBlC,CAAC;AAKF,eAAO,MAAM,wBAAwB,QAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAM/D,CAAC"}
@@ -11,14 +11,22 @@ function getEffectiveConfig(config, pinSet) {
11
11
  ...(config.oidcProvider != null && { oidcProvider: config.oidcProvider }),
12
12
  };
13
13
  }
14
- export const KeycloakConfigProvider = React.memo(({ children, config }) => {
14
+ export const KeycloakConfigProvider = React.memo(({ children, config, offlineAccessEnabled, backgroundReauth, clientSecret, getClientSecret, initRecoveryStrategy, deferStoredTokensUntilRefresh, }) => {
15
15
  const [pinSet, setPinSet] = useState(null);
16
16
  useEffect(() => {
17
17
  let mounted = true;
18
- credentialStorage.hasPIN().then(hasPin => {
18
+ credentialStorage
19
+ .hasPIN()
20
+ .then(hasPin => {
19
21
  if (mounted) {
20
22
  setPinSet(hasPin);
21
23
  }
24
+ })
25
+ .catch(error => {
26
+ console.error('[KeycloakConfigProvider] PIN check error:', error);
27
+ if (mounted) {
28
+ setPinSet(false);
29
+ }
22
30
  });
23
31
  return () => {
24
32
  mounted = false;
@@ -29,7 +37,33 @@ export const KeycloakConfigProvider = React.memo(({ children, config }) => {
29
37
  setPinSet(hasPin);
30
38
  }, []);
31
39
  const effectiveConfig = useMemo(() => getEffectiveConfig(config, pinSet), [config, pinSet]);
32
- const value = useMemo(() => ({ config: effectiveConfig, refreshPinCheck }), [effectiveConfig, refreshPinCheck]);
40
+ const resolvedOfflineAccessEnabled = offlineAccessEnabled ?? config.offlineAccessEnabled ?? true;
41
+ const resolvedClientSecret = clientSecret ?? config.clientSecret;
42
+ const resolvedGetClientSecret = getClientSecret ?? config.getClientSecret;
43
+ const resolvedInitRecoveryStrategy = initRecoveryStrategy ?? 'none';
44
+ const resolvedDeferStoredTokensUntilRefresh = deferStoredTokensUntilRefresh ?? false;
45
+ const resolvedBackgroundReauth = useMemo(() => backgroundReauth ?? config.backgroundReauth ?? {}, [backgroundReauth, config.backgroundReauth]);
46
+ const value = useMemo(() => ({
47
+ config: effectiveConfig,
48
+ isConfigReady: pinSet !== null,
49
+ offlineAccessEnabled: resolvedOfflineAccessEnabled,
50
+ backgroundReauth: resolvedBackgroundReauth,
51
+ clientSecret: resolvedClientSecret,
52
+ getClientSecret: resolvedGetClientSecret,
53
+ initRecoveryStrategy: resolvedInitRecoveryStrategy,
54
+ deferStoredTokensUntilRefresh: resolvedDeferStoredTokensUntilRefresh,
55
+ refreshPinCheck,
56
+ }), [
57
+ effectiveConfig,
58
+ pinSet,
59
+ resolvedOfflineAccessEnabled,
60
+ resolvedBackgroundReauth,
61
+ resolvedClientSecret,
62
+ resolvedGetClientSecret,
63
+ resolvedInitRecoveryStrategy,
64
+ resolvedDeferStoredTokensUntilRefresh,
65
+ refreshPinCheck,
66
+ ]);
33
67
  return (<KeycloakConfigContext.Provider value={value}>{children}</KeycloakConfigContext.Provider>);
34
68
  });
35
69
  KeycloakConfigProvider.displayName = 'KeycloakConfigProvider';
@@ -40,6 +74,21 @@ export const useKeycloakConfig = () => {
40
74
  }
41
75
  return context.config;
42
76
  };
77
+ export const useKeycloakSessionOptions = () => {
78
+ const context = useContext(KeycloakConfigContext);
79
+ if (!context) {
80
+ throw new Error('useKeycloakSessionOptions must be used within KeycloakConfigProvider');
81
+ }
82
+ return {
83
+ offlineAccessEnabled: context.offlineAccessEnabled,
84
+ isConfigReady: context.isConfigReady,
85
+ backgroundReauth: context.backgroundReauth,
86
+ clientSecret: context.clientSecret,
87
+ getClientSecret: context.getClientSecret,
88
+ initRecoveryStrategy: context.initRecoveryStrategy,
89
+ deferStoredTokensUntilRefresh: context.deferStoredTokensUntilRefresh,
90
+ };
91
+ };
43
92
  export const useKeycloakConfigRefresh = () => {
44
93
  const context = useContext(KeycloakConfigContext);
45
94
  if (!context) {