@riligar/auth-react 1.22.0 → 1.24.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/dist/index.esm.js CHANGED
@@ -6,7 +6,6 @@ import { Navigate, Outlet, useNavigate } from 'react-router-dom';
6
6
  import { Modal, Stack, Text, Image, Title, Paper, TextInput, PasswordInput, Anchor, Button, Divider, Group, Center, Loader, Box, Avatar, Collapse, Card, Tooltip, ThemeIcon, Badge } from '@mantine/core';
7
7
  import { useForm } from '@mantine/form';
8
8
  import { IconMail, IconLock, IconArrowRight, IconBrandGoogle, IconBrandGithub, IconUser, IconSend, IconCheck, IconX, IconRefresh, IconPhoto, IconTrash, IconPencil, IconShield, IconKey, IconDevices, IconDeviceMobile, IconLogout, IconUserCircle } from '@tabler/icons-react';
9
- import { notifications } from '@mantine/notifications';
10
9
 
11
10
  // Config - API Base URL fixa para o serviço de autenticação
12
11
  let API_BASE = 'https://manager.myauth.click';
@@ -750,32 +749,39 @@ const useAuthStore = create((set, get) => ({
750
749
  if (isAuthenticated()) {
751
750
  const token = window.localStorage.getItem('auth:token');
752
751
  if (token) {
753
- // Decodifica o token para verificar tempo de expiração
754
- const base64Url = token.split('.')[1];
755
- const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
756
- const jsonPayload = window.atob(base64);
757
- const payload = JSON.parse(jsonPayload);
752
+ // Usa o decoder oficial do SDK que é mais seguro
753
+ const payload = decodeJWT(token);
754
+
755
+ // Se não for um JWT ou não tiver expiração, não fazemos refresh em background
756
+ // O backend cuidará da expiração da sessão opaca via 401 nas requisições normais
757
+ if (!payload || !payload.exp) return;
758
758
  const now = Date.now() / 1000;
759
759
  const timeUntilExpiry = payload.exp - now;
760
760
 
761
- // Se o token expira em menos de 5 minutos, faz refresh
761
+ // Se o token expira em menos de 5 minutos, tenta o refresh
762
762
  if (timeUntilExpiry < 300) {
763
- // 5 minutos
764
- await refreshToken();
765
- const user = getCurrentUser();
766
- set({
767
- user
768
- });
763
+ try {
764
+ await refreshToken();
765
+ const user = getCurrentUser();
766
+ set({
767
+ user
768
+ });
769
+ } catch (refreshErr) {
770
+ console.warn('[AuthStore] Falha ao renovar token:', refreshErr);
771
+ // Só desloga se for um erro de autenticação explícito (401)
772
+ if (refreshErr.res?.status === 401) {
773
+ set({
774
+ user: null
775
+ });
776
+ window.localStorage.removeItem('auth:token');
777
+ }
778
+ }
769
779
  }
770
780
  }
771
781
  }
772
782
  } catch (error) {
773
- console.error('Erro no refresh automático:', error);
774
- // Em caso de erro, faz logout
775
- set({
776
- user: null
777
- });
778
- window.localStorage.removeItem('auth:token');
783
+ // Erros de processamento interno não devem deslogar o usuário
784
+ console.error('[AuthStore] Erro no ciclo de refresh automático:', error);
779
785
  }
780
786
  }, 4 * 60 * 1000); // Verifica a cada 4 minutos
781
787
 
@@ -2066,6 +2072,8 @@ function UserProfile({
2066
2072
  onProfileUpdate,
2067
2073
  onPasswordChange,
2068
2074
  onEmailChange,
2075
+ onSessionRevoked,
2076
+ onOtherSessionsRevoked,
2069
2077
  onError,
2070
2078
  // Features toggle
2071
2079
  showAvatar = true,
@@ -2152,7 +2160,9 @@ function UserProfile({
2152
2160
  if (isCurrentSession) {
2153
2161
  try {
2154
2162
  await revokeSession(sessionId);
2163
+ onSessionRevoked?.(sessionId);
2155
2164
  } catch (error) {
2165
+ onError?.(error);
2156
2166
  // Even if it fails, we're revoking our own session, so just logout
2157
2167
  // The server already revoked our session
2158
2168
  }
@@ -2166,11 +2176,7 @@ function UserProfile({
2166
2176
  // Revoking another session
2167
2177
  try {
2168
2178
  await revokeSession(sessionId);
2169
- notifications.show({
2170
- title: labels.successTitle || 'Sucesso',
2171
- message: labels.sessionRevoked || 'Sessão encerrada com sucesso',
2172
- color: 'green'
2173
- });
2179
+ onSessionRevoked?.(sessionId);
2174
2180
  } catch (error) {
2175
2181
  // Check for 401 error (our SDK uses error.res, axios uses error.response)
2176
2182
  const status = error.res?.status || error.response?.status;
@@ -2181,28 +2187,14 @@ function UserProfile({
2181
2187
  if (variant === 'modal') onClose?.();
2182
2188
  return;
2183
2189
  }
2184
- notifications.show({
2185
- title: labels.errorTitle || 'Erro',
2186
- message: error.message || labels.sessionRevokeFailed || 'Falha ao encerrar sessão',
2187
- color: 'red'
2188
- });
2189
2190
  onError?.(error);
2190
2191
  }
2191
2192
  };
2192
2193
  const handleRevokeOtherSessions = async () => {
2193
2194
  try {
2194
2195
  await revokeOtherSessions();
2195
- notifications.show({
2196
- title: labels.successTitle || 'Sucesso',
2197
- message: labels.otherSessionsRevoked || 'Todas as outras sessões foram encerradas',
2198
- color: 'green'
2199
- });
2196
+ onOtherSessionsRevoked?.();
2200
2197
  } catch (error) {
2201
- notifications.show({
2202
- title: labels.errorTitle || 'Erro',
2203
- message: error.message || labels.otherSessionsRevokeFailed || 'Falha ao encerrar sessões',
2204
- color: 'red'
2205
- });
2206
2198
  onError?.(error);
2207
2199
  }
2208
2200
  };
@@ -2263,21 +2255,13 @@ function UserProfile({
2263
2255
 
2264
2256
  // Validate file type
2265
2257
  if (!file.type.startsWith('image/')) {
2266
- notifications.show({
2267
- title: labels.errorTitle || 'Erro',
2268
- message: labels.avatarInvalidType || 'Por favor, selecione uma imagem válida',
2269
- color: 'red'
2270
- });
2258
+ onError?.(new Error(labels.avatarInvalidType || 'Por favor, selecione uma imagem válida'));
2271
2259
  return;
2272
2260
  }
2273
2261
 
2274
2262
  // Validate file size
2275
2263
  if (file.size > maxAvatarSize) {
2276
- notifications.show({
2277
- title: labels.errorTitle || 'Erro',
2278
- message: labels.avatarTooLarge || `Imagem muito grande. Máximo ${Math.round(maxAvatarSize / 1024)}KB.`,
2279
- color: 'red'
2280
- });
2264
+ onError?.(new Error(labels.avatarTooLarge || `Imagem muito grande. Máximo ${Math.round(maxAvatarSize / 1024)}KB.`));
2281
2265
  return;
2282
2266
  }
2283
2267
  setAvatarFile(file);
@@ -2316,40 +2300,20 @@ function UserProfile({
2316
2300
  const handleChangePassword = async values => {
2317
2301
  try {
2318
2302
  await changePassword(values.currentPassword, values.newPassword);
2319
- notifications.show({
2320
- title: labels.successTitle || 'Sucesso',
2321
- message: labels.passwordChanged || 'Senha alterada com sucesso',
2322
- color: 'green'
2323
- });
2324
2303
  passwordForm.reset();
2325
2304
  setEditingSection(null);
2326
2305
  onPasswordChange?.();
2327
2306
  } catch (error) {
2328
- notifications.show({
2329
- title: labels.errorTitle || 'Erro',
2330
- message: error.message || labels.passwordChangeFailed || 'Falha ao alterar senha',
2331
- color: 'red'
2332
- });
2333
2307
  onError?.(error);
2334
2308
  }
2335
2309
  };
2336
2310
  const handleChangeEmail = async values => {
2337
2311
  try {
2338
2312
  await changeEmail(values.newEmail);
2339
- notifications.show({
2340
- title: labels.successTitle || 'Sucesso',
2341
- message: labels.emailVerificationSent || 'Verifique seu novo email para confirmar a alteração',
2342
- color: 'green'
2343
- });
2344
2313
  emailForm.reset();
2345
2314
  setEditingSection(null);
2346
2315
  onEmailChange?.(values.newEmail);
2347
2316
  } catch (error) {
2348
- notifications.show({
2349
- title: labels.errorTitle || 'Erro',
2350
- message: error.message || labels.emailChangeFailed || 'Falha ao alterar email',
2351
- color: 'red'
2352
- });
2353
2317
  onError?.(error);
2354
2318
  }
2355
2319
  };
@@ -2358,43 +2322,29 @@ function UserProfile({
2358
2322
  await updateProfile({
2359
2323
  name: values.name
2360
2324
  });
2361
- notifications.show({
2362
- title: labels.successTitle || 'Sucesso',
2363
- message: labels.nameUpdated || 'Nome atualizado com sucesso',
2364
- color: 'green'
2365
- });
2366
2325
  nameForm.reset();
2367
2326
  setEditingSection(null);
2368
2327
  onProfileUpdate?.({
2369
2328
  name: values.name
2370
2329
  });
2371
2330
  } catch (error) {
2372
- notifications.show({
2373
- title: labels.errorTitle || 'Erro',
2374
- message: error.message || labels.nameUpdateFailed || 'Falha ao atualizar nome',
2375
- color: 'red'
2376
- });
2377
2331
  onError?.(error);
2378
2332
  }
2379
2333
  };
2380
2334
  const handleChangeAvatar = async () => {
2381
2335
  if (!avatarPreview) {
2382
- notifications.show({
2383
- title: labels.errorTitle || 'Erro',
2384
- message: labels.avatarRequired || 'Selecione uma imagem',
2385
- color: 'red'
2386
- });
2336
+ // Optionally handle this validation error via callback or just return?
2337
+ // Since it's a validation error before async call, we might want to expose it too?
2338
+ // The original code used notifications. Let's send to onError for consistency or just return if it's UI state.
2339
+ // Actually, for validation within the component, maybe we can just let it be silent or use form error if applicable?
2340
+ // But this is outside form context. Let's use onError with a custom error object.
2341
+ onError?.(new Error(labels.avatarRequired || 'Selecione uma imagem'));
2387
2342
  return;
2388
2343
  }
2389
2344
  try {
2390
2345
  await updateProfile({
2391
2346
  image: avatarPreview
2392
2347
  });
2393
- notifications.show({
2394
- title: labels.successTitle || 'Sucesso',
2395
- message: labels.avatarUpdated || 'Foto de perfil atualizada com sucesso',
2396
- color: 'green'
2397
- });
2398
2348
  setAvatarPreview(null);
2399
2349
  setAvatarFile(null);
2400
2350
  setEditingSection(null);
@@ -2402,11 +2352,6 @@ function UserProfile({
2402
2352
  image: avatarPreview
2403
2353
  });
2404
2354
  } catch (error) {
2405
- notifications.show({
2406
- title: labels.errorTitle || 'Erro',
2407
- message: error.message || labels.avatarUpdateFailed || 'Falha ao atualizar foto',
2408
- color: 'red'
2409
- });
2410
2355
  onError?.(error);
2411
2356
  }
2412
2357
  };
@@ -2415,11 +2360,6 @@ function UserProfile({
2415
2360
  await updateProfile({
2416
2361
  image: ''
2417
2362
  });
2418
- notifications.show({
2419
- title: labels.successTitle || 'Sucesso',
2420
- message: labels.avatarRemoved || 'Foto de perfil removida',
2421
- color: 'green'
2422
- });
2423
2363
  setAvatarPreview(null);
2424
2364
  setAvatarFile(null);
2425
2365
  setEditingSection(null);
@@ -2427,11 +2367,6 @@ function UserProfile({
2427
2367
  image: ''
2428
2368
  });
2429
2369
  } catch (error) {
2430
- notifications.show({
2431
- title: labels.errorTitle || 'Erro',
2432
- message: error.message || labels.avatarRemoveFailed || 'Falha ao remover foto',
2433
- color: 'red'
2434
- });
2435
2370
  onError?.(error);
2436
2371
  }
2437
2372
  };