@riligar/auth-react 1.15.0 → 1.16.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.js CHANGED
@@ -276,11 +276,11 @@ const getSession = async () => {
276
276
  const listSessions = async () => {
277
277
  return await api('/auth/list-sessions');
278
278
  };
279
- const revokeSession = async token => {
280
- return await api('/auth/revoke-session', {
279
+ const revokeSession = async id => {
280
+ return await api('/auth/revoke-session-by-id', {
281
281
  method: 'POST',
282
282
  body: JSON.stringify({
283
- token
283
+ id
284
284
  })
285
285
  });
286
286
  };
@@ -342,7 +342,7 @@ const useAuthStore = zustand.create((set, get) => ({
342
342
  error: null,
343
343
  // Session management
344
344
  sessions: [],
345
- currentSessionToken: null,
345
+ currentSession: null,
346
346
  // Loading states granulares
347
347
  loadingStates: {
348
348
  signIn: false,
@@ -400,9 +400,14 @@ const useAuthStore = zustand.create((set, get) => ({
400
400
  // Precisamos buscar a sessão no servidor.
401
401
  if (!user) {
402
402
  try {
403
- const session = await getSession();
404
- if (session?.user) {
405
- user = session.user;
403
+ const sessionData = await getSession();
404
+ if (sessionData?.user) {
405
+ user = sessionData.user;
406
+ }
407
+ if (sessionData?.session) {
408
+ set({
409
+ currentSession: sessionData.session
410
+ });
406
411
  }
407
412
  } catch (sessionError) {
408
413
  console.warn('[AuthStore] Failed to fetch session:', sessionError);
@@ -475,8 +480,13 @@ const useAuthStore = zustand.create((set, get) => ({
475
480
 
476
481
  // Se não encontrou no token (sessão opaca), busca do servidor
477
482
  if (!user) {
478
- const session = await getSession();
479
- if (session?.user) user = session.user;
483
+ const sessionData = await getSession();
484
+ if (sessionData?.user) user = sessionData.user;
485
+ if (sessionData?.session) {
486
+ set({
487
+ currentSession: sessionData.session
488
+ });
489
+ }
480
490
  }
481
491
  set({
482
492
  user,
@@ -658,14 +668,14 @@ const useAuthStore = zustand.create((set, get) => ({
658
668
  /* Session */
659
669
  getSession: async () => {
660
670
  try {
661
- const session = await getSession();
662
- // Store current session token for comparison
663
- if (session?.session?.token) {
671
+ const sessionData = await getSession();
672
+ // Store current session for comparison (includes id)
673
+ if (sessionData?.session) {
664
674
  set({
665
- currentSessionToken: session.session.token
675
+ currentSession: sessionData.session
666
676
  });
667
677
  }
668
- return session;
678
+ return sessionData;
669
679
  } catch (err) {
670
680
  set({
671
681
  error: err
@@ -917,8 +927,24 @@ function AuthProvider({
917
927
  checkTokenValidity();
918
928
  }
919
929
  };
930
+
931
+ // Escuta evento de sessão revogada (quando o usuário revoga sua própria sessão)
932
+ const handleSessionRevoked = () => {
933
+ // Limpa o usuário do store
934
+ useAuthStore.setState({
935
+ user: null,
936
+ currentSession: null,
937
+ sessions: []
938
+ });
939
+ // Dispara evento de logout para sincronizar entre abas
940
+ localStorage.setItem('auth:logout', Date.now());
941
+ };
920
942
  window.addEventListener('storage', handleStorageChange);
921
- return () => window.removeEventListener('storage', handleStorageChange);
943
+ window.addEventListener('auth:session-revoked', handleSessionRevoked);
944
+ return () => {
945
+ window.removeEventListener('storage', handleStorageChange);
946
+ window.removeEventListener('auth:session-revoked', handleSessionRevoked);
947
+ };
922
948
  }, [checkTokenValidity]);
923
949
 
924
950
  // Verifica validade do token periodicamente
@@ -1006,6 +1032,19 @@ const useUser = () => useAuthStore(shallow.useShallow(s => ({
1006
1032
  // Alias deprecado para backwards compatibility
1007
1033
  const useProfile = useUser;
1008
1034
 
1035
+ // Sessions Management Hook
1036
+ const useSessions = () => useAuthStore(shallow.useShallow(s => ({
1037
+ currentSession: s.currentSession,
1038
+ sessions: s.sessions,
1039
+ getSession: s.getSession,
1040
+ listSessions: s.listSessions,
1041
+ revokeSession: s.revokeSession,
1042
+ revokeOtherSessions: s.revokeOtherSessions,
1043
+ loadingListSessions: s.loadingStates.listSessions,
1044
+ loadingRevokeSession: s.loadingStates.revokeSession,
1045
+ error: s.error
1046
+ })));
1047
+
1009
1048
  // Application Logo Hook
1010
1049
  const useApplicationLogo = () => {
1011
1050
  const applicationInfo = useAuthStore(s => s.applicationInfo);
@@ -2035,6 +2074,7 @@ function UserProfile({
2035
2074
  showName = true,
2036
2075
  showEmail = true,
2037
2076
  showPassword = true,
2077
+ showSessions = true,
2038
2078
  // Customização
2039
2079
  labels = {},
2040
2080
  title = 'Account',
@@ -2062,6 +2102,111 @@ function UserProfile({
2062
2102
  loadingChangeEmail
2063
2103
  } = useProfile();
2064
2104
 
2105
+ // Hook para sessions
2106
+ const {
2107
+ currentSession,
2108
+ sessions,
2109
+ listSessions,
2110
+ getSession,
2111
+ revokeSession,
2112
+ revokeOtherSessions,
2113
+ loadingListSessions,
2114
+ loadingRevokeSession
2115
+ } = useSessions();
2116
+
2117
+ // Load sessions when opened (modal) or component mounts (card), and when sessions section is opened
2118
+ react.useEffect(() => {
2119
+ if (showSessions && (variant === 'card' || opened)) {
2120
+ // Fetch current session first to get the session ID, then list all sessions
2121
+ getSession().catch(err => console.warn('Failed to get current session:', err));
2122
+ listSessions().catch(err => console.warn('Failed to load sessions:', err));
2123
+ }
2124
+ }, [opened, showSessions, variant]);
2125
+
2126
+ // Helper to parse user agent string
2127
+ const parseUserAgent = ua => {
2128
+ if (!ua) return {
2129
+ browser: 'Unknown Browser',
2130
+ os: 'Unknown OS'
2131
+ };
2132
+ let browser = 'Unknown Browser';
2133
+ let os = 'Unknown OS';
2134
+
2135
+ // Detect browser
2136
+ if (ua.includes('Chrome') && !ua.includes('Edg')) browser = 'Chrome';else if (ua.includes('Firefox')) browser = 'Firefox';else if (ua.includes('Safari') && !ua.includes('Chrome')) browser = 'Safari';else if (ua.includes('Edg')) browser = 'Edge';else if (ua.includes('Opera') || ua.includes('OPR')) browser = 'Opera';
2137
+
2138
+ // Detect OS
2139
+ if (ua.includes('Windows')) os = 'Windows';else if (ua.includes('Mac OS')) os = 'macOS';else if (ua.includes('Linux')) os = 'Linux';else if (ua.includes('Android')) os = 'Android';else if (ua.includes('iPhone') || ua.includes('iPad')) os = 'iOS';
2140
+ return {
2141
+ browser,
2142
+ os
2143
+ };
2144
+ };
2145
+
2146
+ // Session handlers
2147
+ const handleRevokeSession = async sessionId => {
2148
+ // Check if revoking current session
2149
+ const isCurrentSession = sessionId === currentSession?.id || sessions.length === 1;
2150
+
2151
+ // If revoking current session, handle logout immediately after revocation
2152
+ if (isCurrentSession) {
2153
+ try {
2154
+ await revokeSession(sessionId);
2155
+ } catch (error) {
2156
+ // Even if it fails, we're revoking our own session, so just logout
2157
+ // The server already revoked our session
2158
+ }
2159
+ // Clear auth state and redirect
2160
+ localStorage.removeItem('auth:token');
2161
+ window.dispatchEvent(new CustomEvent('auth:session-revoked'));
2162
+ if (variant === 'modal') onClose?.();
2163
+ return;
2164
+ }
2165
+
2166
+ // Revoking another session
2167
+ try {
2168
+ await revokeSession(sessionId);
2169
+ notifications.notifications.show({
2170
+ title: labels.successTitle || 'Sucesso',
2171
+ message: labels.sessionRevoked || 'Sessão encerrada com sucesso',
2172
+ color: 'green'
2173
+ });
2174
+ } catch (error) {
2175
+ // Check for 401 error (our SDK uses error.res, axios uses error.response)
2176
+ const status = error.res?.status || error.response?.status;
2177
+ if (status === 401) {
2178
+ // This means our session was revoked, not the target one - do logout
2179
+ localStorage.removeItem('auth:token');
2180
+ window.dispatchEvent(new CustomEvent('auth:session-revoked'));
2181
+ if (variant === 'modal') onClose?.();
2182
+ return;
2183
+ }
2184
+ notifications.notifications.show({
2185
+ title: labels.errorTitle || 'Erro',
2186
+ message: error.message || labels.sessionRevokeFailed || 'Falha ao encerrar sessão',
2187
+ color: 'red'
2188
+ });
2189
+ onError?.(error);
2190
+ }
2191
+ };
2192
+ const handleRevokeOtherSessions = async () => {
2193
+ try {
2194
+ await revokeOtherSessions();
2195
+ notifications.notifications.show({
2196
+ title: labels.successTitle || 'Sucesso',
2197
+ message: labels.otherSessionsRevoked || 'Todas as outras sessões foram encerradas',
2198
+ color: 'green'
2199
+ });
2200
+ } catch (error) {
2201
+ notifications.notifications.show({
2202
+ title: labels.errorTitle || 'Erro',
2203
+ message: error.message || labels.otherSessionsRevokeFailed || 'Falha ao encerrar sessões',
2204
+ color: 'red'
2205
+ });
2206
+ onError?.(error);
2207
+ }
2208
+ };
2209
+
2065
2210
  // Password form
2066
2211
  const passwordForm = form.useForm({
2067
2212
  initialValues: {
@@ -2593,7 +2738,7 @@ function UserProfile({
2593
2738
  })]
2594
2739
  })]
2595
2740
  })]
2596
- }), showPassword && /*#__PURE__*/jsxRuntime.jsxs(core.Box, {
2741
+ }), (showPassword || showSessions) && /*#__PURE__*/jsxRuntime.jsxs(core.Box, {
2597
2742
  mb: "md",
2598
2743
  children: [/*#__PURE__*/jsxRuntime.jsx(SectionHeader, {
2599
2744
  icon: iconsReact.IconShield,
@@ -2601,69 +2746,196 @@ function UserProfile({
2601
2746
  description: labels.securityDescription || 'Protect your account'
2602
2747
  }), /*#__PURE__*/jsxRuntime.jsxs(core.Stack, {
2603
2748
  gap: "sm",
2604
- children: [/*#__PURE__*/jsxRuntime.jsx(SettingRow, {
2605
- label: labels.password || 'Password',
2606
- action: labels.change || 'Change',
2607
- actionLabel: labels.changePassword || 'Change your password',
2608
- onClick: () => handleToggleSection('password'),
2609
- expanded: editingSection === 'password',
2610
- children: /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
2611
- gap: "xs",
2612
- children: [/*#__PURE__*/jsxRuntime.jsx(iconsReact.IconKey, {
2613
- size: 16,
2614
- color: "var(--mantine-color-dimmed)"
2615
- }), /*#__PURE__*/jsxRuntime.jsx(core.Text, {
2616
- size: "sm",
2617
- c: "dimmed",
2618
- children: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"
2619
- })]
2620
- })
2621
- }), /*#__PURE__*/jsxRuntime.jsx(core.Collapse, {
2622
- in: editingSection === 'password',
2623
- children: /*#__PURE__*/jsxRuntime.jsx(core.Card, {
2624
- p: "md",
2625
- withBorder: true,
2626
- children: /*#__PURE__*/jsxRuntime.jsx("form", {
2627
- onSubmit: passwordForm.onSubmit(handleChangePassword),
2628
- children: /*#__PURE__*/jsxRuntime.jsxs(core.Stack, {
2629
- gap: "sm",
2630
- children: [/*#__PURE__*/jsxRuntime.jsx(core.PasswordInput, {
2631
- label: labels.currentPassword || 'Senha Atual',
2632
- placeholder: labels.currentPasswordPlaceholder || 'Digite sua senha atual',
2633
- ...passwordForm.getInputProps('currentPassword')
2634
- }), /*#__PURE__*/jsxRuntime.jsx(core.PasswordInput, {
2635
- label: labels.newPassword || 'Nova Senha',
2636
- placeholder: labels.newPasswordPlaceholder || 'Digite a nova senha',
2637
- description: labels.passwordDescription || 'Mínimo 8 caracteres',
2638
- ...passwordForm.getInputProps('newPassword')
2639
- }), /*#__PURE__*/jsxRuntime.jsx(core.PasswordInput, {
2640
- label: labels.confirmPassword || 'Confirmar Nova Senha',
2641
- placeholder: labels.confirmPasswordPlaceholder || 'Confirme a nova senha',
2642
- ...passwordForm.getInputProps('confirmPassword')
2643
- }), /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
2644
- justify: "flex-end",
2645
- gap: "xs",
2646
- children: [/*#__PURE__*/jsxRuntime.jsx(core.Button, {
2647
- variant: "default",
2648
- size: "xs",
2649
- onClick: () => handleToggleSection('password'),
2650
- leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconX, {
2651
- size: 14
2652
- }),
2653
- children: labels.cancel || 'Cancelar'
2654
- }), /*#__PURE__*/jsxRuntime.jsx(core.Button, {
2655
- type: "submit",
2656
- size: "xs",
2657
- loading: loadingChangePassword,
2658
- leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconCheck, {
2659
- size: 14
2660
- }),
2661
- children: labels.save || 'Salvar'
2749
+ children: [showPassword && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2750
+ children: [/*#__PURE__*/jsxRuntime.jsx(SettingRow, {
2751
+ label: labels.password || 'Password',
2752
+ action: labels.change || 'Change',
2753
+ actionLabel: labels.changePassword || 'Change your password',
2754
+ onClick: () => handleToggleSection('password'),
2755
+ expanded: editingSection === 'password',
2756
+ children: /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
2757
+ gap: "xs",
2758
+ children: [/*#__PURE__*/jsxRuntime.jsx(iconsReact.IconKey, {
2759
+ size: 16,
2760
+ color: "var(--mantine-color-dimmed)"
2761
+ }), /*#__PURE__*/jsxRuntime.jsx(core.Text, {
2762
+ size: "sm",
2763
+ c: "dimmed",
2764
+ children: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"
2765
+ })]
2766
+ })
2767
+ }), /*#__PURE__*/jsxRuntime.jsx(core.Collapse, {
2768
+ in: editingSection === 'password',
2769
+ children: /*#__PURE__*/jsxRuntime.jsx(core.Card, {
2770
+ p: "md",
2771
+ withBorder: true,
2772
+ children: /*#__PURE__*/jsxRuntime.jsx("form", {
2773
+ onSubmit: passwordForm.onSubmit(handleChangePassword),
2774
+ children: /*#__PURE__*/jsxRuntime.jsxs(core.Stack, {
2775
+ gap: "sm",
2776
+ children: [/*#__PURE__*/jsxRuntime.jsx(core.PasswordInput, {
2777
+ label: labels.currentPassword || 'Senha Atual',
2778
+ placeholder: labels.currentPasswordPlaceholder || 'Digite sua senha atual',
2779
+ ...passwordForm.getInputProps('currentPassword')
2780
+ }), /*#__PURE__*/jsxRuntime.jsx(core.PasswordInput, {
2781
+ label: labels.newPassword || 'Nova Senha',
2782
+ placeholder: labels.newPasswordPlaceholder || 'Digite a nova senha',
2783
+ description: labels.passwordDescription || 'Mínimo 8 caracteres',
2784
+ ...passwordForm.getInputProps('newPassword')
2785
+ }), /*#__PURE__*/jsxRuntime.jsx(core.PasswordInput, {
2786
+ label: labels.confirmPassword || 'Confirmar Nova Senha',
2787
+ placeholder: labels.confirmPasswordPlaceholder || 'Confirme a nova senha',
2788
+ ...passwordForm.getInputProps('confirmPassword')
2789
+ }), /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
2790
+ justify: "flex-end",
2791
+ gap: "xs",
2792
+ children: [/*#__PURE__*/jsxRuntime.jsx(core.Button, {
2793
+ variant: "default",
2794
+ size: "xs",
2795
+ onClick: () => handleToggleSection('password'),
2796
+ leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconX, {
2797
+ size: 14
2798
+ }),
2799
+ children: labels.cancel || 'Cancelar'
2800
+ }), /*#__PURE__*/jsxRuntime.jsx(core.Button, {
2801
+ type: "submit",
2802
+ size: "xs",
2803
+ loading: loadingChangePassword,
2804
+ leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconCheck, {
2805
+ size: 14
2806
+ }),
2807
+ children: labels.save || 'Salvar'
2808
+ })]
2662
2809
  })]
2663
- })]
2810
+ })
2664
2811
  })
2665
2812
  })
2666
- })
2813
+ })]
2814
+ }), showSessions && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2815
+ children: [/*#__PURE__*/jsxRuntime.jsx(SettingRow, {
2816
+ label: labels.sessions || 'Sessions',
2817
+ action: editingSection === 'sessions' ? labels.close || 'Close' : labels.manage || 'Manage',
2818
+ actionLabel: labels.manageSessions || 'Manage your active sessions',
2819
+ onClick: () => handleToggleSection('sessions'),
2820
+ expanded: editingSection === 'sessions',
2821
+ children: /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
2822
+ gap: "xs",
2823
+ children: [/*#__PURE__*/jsxRuntime.jsx(iconsReact.IconDevices, {
2824
+ size: 16,
2825
+ color: "var(--mantine-color-dimmed)"
2826
+ }), /*#__PURE__*/jsxRuntime.jsx(core.Text, {
2827
+ size: "sm",
2828
+ c: "dimmed",
2829
+ children: sessions.length > 0 ? `${sessions.length} active session${sessions.length > 1 ? 's' : ''}` : loadingListSessions ? 'Loading...' : 'No sessions'
2830
+ })]
2831
+ })
2832
+ }), /*#__PURE__*/jsxRuntime.jsx(core.Collapse, {
2833
+ in: editingSection === 'sessions',
2834
+ children: /*#__PURE__*/jsxRuntime.jsx(core.Card, {
2835
+ p: "md",
2836
+ withBorder: true,
2837
+ children: /*#__PURE__*/jsxRuntime.jsx(core.Stack, {
2838
+ gap: "md",
2839
+ children: loadingListSessions ? /*#__PURE__*/jsxRuntime.jsx(core.Text, {
2840
+ size: "sm",
2841
+ c: "dimmed",
2842
+ ta: "center",
2843
+ children: labels.loadingSessions || 'Carregando sessões...'
2844
+ }) : sessions.length === 0 ? /*#__PURE__*/jsxRuntime.jsx(core.Text, {
2845
+ size: "sm",
2846
+ c: "dimmed",
2847
+ ta: "center",
2848
+ children: labels.noSessionsFound || 'Nenhuma sessão encontrada'
2849
+ }) : /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2850
+ children: [sessions.map(sessionItem => {
2851
+ const isCurrentSession = sessionItem.id === currentSession?.id;
2852
+ const deviceInfo = parseUserAgent(sessionItem.userAgent);
2853
+ const createdDate = new Date(sessionItem.createdAt);
2854
+ return /*#__PURE__*/jsxRuntime.jsx(core.Card, {
2855
+ p: "sm",
2856
+ style: {
2857
+ borderColor: isCurrentSession ? 'var(--mantine-color-blue-5)' : undefined,
2858
+ backgroundColor: isCurrentSession ? 'var(--mantine-color-blue-light)' : undefined
2859
+ },
2860
+ children: /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
2861
+ justify: "space-between",
2862
+ wrap: "nowrap",
2863
+ children: [/*#__PURE__*/jsxRuntime.jsxs(core.Group, {
2864
+ gap: "sm",
2865
+ wrap: "nowrap",
2866
+ style: {
2867
+ flex: 1
2868
+ },
2869
+ children: [/*#__PURE__*/jsxRuntime.jsx(core.ThemeIcon, {
2870
+ size: 36,
2871
+ variant: "light",
2872
+ color: isCurrentSession ? 'blue' : 'gray',
2873
+ children: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconDeviceMobile, {
2874
+ size: 18
2875
+ })
2876
+ }), /*#__PURE__*/jsxRuntime.jsxs(core.Box, {
2877
+ style: {
2878
+ flex: 1
2879
+ },
2880
+ children: [/*#__PURE__*/jsxRuntime.jsxs(core.Group, {
2881
+ gap: "xs",
2882
+ children: [/*#__PURE__*/jsxRuntime.jsx(core.Text, {
2883
+ size: "sm",
2884
+ fw: 500,
2885
+ children: deviceInfo.browser
2886
+ }), isCurrentSession && /*#__PURE__*/jsxRuntime.jsx(core.Badge, {
2887
+ size: "xs",
2888
+ variant: "filled",
2889
+ color: "blue",
2890
+ children: labels.thisDevice || 'Este dispositivo'
2891
+ })]
2892
+ }), /*#__PURE__*/jsxRuntime.jsxs(core.Text, {
2893
+ size: "xs",
2894
+ c: "dimmed",
2895
+ children: [deviceInfo.os, " \u2022 ", sessionItem.ipAddress || labels.unknownIP || 'IP desconhecido']
2896
+ }), /*#__PURE__*/jsxRuntime.jsxs(core.Text, {
2897
+ size: "xs",
2898
+ c: "dimmed",
2899
+ children: [labels.createdAt || 'Criada em', " ", createdDate.toLocaleDateString('pt-BR'), " ", labels.at || 'às', ' ', createdDate.toLocaleTimeString('pt-BR', {
2900
+ hour: '2-digit',
2901
+ minute: '2-digit'
2902
+ })]
2903
+ })]
2904
+ })]
2905
+ }), /*#__PURE__*/jsxRuntime.jsx(core.Tooltip, {
2906
+ label: isCurrentSession ? labels.signOutAndEnd || 'Encerrar e sair' : labels.endSession || 'Encerrar sessão',
2907
+ children: /*#__PURE__*/jsxRuntime.jsx(core.Button, {
2908
+ variant: "light",
2909
+ color: "red",
2910
+ size: "xs",
2911
+ onClick: () => handleRevokeSession(sessionItem.id),
2912
+ loading: loadingRevokeSession,
2913
+ leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconLogout, {
2914
+ size: 14
2915
+ }),
2916
+ children: labels.end || 'Encerrar'
2917
+ })
2918
+ })]
2919
+ })
2920
+ }, sessionItem.id);
2921
+ }), sessions.length > 1 && /*#__PURE__*/jsxRuntime.jsx(core.Group, {
2922
+ justify: "flex-end",
2923
+ children: /*#__PURE__*/jsxRuntime.jsx(core.Button, {
2924
+ variant: "light",
2925
+ color: "red",
2926
+ size: "xs",
2927
+ onClick: handleRevokeOtherSessions,
2928
+ loading: loadingRevokeSession,
2929
+ leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconLogout, {
2930
+ size: 14
2931
+ }),
2932
+ children: labels.endOtherSessions || 'Encerrar todas as outras sessões'
2933
+ })
2934
+ })]
2935
+ })
2936
+ })
2937
+ })
2938
+ })]
2667
2939
  })]
2668
2940
  })]
2669
2941
  })]
@@ -2905,6 +3177,7 @@ exports.useMagicLink = useMagicLink;
2905
3177
  exports.usePasswordReset = usePasswordReset;
2906
3178
  exports.useProfile = useUser;
2907
3179
  exports.useSession = useSession;
3180
+ exports.useSessions = useSessions;
2908
3181
  exports.useSignIn = useSignIn;
2909
3182
  exports.useSignOut = useSignOut;
2910
3183
  exports.useSignUp = useSignUp;