()
| 12 | * @returns Promise that resolves to the new token or null if refresh failed |
| 13 | */ |
| 14 | export const refreshAccessToken = async (): Promise<string | null> => { |
| 15 | // If a refresh is already in progress, return that promise |
| 16 | if (isRefreshing && refreshPromise) { |
| 17 | return refreshPromise; |
| 18 | } |
| 19 | |
| 20 | isRefreshing = true; |
| 21 | refreshPromise = (async () => { |
| 22 | try { |
| 23 | const refreshToken = localStorage.getItem(LocalStore.refreshToken); |
| 24 | if (!refreshToken) { |
| 25 | return null; |
| 26 | } |
| 27 | |
| 28 | // Use Apollo client to refresh the token |
| 29 | const result = await client.mutate({ |
| 30 | mutation: REFRESH_TOKEN_MUTATION, |
| 31 | variables: { refreshToken }, |
| 32 | }); |
| 33 | |
| 34 | if (result.data?.refreshToken?.accessToken) { |
| 35 | const newAccessToken = result.data.refreshToken.accessToken; |
| 36 | const newRefreshToken = |
| 37 | result.data.refreshToken.refreshToken || refreshToken; |
| 38 | |
| 39 | localStorage.setItem(LocalStore.accessToken, newAccessToken); |
| 40 | localStorage.setItem(LocalStore.refreshToken, newRefreshToken); |
| 41 | |
| 42 | console.log('Token refreshed successfully'); |
| 43 | return newAccessToken; |
| 44 | } |
| 45 | |
| 46 | return null; |
| 47 | } catch (error) { |
| 48 | console.error('Error refreshing token:', error); |
| 49 | return null; |
| 50 | } finally { |
| 51 | isRefreshing = false; |
| 52 | refreshPromise = null; |
| 53 | } |
| 54 | })(); |
| 55 | |
| 56 | return refreshPromise; |
| 57 | }; |
| 58 | |
| 59 | /** |
| 60 | * Fetch wrapper that handles authentication and token refresh |
no outgoing calls
no test coverage detected