Обработка ошибки 401 Unauthorized при обновлении страницы с помощью JWT и React/Django
Я разрабатываю приложение для аутентификации, используя Django для бэкенда и React для фронтенда. Я реализовал JWT-токены для аутентификации. Когда я вхожу в систему, куки HttpOnly (access_token и refresh_token) устанавливаются правильно. Однако при обновлении страницы я сталкиваюсь с ошибкой 401 (Unauthorized) в консоли.
Ошибка возникла во время GET-запроса к http://localhost:8000/api/get-me/.
Выпуск
После успешного входа в систему, если я обновляю страницу, то получаю в консоли ошибку 401 (Unauthorized) для GET-запроса к конечной точке /api/get-me/. Такое поведение является неожиданным, поскольку пользователь должен быть аутентифицирован после обновления страницы.
Вопрос
Как сделать так, чтобы пользователь оставался аутентифицированным даже после обновления страницы, и избежать ошибки 401 (Unauthorized)? Может быть, что-то упущено или неправильно в моей конфигурации JWT и обработки куки?
Релевантный код
urls.py
path('token/', views.LoginView.as_view(), name='token_obtain_pair'),
path('get-me/', views.GetUserDetailView.as_view(), name='get-me'),
path('token-validate/', views.TokenValidationView.as_view(), name='token-validate'),
views.py
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework.permissions import IsAuthenticated
class TokenValidationView(APIView):
authentication_classes = [JWTAuthentication]
def get(self, request):
# If the code reaches here, it means the token is valid
return Response({"message": "Token is valid"}, status=status.HTTP_200_OK)
class GetUserDetailView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
serializer = UserDetailSerializer(request.user)
print(serializer.data)
return Response(serializer.data)
class LoginView(APIView):
def post(self, request, *args, **kwargs):
serializer = LoginUserSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
user = serializer.validated_data
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
response = Response({
'user_info': {
'id': user.id,
'first_name': user.first_name,
'last_name': user.last_name,
'email': user.email,
'is_email_verified': user.is_email_verified
}
}, status=status.HTTP_200_OK)
response.set_cookie(
'access_token',
access_token,
httponly=True,
max_age=int(refresh.access_token.lifetime.total_seconds()),
secure=True,
)
response.set_cookie(
'refresh_token',
str(refresh),
httponly=True,
max_age=int(refresh.lifetime.total_seconds()),
secure=True
)
return response
return Response(serializer.errors, status=400)
serializers.py
class LoginUserSerializer(serializers.Serializer):
email = serializers.EmailField()
password = serializers.CharField()
def validate(self, data):
user = authenticate(**data)
if user and user.is_active:
return user
raise serializers.ValidationError("Incorrect Credentials")
App.js
const App = () => {
const dispatch = useDispatch();
const user = useSelector(selectUser); // Get the user from Redux store
useEffect(() => {
const validateTokenAndFetchUser = async () => {
try {
// Validate token
await axiosInstance.get('/api/token-validate/');
// If token is valid, fetch user info
const userInfoResponse = await axiosInstance.get('/get-me/');
console.log(userInfoResponse)
dispatch(loginUser({ state: userInfoResponse.data }));
} catch (error) {
console.error("Error in token validation or fetching user data:", error);
}
};
validateTokenAndFetchUser();
}, [dispatch]);
const handleLogin = async (email, password) => {
try {
const userData = await login(email, password);
console.log(userData);
dispatch(loginUser({ email: userData['email'], token: 'tralala' }));
} catch (error) {
console.error("Login failed:", error);
}
};
return (
<Router>
<Menu />
<div>
<Routes>
<Route
path="/login"
element={user ? <Navigate to="/" /> : <Login onLogin={handleLogin} />}
/>
<Route
path="/"
element={user ? <Dashboard user={user} /> : <Home />}
/>
</Routes>
</div>
</Router>
);
};
export default App;
axiosConfig.js
// axiosConfig.js
import axios from 'axios';
const axiosInstance = axios.create({
baseURL: 'http://localhost:8000/api/', // Replace with your API's base URL
headers: {
'Content-Type': 'application/json',
},
withCredentials: true, // This will ensure cookies are sent with the request
});
axiosInstance.interceptors.response.use(response => {
return response;
}, error => {
if (error.response && error.response.status === 401) {
console.log('Error 401')
}
return Promise.reject(error);
});
export default axiosInstance;