@@ -3,6 +3,7 @@ import type { ReactNode } from 'react';
33import { useQueryClient } from '@tanstack/react-query' ;
44import type { User } from '../types/entities.ts' ;
55import { userService } from '../api/services/user.service.ts' ;
6+ import { authService } from '../api/services/auth.service.ts' ;
67import { AuthContext } from './AuthContext' ;
78import apiClient , { TOKEN_STORAGE_KEY } from '../api/apiClient' ;
89
@@ -14,39 +15,17 @@ export interface AuthContextType {
1415 logout : ( ) => void ;
1516}
1617
17- // This provider handles authentication and user state globally.
1818export const AuthProvider = ( { children } : { children : ReactNode } ) => {
19- // State for the authenticated user and to know if we're loading data.
2019 const [ user , setUser ] = useState < User | null > ( null ) ;
2120 const [ isLoading , setIsLoading ] = useState ( true ) ;
2221 const queryClient = useQueryClient ( ) ;
2322
24- // Function to get the user's profile and update the global state.
2523 const fetchProfileAndSetUser = useCallback ( async ( ) => {
26- try {
27- const userData = await userService . getProfile ( ) ;
28- setUser ( userData ) ;
29- } catch ( error ) {
30- // If it fails, we remove the token and log out the user.
31- console . error ( 'Failed to fetch profile, logging out.' , error ) ;
32- localStorage . removeItem ( TOKEN_STORAGE_KEY ) ;
33- setUser ( null ) ;
34- }
24+ const userData = await userService . getProfile ( ) ;
25+ setUser ( userData ) ;
26+ return userData ;
3527 } , [ ] ) ;
3628
37- // When this runs for the first time, we look for a saved token and try to log in with it.
38- useEffect ( ( ) => {
39- const validateTokenOnLoad = async ( ) => {
40- const token = localStorage . getItem ( TOKEN_STORAGE_KEY ) ;
41- if ( token ) {
42- await fetchProfileAndSetUser ( ) ;
43- }
44- setIsLoading ( false ) ;
45- } ;
46- validateTokenOnLoad ( ) ;
47- } , [ fetchProfileAndSetUser ] ) ;
48-
49- // Login function: saves the token, sets it in the header, and gets the profile.
5029 const login = useCallback (
5130 async ( token : string ) => {
5231 localStorage . setItem ( TOKEN_STORAGE_KEY , token ) ;
@@ -56,50 +35,69 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
5635 [ fetchProfileAndSetUser ]
5736 ) ;
5837
59- // Logout function: removes the token and header, and clears the user.
60- const logout = useCallback ( ( ) => {
38+ const logout = useCallback ( async ( ) => {
6139 try {
62- // Clear all React Query caches to avoid stale data
40+ await authService . logout ( ) ;
41+ } catch ( error ) {
42+ console . error ( 'Error logout servidor:' , error ) ;
43+ } finally {
6344 queryClient . clear ( ) ;
64-
65- // Remove JWT token from localStorage using the correct key
6645 localStorage . removeItem ( TOKEN_STORAGE_KEY ) ;
46+
47+ [ 'jwt_token' , 'token' , 'authToken' ] . forEach ( ( k ) => localStorage . removeItem ( k ) ) ;
6748
68- // Also remove any other common auth-related keys (defensive cleanup)
69- const authKeys = [
70- 'jwt_token' ,
71- 'token' ,
72- 'authToken' ,
73- 'access_token' ,
74- 'refresh_token' ,
75- ] ;
76- authKeys . forEach ( ( key ) => {
77- if ( localStorage . getItem ( key ) ) {
78- localStorage . removeItem ( key ) ;
79- }
80- } ) ;
81-
82- // Clear Authorization header from future requests
8349 delete apiClient . defaults . headers . common [ 'Authorization' ] ;
84-
85- // Clear user state
86- setUser ( null ) ;
87- setIsLoading ( false ) ;
88-
89- console . log ( 'Logout completado exitosamente' ) ;
90- } catch ( error ) {
91- console . error ( 'Error durante logout:' , error ) ;
92- // Force cleanup even if something fails
93- localStorage . clear ( ) ;
9450 setUser ( null ) ;
9551 setIsLoading ( false ) ;
9652 }
9753 } , [ queryClient ] ) ;
9854
99- // We keep these values ready so the app doesn't reload too much.
55+ useEffect ( ( ) => {
56+ const initAuth = async ( ) => {
57+ const token = localStorage . getItem ( TOKEN_STORAGE_KEY ) ;
58+
59+ const trySilentRefresh = async ( ) => {
60+ try {
61+ const { data } = await apiClient . post ( '/auth/refresh' ) ;
62+ const newToken = data . data . token ;
63+ if ( newToken ) {
64+ await login ( newToken ) ;
65+ return true ;
66+ }
67+ } catch {
68+ return false ;
69+ }
70+ return false ;
71+ } ;
72+
73+ if ( token ) {
74+ apiClient . defaults . headers . common [ 'Authorization' ] = `Bearer ${ token } ` ;
75+ try {
76+ await fetchProfileAndSetUser ( ) ;
77+ } catch {
78+ console . warn ( "Token local inválido o expirado. Intentando renovar sesión..." ) ;
79+
80+ const success = await trySilentRefresh ( ) ;
81+
82+ if ( ! success ) {
83+ console . error ( "No se pudo restaurar la sesión." ) ;
84+ localStorage . removeItem ( TOKEN_STORAGE_KEY ) ;
85+ setUser ( null ) ;
86+ }
87+ }
88+ } else {
89+ await trySilentRefresh ( ) ;
90+ }
91+
92+ setIsLoading ( false ) ;
93+ } ;
94+
95+ initAuth ( ) ;
96+ } , [ fetchProfileAndSetUser , login ] ) ;
97+
10098 const value = useMemo (
10199 ( ) => ( {
102- isAuthenticated : ! ! user , // We know if someone is logged in if there's a user object.
100+ isAuthenticated : ! ! user ,
103101 user,
104102 isLoading,
105103 login,
@@ -108,6 +106,5 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
108106 [ user , isLoading , login , logout ]
109107 ) ;
110108
111- // Provide the context to child components.
112109 return < AuthContext . Provider value = { value } > { children } </ AuthContext . Provider > ;
113- } ;
110+ } ;
0 commit comments