Files
ridebus-admin/frontend/src/contexts/AuthContext.jsx
Zakhar Timoshenko f218f0123a Add SVG assets for login devices and passkey icons
- Created a new SVG file for login devices with detailed paths and styles.
- Added a new SVG file for the passkey icon with multiple vector paths and fill attributes.
2026-09-17 20:31:10 +03:00

142 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createContext, useState, useContext, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import { getApiBaseUrl } from '../utils/apiBaseUrl';
const AuthContext = createContext(null);
const API_BASE_URL = getApiBaseUrl();
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const navigate = useNavigate();
useEffect(() => {
checkAuth();
}, []);
const checkAuth = async () => {
const token = localStorage.getItem('accessToken');
const refreshToken = localStorage.getItem('refreshToken');
if (!token || !refreshToken) {
setLoading(false);
return;
}
try {
const response = await axios.get(`${API_BASE_URL}/auth/me`, {
headers: { Authorization: `Bearer ${token}` }
});
setUser(response.data);
} catch (error) {
// Try to refresh token
try {
const refreshResponse = await axios.post(`${API_BASE_URL}/auth/refresh`, {
refreshToken
});
localStorage.setItem('accessToken', refreshResponse.data.accessToken);
// Retry getting user
const userResponse = await axios.get(`${API_BASE_URL}/auth/me`, {
headers: { Authorization: `Bearer ${refreshResponse.data.accessToken}` }
});
setUser(userResponse.data);
} catch (refreshError) {
// Refresh failed, logout
logout();
}
} finally {
setLoading(false);
}
};
const login = async (username, password) => {
try {
const response = await axios.post(`${API_BASE_URL}/auth/login`, {
username,
password
});
completeLogin(response.data);
return { success: true };
} catch (error) {
console.error('Login error:', error);
return {
success: false,
error: error.response?.data?.error || 'Login failed'
};
}
};
const loginWithPasskey = async (credential) => {
try {
const response = await axios.post(
`${API_BASE_URL}/auth/passkeys/authentication/verify`,
{ response: credential }
);
completeLogin(response.data);
return { success: true };
} catch (error) {
console.error('Passkey login error:', error);
return {
success: false,
error: error.response?.data?.error || 'Не удалось выполнить вход по passkey'
};
}
};
const completeLogin = ({ user, accessToken, refreshToken }) => {
localStorage.setItem('accessToken', accessToken);
localStorage.setItem('refreshToken', refreshToken);
setUser(user);
navigate('/');
};
const logout = async () => {
const refreshToken = localStorage.getItem('refreshToken');
if (refreshToken) {
try {
await axios.post(`${API_BASE_URL}/auth/logout`, { refreshToken });
} catch (error) {
console.error('Logout error:', error);
}
}
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
setUser(null);
navigate('/login');
};
const getAccessToken = () => {
return localStorage.getItem('accessToken');
};
const value = {
user,
loading,
login,
loginWithPasskey,
logout,
getAccessToken,
isAuthenticated: !!user,
isAdmin: user?.role === 'admin'
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}