Spaces:
Running
Running
File size: 3,590 Bytes
3299552 0056542 3299552 9f5c199 3299552 0056542 3299552 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
// src/hooks/useAuth.js
import { useState, useContext, createContext, useCallback, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { login as apiLogin, logout as apiLogout, refreshToken as apiRefreshToken } from '../services/auth';
import { useLocalStorage } from './useLocalStorage';
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [authTokens, setAuthTokens] = useLocalStorage('authTokens', null);
const [userType, setUserType] = useLocalStorage('userType', 'Enthusiast');
const [isRefreshing, setIsRefreshing] = useState(false);
const navigate = useNavigate();
const parseJwt = (token) => {
try {
return JSON.parse(atob(token.split('.')[1]));
} catch (e) {
return null;
}
};
const login = useCallback(async (username, password) => {
try {
const { access_token, refresh_token } = await apiLogin(username, password);
setAuthTokens({
access: access_token,
refresh: refresh_token
});
setUser(username);
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
}, [setAuthTokens]);
const logout = useCallback(async () => {
try {
if (authTokens?.access) {
await apiLogout(authTokens.access);
}
setAuthTokens(null);
setUser(null);
navigate('/login');
} catch (error) {
console.error('Logout error:', error);
// Even if logout API fails, clear local tokens
setAuthTokens(null);
setUser(null);
}
}, [authTokens, setAuthTokens, navigate]);
const refreshToken = useCallback(async () => {
if (!authTokens?.refresh || isRefreshing) return;
setIsRefreshing(true);
try {
const { access_token, refresh_token } = await apiRefreshToken({
refresh_token: authTokens.refresh
});
setAuthTokens({
access: access_token,
refresh: refresh_token
});
return access_token;
} catch (error) {
console.error('Token refresh failed:', error);
logout(); // Full logout if refresh fails
throw error;
} finally {
setIsRefreshing(false);
}
}, [authTokens, isRefreshing, setAuthTokens, logout]);
// Auto-refresh token when it's about to expire
useEffect(() => {
const refreshInterval = setInterval(() => {
if (authTokens?.access) {
const { exp } = parseJwt(authTokens.access);
// Refresh token if it expires in less than 5 minutes
if (exp * 1000 - Date.now() < 300000) {
refreshToken();
}
}
}, 60000); // Check every minute
return () => clearInterval(refreshInterval);
}, [authTokens, refreshToken]);
const getAccessToken = useCallback(async () => {
if (!authTokens?.access) return null;
const { exp } = parseJwt(authTokens.access);
if (exp * 1000 - Date.now() < 30000) { // If expires in <30 seconds
return await refreshToken();
}
return authTokens.access;
}, [authTokens, refreshToken]);
const value = {
user,
authTokens,
userType,
setUserType,
isAuthenticated: !!authTokens?.access,
isRefreshing,
login,
logout,
refreshToken,
getAccessToken
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}; |