@@ -8,10 +8,13 @@ import {
88 useState ,
99 ReactNode ,
1010} from 'react' ;
11+
1112import { Patient } from './auth.types' ;
12- import { refreshPatient } from './auth.service' ;
13+ import { refreshPatient , logoutPatient } from './auth.service' ;
1314import { api } from '../lib/api' ;
14- import { logoutPatient } from './auth.service' ;
15+
16+ import { useQueryClient } from '@tanstack/react-query' ;
17+ import { getProfile } from '@/src/features/patient/api/getProfile' ;
1518
1619type AuthState = {
1720 accessToken : string | null ;
@@ -24,14 +27,16 @@ type AuthContextType = {
2427 loginSuccess : ( data : {
2528 accessToken : string ;
2629 patient : Patient ;
27-
2830 } ) => void ;
2931 logout : ( ) => void ;
3032} ;
3133
3234const AuthContext = createContext < AuthContextType | null > ( null ) ;
3335
3436export function AuthProvider ( { children } : { children : ReactNode } ) {
37+
38+ const queryClient = useQueryClient ( ) ;
39+
3540 const [ auth , setAuth ] = useState < AuthState > ( {
3641 accessToken : null ,
3742 patient : null ,
@@ -42,12 +47,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
4247 const refreshingRef = useRef < Promise < string > | null > ( null ) ;
4348 const interceptorsInitialized = useRef ( false ) ;
4449
45- // Keep token ref in sync
50+ // Keep token ref synced
4651 useEffect ( ( ) => {
4752 accessTokenRef . current = auth . accessToken ;
4853 } , [ auth . accessToken ] ) ;
4954
50- // Setup Axios interceptors once
55+ // Axios interceptors
5156 useEffect ( ( ) => {
5257 if ( interceptorsInitialized . current ) return ;
5358 interceptorsInitialized . current = true ;
@@ -62,6 +67,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
6267 const resId = api . interceptors . response . use (
6368 res => res ,
6469 async error => {
70+
6571 const original = error . config ;
6672
6773 if (
@@ -77,123 +83,171 @@ export function AuthProvider({ children }: { children: ReactNode }) {
7783 original . _retry = true ;
7884
7985 try {
86+
8087 if ( ! refreshingRef . current ) {
81- refreshingRef . current = refreshPatient ( ) . then ( res => {
82- setAuth ( prev => ( {
83- ...prev ,
84- accessToken : res . accessToken ,
85- } ) ) ;
86- refreshingRef . current = null ;
87- return res . accessToken ;
88- } ) ;
88+
89+ refreshingRef . current = refreshPatient ( ) . then ( res => {
90+
91+ api . defaults . headers . common [ 'Authorization' ] = `Bearer ${ res . accessToken } ` ;
92+
93+ setAuth ( prev => ( {
94+ ...prev ,
95+ accessToken : res . accessToken
96+ } ) ) ;
97+
98+ refreshingRef . current = null ;
99+
100+ return res . accessToken ;
101+ } ) ;
102+
89103 }
90104
91105 const newToken = await refreshingRef . current ;
106+
92107 original . headers . Authorization = `Bearer ${ newToken } ` ;
108+
93109 return api ( original ) ;
110+
94111 } catch {
112+
95113 refreshingRef . current = null ;
114+
96115 setAuth ( {
97116 accessToken : null ,
98117 patient : null ,
99118 isRestoring : false ,
100119 } ) ;
120+
101121 return Promise . reject ( error ) ;
122+
102123 }
124+
103125 }
104126 ) ;
105127
106128 return ( ) => {
107129 api . interceptors . request . eject ( reqId ) ;
108130 api . interceptors . response . eject ( resId ) ;
109131 } ;
132+
110133 } , [ ] ) ;
111134
112- // 🔁 Restore session on page load
113- // 🔁 Restore session ONLY if no accessToken
114- // 🔁 Restore session ONCE on mount
115- useEffect ( ( ) => {
116- let cancelled = false ;
117-
118- ( async ( ) => {
119- // 🔥 IMPORTANT FIX
120- if ( accessTokenRef . current ) {
121- setAuth ( prev => ( {
122- ...prev ,
123- isRestoring : false ,
124- } ) ) ;
125- return ;
126- }
135+ /*
136+ Restore session on page load
137+ */
127138
128- try {
129- const refreshRes = await refreshPatient ( ) ;
139+ useEffect ( ( ) => {
140+
141+ let cancelled = false ;
142+
143+ ( async ( ) => {
144+
145+ if ( accessTokenRef . current ) {
146+ setAuth ( prev => ( {
147+ ...prev ,
148+ isRestoring : false
149+ } ) ) ;
150+ return ;
151+ }
152+
153+ try {
130154
131- accessTokenRef . current = refreshRes . accessToken ;
155+ const refreshRes = await refreshPatient ( ) ;
132156
133- const profileRes = await api . get ( '/patients/public/auth/me' ) ;
157+ accessTokenRef . current = refreshRes . accessToken ;
134158
135- if ( cancelled ) return ;
159+ const profileRes = await api . get ( '/patients/public/auth/me' ) ;
136160
137- setAuth ( {
138- accessToken : refreshRes . accessToken ,
139- patient : profileRes . data ,
140- isRestoring : false ,
141- } ) ;
142- } catch {
143- if ( cancelled ) return ;
161+ if ( cancelled ) return ;
144162
145- setAuth ( {
146- accessToken : null ,
147- patient : null ,
148- isRestoring : false ,
149- } ) ;
150- }
151- } ) ( ) ;
163+ setAuth ( {
164+ accessToken : refreshRes . accessToken ,
165+ patient : profileRes . data ,
166+ isRestoring : false
167+ } ) ;
152168
153- return ( ) => {
154- cancelled = true ;
155- } ;
156- } , [ ] ) ;
169+ } catch {
157170
171+ if ( cancelled ) return ;
158172
173+ setAuth ( {
174+ accessToken : null ,
175+ patient : null ,
176+ isRestoring : false
177+ } ) ;
178+
179+ }
180+
181+ } ) ( ) ;
182+
183+ return ( ) => {
184+ cancelled = true ;
185+ } ;
186+
187+ } , [ ] ) ;
188+
189+ /*
190+ Login success handler
191+ */
159192
160193function loginSuccess ( data : {
161194 accessToken : string ;
162195 patient : Patient ;
163196} ) {
164- accessTokenRef . current = data . accessToken ; // 🔥 ADD THIS LINE
197+
198+ accessTokenRef . current = data . accessToken ;
199+
200+ // 🔥 Ensure axios always has the token immediately
201+ api . defaults . headers . common [ 'Authorization' ] = `Bearer ${ data . accessToken } ` ;
165202
166203 setAuth ( {
167204 accessToken : data . accessToken ,
168205 patient : data . patient ,
169- isRestoring : false ,
206+ isRestoring : false
170207 } ) ;
171- }
172208
173- async function logout ( ) {
174- try {
175- await logoutPatient ( ) ;
176- } catch { }
209+ queryClient . invalidateQueries ( {
210+ queryKey : [ 'patient-profile' ]
211+ } ) ;
177212
178- setAuth ( {
179- accessToken : null ,
180- patient : null ,
181- isRestoring : false ,
213+ queryClient . prefetchQuery ( {
214+ queryKey : [ 'patient-profile' ] ,
215+ queryFn : getProfile
182216 } ) ;
183217}
184218
219+ async function logout ( ) {
220+
221+ try {
222+ await logoutPatient ( ) ;
223+ } catch { }
224+
225+ setAuth ( {
226+ accessToken : null ,
227+ patient : null ,
228+ isRestoring : false
229+ } ) ;
230+
231+ queryClient . clear ( ) ;
232+
233+ }
185234
186235 return (
187236 < AuthContext . Provider value = { { auth, loginSuccess, logout } } >
188237 { children }
189238 </ AuthContext . Provider >
190239 ) ;
240+
191241}
192242
193243export function useAuth ( ) {
244+
194245 const ctx = useContext ( AuthContext ) ;
246+
195247 if ( ! ctx ) {
196248 throw new Error ( 'useAuth must be used inside AuthProvider' ) ;
197249 }
250+
198251 return ctx ;
199- }
252+
253+ }
0 commit comments