fix: auto login and logout functionality (#3629)

* Made auto login not call refresh

* Fixed mutation query

* Removed navigation to login on logout function

* Made refresh token set cookie for docker

* Added return to login with custom navigate and added check for refreshing only if its authenticated

* Minor refactor

* Navigate after logout on get autologin

* Removed unused console.log
This commit is contained in:
Lucas Oliveira 2024-08-30 15:19:32 -03:00 • committed by GitHub
commit a71375c6f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 49 additions and 61 deletions

View file

@ -1,24 +1,16 @@
import { import {
LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS, LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS,
LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV, LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV,
LANGFLOW_AUTO_LOGIN_OPTION,
} from "@/constants/constants"; } from "@/constants/constants";
import { import { useRefreshAccessToken } from "@/controllers/API/queries/auth";
useLogout, import { CustomNavigate } from "@/customization/components/custom-navigate";
useRefreshAccessToken,
} from "@/controllers/API/queries/auth";
import useAuthStore from "@/stores/authStore"; import useAuthStore from "@/stores/authStore";
import { useEffect } from "react"; import { useEffect } from "react";
import { Cookies } from "react-cookie";
export const ProtectedRoute = ({ children }) => { export const ProtectedRoute = ({ children }) => {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated); const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const hasToken = !!localStorage.getItem(LANGFLOW_AUTO_LOGIN_OPTION);
const cookies = new Cookies();
const refreshToken = cookies.get("refresh_token");
const { mutate: mutateRefresh } = useRefreshAccessToken(); const { mutate: mutateRefresh } = useRefreshAccessToken();
const { mutate: mutationLogout } = useLogout(); const autoLogin = useAuthStore((state) => state.autoLogin);
useEffect(() => { useEffect(() => {
const envRefreshTime = LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV; const envRefreshTime = LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV;
@ -29,19 +21,17 @@ export const ProtectedRoute = ({ children }) => {
: envRefreshTime; : envRefreshTime;
const intervalFunction = () => { const intervalFunction = () => {
if (isAuthenticated) { mutateRefresh();
mutateRefresh({ refresh_token: refreshToken });
}
}; };
const intervalId = setInterval(intervalFunction, accessTokenTimer * 1000); if (!autoLogin && isAuthenticated) {
intervalFunction(); const intervalId = setInterval(intervalFunction, accessTokenTimer * 1000);
intervalFunction();
return () => clearInterval(intervalId); return () => clearInterval(intervalId);
}
}, [isAuthenticated]); }, [isAuthenticated]);
if (!isAuthenticated && !autoLogin) {
if (!isAuthenticated && hasToken) { return <CustomNavigate to="/login" replace />;
mutationLogout();
} else { } else {
return children; return children;
} }

View file

@ -145,21 +145,18 @@ function ApiInterceptor() {
error.config.headers[key] = value; error.config.headers[key] = value;
} }
} }
mutationRenewAccessToken( mutationRenewAccessToken(undefined, {
{}, onSuccess: async () => {
{ authenticationErrorCount = 0;
onSuccess: async (data) => { await remakeRequest(error);
authenticationErrorCount = 0; authenticationErrorCount = 0;
await remakeRequest(error);
authenticationErrorCount = 0;
},
onError: (error) => {
console.error(error);
mutationLogout();
return Promise.reject("Authentication error");
},
}, },
); onError: (error) => {
console.error(error);
mutationLogout();
return Promise.reject("Authentication error");
},
});
} }
async function clearBuildVerticesState(error) { async function clearBuildVerticesState(error) {

View file

@ -1,4 +1,5 @@
import { AuthContext } from "@/contexts/authContext"; import { AuthContext } from "@/contexts/authContext";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import useAuthStore from "@/stores/authStore"; import useAuthStore from "@/stores/authStore";
import { AxiosError } from "axios"; import { AxiosError } from "axios";
import { useContext } from "react"; import { useContext } from "react";
@ -23,7 +24,8 @@ export const useGetAutoLogin: useQueryFunctionType<undefined, undefined> = (
const setAutoLogin = useAuthStore((state) => state.setAutoLogin); const setAutoLogin = useAuthStore((state) => state.setAutoLogin);
const isAuthenticated = useAuthStore((state) => state.isAuthenticated); const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const isLoginPage = location.pathname.includes("login"); const isLoginPage = location.pathname.includes("login");
const { mutate: mutationLogout } = useLogout(); const navigate = useCustomNavigate();
const { mutateAsync: mutationLogout } = useLogout();
async function getAutoLoginFn(): Promise<null> { async function getAutoLoginFn(): Promise<null> {
try { try {
@ -41,8 +43,8 @@ export const useGetAutoLogin: useQueryFunctionType<undefined, undefined> = (
setAutoLogin(false); setAutoLogin(false);
if (!isLoginPage) { if (!isLoginPage) {
if (!isAuthenticated) { if (!isAuthenticated) {
mutationLogout(); await mutationLogout();
throw new Error("Unauthorized"); navigate("/login");
} else { } else {
getUser(); getUser();
} }

View file

@ -1,9 +1,5 @@
import { keepPreviousData, UseMutationResult } from "@tanstack/react-query"; import { UseMutationResult } from "@tanstack/react-query";
import { import { useMutationFunctionType, Users } from "../../../../types/api";
useMutationFunctionType,
useQueryFunctionType,
Users,
} from "../../../../types/api";
import { api } from "../../api"; import { api } from "../../api";
import { getURL } from "../../helpers/constants"; import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor"; import { UseRequestProcessor } from "../../services/request-processor";
@ -35,7 +31,7 @@ export const useGetUsers: useMutationFunctionType<any, getUsersQueryParams> = (
getUsersQueryParams, getUsersQueryParams,
any, any,
getUsersQueryParams getUsersQueryParams
> = mutate(["useRefreshAccessToken"], getUsers, options); > = mutate(["useGetUsers"], getUsers, options);
return mutation; return mutation;
}; };

View file

@ -1,7 +1,6 @@
import useAuthStore from "@/stores/authStore"; import useAuthStore from "@/stores/authStore";
import { useMutationFunctionType } from "@/types/api"; import { useMutationFunctionType } from "@/types/api";
import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate";
import { api } from "../../api"; import { api } from "../../api";
import { getURL } from "../../helpers/constants"; import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor"; import { UseRequestProcessor } from "../../services/request-processor";
@ -10,7 +9,6 @@ export const useLogout: useMutationFunctionType<undefined, void> = (
options?, options?,
) => { ) => {
const { mutate } = UseRequestProcessor(); const { mutate } = UseRequestProcessor();
const navigate = useCustomNavigate();
const logout = useAuthStore((state) => state.logout); const logout = useAuthStore((state) => state.logout);
async function logoutUser(): Promise<any> { async function logoutUser(): Promise<any> {
@ -25,7 +23,6 @@ export const useLogout: useMutationFunctionType<undefined, void> = (
const mutation = mutate(["useLogout"], logoutUser, { const mutation = mutate(["useLogout"], logoutUser, {
onSuccess: () => { onSuccess: () => {
logout(); logout();
navigate("/login");
}, },
onError: (error) => { onError: (error) => {
console.error(error); console.error(error);

View file

@ -1,24 +1,31 @@
import { LoginType, changeUser, useMutationFunctionType } from "@/types/api"; import { LANGFLOW_REFRESH_TOKEN } from "@/constants/constants";
import { UseMutationResult } from "@tanstack/react-query"; import { useMutationFunctionType } from "@/types/api";
import { Cookies } from "react-cookie";
import { api } from "../../api"; import { api } from "../../api";
import { getURL } from "../../helpers/constants"; import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor"; import { UseRequestProcessor } from "../../services/request-processor";
interface IRefreshAccessToken {
access_token: string;
refresh_token: string;
token_type: string;
}
export const useRefreshAccessToken: useMutationFunctionType<undefined, any> = ( export const useRefreshAccessToken: useMutationFunctionType<
options?, undefined,
) => { undefined | void,
IRefreshAccessToken
> = (options?) => {
const { mutate } = UseRequestProcessor(); const { mutate } = UseRequestProcessor();
const cookies = new Cookies();
async function refreshAccess(): Promise<IRefreshAccessToken> {
const res = await api.post<IRefreshAccessToken>(`${getURL("REFRESH")}`);
cookies.set(LANGFLOW_REFRESH_TOKEN, res.data.refresh_token, { path: "/" });
async function refreshAccess(): Promise<any> {
const res = await api.post(`${getURL("REFRESH")}`);
return res.data; return res.data;
} }
const mutation: UseMutationResult = mutate( const mutation = mutate(["useRefreshAccessToken"], refreshAccess, options);
["useRefreshAccessToken"],
refreshAccess,
options,
);
return mutation; return mutation;
}; };

View file

@ -42,7 +42,6 @@ export default function UserManagementModal({
}: inputHandlerEventType): void { }: inputHandlerEventType): void {
setInputState((prev) => ({ ...prev, [name]: value })); setInputState((prev) => ({ ...prev, [name]: value }));
} }
console.log(data);
useEffect(() => { useEffect(() => {
if (open) { if (open) {