fix: treat auto login session when changed on backend .env (#2768)

* feat(App.tsx): add LANGFLOW_AUTO_LOGIN_OPTION constant to handle auto login feature
feat(authContext.tsx): add LANGFLOW_AUTO_LOGIN_OPTION constant to handle auto login feature
feat(api.tsx): add support for LANGFLOW_AUTO_LOGIN_OPTION constant to handle auto login feature
feat(LoginPage/index.tsx): add support for auto login feature in login function
feat(LoginAdminPage/index.tsx): add support for auto login feature in login function

* [autofix.ci] apply automated fixes

* 🐛 (frontend): fix potential bug by checking if response object is null before accessing status property in saveFlowToDatabase and getFlowFromDatabase functions

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Cristhian Zanforlin Lousa 2024-07-17 15:55:21 -05:00 • committed by GitHub
commit e765a7fee6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 49 additions and 24 deletions

View file

@ -1,4 +1,5 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { Cookies } from "react-cookie";
import { ErrorBoundary } from "react-error-boundary"; import { ErrorBoundary } from "react-error-boundary";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import "reactflow/dist/style.css"; import "reactflow/dist/style.css";
@ -10,6 +11,7 @@ import LoadingComponent from "./components/loadingComponent";
import { import {
FETCH_ERROR_DESCRIPION, FETCH_ERROR_DESCRIPION,
FETCH_ERROR_MESSAGE, FETCH_ERROR_MESSAGE,
LANGFLOW_AUTO_LOGIN_OPTION,
} from "./constants/constants"; } from "./constants/constants";
import { AuthContext } from "./contexts/authContext"; import { AuthContext } from "./contexts/authContext";
import { autoLogin } from "./controllers/API"; import { autoLogin } from "./controllers/API";
@ -27,13 +29,14 @@ import { useFolderStore } from "./stores/foldersStore";
export default function App() { export default function App() {
useTrackLastVisitedPath(); useTrackLastVisitedPath();
const isLoading = useFlowsManagerStore((state) => state.isLoading); const isLoading = useFlowsManagerStore((state) => state.isLoading);
const { isAuthenticated, login, setUserData, setAutoLogin, getUser } = const { isAuthenticated, login, setUserData, setAutoLogin, getUser, logout } =
useContext(AuthContext); useContext(AuthContext);
const setLoading = useAlertStore((state) => state.setLoading); const setLoading = useAlertStore((state) => state.setLoading);
const refreshStars = useDarkStore((state) => state.refreshStars); const refreshStars = useDarkStore((state) => state.refreshStars);
const dark = useDarkStore((state) => state.dark); const dark = useDarkStore((state) => state.dark);
useGetVersionQuery(); useGetVersionQuery();
const cookies = new Cookies();
const isLoadingFolders = useFolderStore((state) => state.isLoadingFolders); const isLoadingFolders = useFolderStore((state) => state.isLoadingFolders);
@ -60,7 +63,7 @@ export default function App() {
.then(async (user) => { .then(async (user) => {
if (user && user["access_token"]) { if (user && user["access_token"]) {
user["refresh_token"] = "auto"; user["refresh_token"] = "auto";
login(user["access_token"]); login(user["access_token"], "auto");
setUserData(user); setUserData(user);
setAutoLogin(true); setAutoLogin(true);
fetchAllData(); fetchAllData();
@ -69,6 +72,14 @@ export default function App() {
.catch(async (error) => { .catch(async (error) => {
if (error.name !== "CanceledError") { if (error.name !== "CanceledError") {
setAutoLogin(false); setAutoLogin(false);
if (
cookies.get(LANGFLOW_AUTO_LOGIN_OPTION) === "auto" &&
isAuthenticated
) {
logout();
return;
}
if (isAuthenticated && !isLoginPage) { if (isAuthenticated && !isLoginPage) {
getUser(); getUser();
fetchAllData(); fetchAllData();

View file

@ -866,3 +866,7 @@ export const TABS_ORDER = [
"python code", "python code",
"chat widget html", "chat widget html",
]; ];
export const LANGFLOW_ACCESS_TOKEN = "access_token_lf";
export const LANGFLOW_API_TOKEN = "apikey_tkn_lflw";
export const LANGFLOW_AUTO_LOGIN_OPTION = "auto_login_lf";

View file

@ -1,3 +1,8 @@
import {
LANGFLOW_ACCESS_TOKEN,
LANGFLOW_API_TOKEN,
LANGFLOW_AUTO_LOGIN_OPTION,
} from "@/constants/constants";
import useFlowsManagerStore from "@/stores/flowsManagerStore"; import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { createContext, useEffect, useState } from "react"; import { createContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@ -38,17 +43,17 @@ export function AuthProvider({ children }): React.ReactElement {
const navigate = useNavigate(); const navigate = useNavigate();
const cookies = new Cookies(); const cookies = new Cookies();
const [accessToken, setAccessToken] = useState<string | null>( const [accessToken, setAccessToken] = useState<string | null>(
cookies.get("access_token_lf") ?? null, cookies.get(LANGFLOW_ACCESS_TOKEN) ?? null,
); );
const [isAuthenticated, setIsAuthenticated] = useState<boolean>( const [isAuthenticated, setIsAuthenticated] = useState<boolean>(
!!cookies.get("access_token_lf"), !!cookies.get(LANGFLOW_ACCESS_TOKEN),
); );
const [isAdmin, setIsAdmin] = useState<boolean>(false); const [isAdmin, setIsAdmin] = useState<boolean>(false);
const [userData, setUserData] = useState<Users | null>(null); const [userData, setUserData] = useState<Users | null>(null);
const [autoLogin, setAutoLogin] = useState<boolean>(false); const [autoLogin, setAutoLogin] = useState<boolean>(false);
const setLoading = useAlertStore((state) => state.setLoading); const setLoading = useAlertStore((state) => state.setLoading);
const [apiKey, setApiKey] = useState<string | null>( const [apiKey, setApiKey] = useState<string | null>(
cookies.get("apikey_tkn_lflw"), cookies.get(LANGFLOW_API_TOKEN),
); );
const getFoldersApi = useFolderStore((state) => state.getFoldersApi); const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
@ -61,14 +66,14 @@ export function AuthProvider({ children }): React.ReactElement {
const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder); const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder);
useEffect(() => { useEffect(() => {
const storedAccessToken = cookies.get("access_token_lf"); const storedAccessToken = cookies.get(LANGFLOW_ACCESS_TOKEN);
if (storedAccessToken) { if (storedAccessToken) {
setAccessToken(storedAccessToken); setAccessToken(storedAccessToken);
} }
}, []); }, []);
useEffect(() => { useEffect(() => {
const apiKey = cookies.get("apikey_tkn_lflw"); const apiKey = cookies.get(LANGFLOW_API_TOKEN);
if (apiKey) { if (apiKey) {
setApiKey(apiKey); setApiKey(apiKey);
} }
@ -91,7 +96,8 @@ export function AuthProvider({ children }): React.ReactElement {
}); });
} }
function login(newAccessToken: string) { function login(newAccessToken: string, autoLogin: string) {
cookies.set(LANGFLOW_AUTO_LOGIN_OPTION, autoLogin, { path: "/" });
setAccessToken(newAccessToken); setAccessToken(newAccessToken);
setIsAuthenticated(true); setIsAuthenticated(true);
getUser(); getUser();
@ -103,7 +109,8 @@ export function AuthProvider({ children }): React.ReactElement {
} }
try { try {
await requestLogout(); await requestLogout();
cookies.remove("apikey_tkn_lflw", { path: "/" }); cookies.remove(LANGFLOW_API_TOKEN, { path: "/" });
cookies.remove(LANGFLOW_AUTO_LOGIN_OPTION, { path: "/" });
setIsAdmin(false); setIsAdmin(false);
setUserData(null); setUserData(null);
setAccessToken(null); setAccessToken(null);
@ -111,6 +118,7 @@ export function AuthProvider({ children }): React.ReactElement {
setAllFlows([]); setAllFlows([]);
setSelectedFolder(null); setSelectedFolder(null);
navigate("/login"); navigate("/login");
useFlowsManagerStore.setState({ isLoading: false });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
throw error; throw error;
@ -118,7 +126,7 @@ export function AuthProvider({ children }): React.ReactElement {
} }
function storeApiKey(apikey: string) { function storeApiKey(apikey: string) {
cookies.set("apikey_tkn_lflw", apikey, { path: "/" }); cookies.set(LANGFLOW_ACCESS_TOKEN, apikey, { path: "/" });
setApiKey(apikey); setApiKey(apikey);
} }

View file

@ -1,3 +1,7 @@
import {
LANGFLOW_ACCESS_TOKEN,
LANGFLOW_AUTO_LOGIN_OPTION,
} from "@/constants/constants";
import axios, { AxiosError, AxiosInstance } from "axios"; import axios, { AxiosError, AxiosInstance } from "axios";
import { useContext, useEffect } from "react"; import { useContext, useEffect } from "react";
import { Cookies } from "react-cookie"; import { Cookies } from "react-cookie";
@ -37,7 +41,7 @@ function ApiInterceptor() {
} }
const acceptedRequest = await tryToRenewAccessToken(error); const acceptedRequest = await tryToRenewAccessToken(error);
const accessToken = cookies.get("access_token_lf"); const accessToken = cookies.get(LANGFLOW_ACCESS_TOKEN);
if (!accessToken && error?.config?.url?.includes("login")) { if (!accessToken && error?.config?.url?.includes("login")) {
return Promise.reject(error); return Promise.reject(error);
@ -90,7 +94,7 @@ function ApiInterceptor() {
console.error("Duplicate Request"); console.error("Duplicate Request");
} }
const accessToken = cookies.get("access_token_lf"); const accessToken = cookies.get(LANGFLOW_ACCESS_TOKEN);
if (accessToken && !isAuthorizedURL(config?.url)) { if (accessToken && !isAuthorizedURL(config?.url)) {
config.headers["Authorization"] = `Bearer ${accessToken}`; config.headers["Authorization"] = `Bearer ${accessToken}`;
} }
@ -129,7 +133,7 @@ function ApiInterceptor() {
if (window.location.pathname.includes("/login")) return; if (window.location.pathname.includes("/login")) return;
const res = await renewAccessToken(); const res = await renewAccessToken();
if (res?.data?.access_token && res?.data?.refresh_token) { if (res?.data?.access_token && res?.data?.refresh_token) {
login(res?.data?.access_token); login(res?.data?.access_token, cookies.get(LANGFLOW_AUTO_LOGIN_OPTION));
} }
} catch (error) { } catch (error) {
clearBuildVerticesState(error); clearBuildVerticesState(error);

View file

@ -140,8 +140,8 @@ export async function saveFlowToDatabase(newFlow: {
endpoint_name: newFlow.endpoint_name, endpoint_name: newFlow.endpoint_name,
}); });
if (response.status !== 201) { if (response?.status !== 201) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response?.status}`);
} }
return response.data; return response.data;
} catch (error) { } catch (error) {
@ -254,8 +254,8 @@ export async function deleteFlowFromDatabase(flowId: string) {
export async function getFlowFromDatabase(flowId: number) { export async function getFlowFromDatabase(flowId: number) {
try { try {
const response = await api.get(`${BASE_URL_API}flows/${flowId}`); const response = await api.get(`${BASE_URL_API}flows/${flowId}`);
if (response.status !== 200) { if (response?.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response?.status}`);
} }
return response.data; return response.data;
} catch (error) { } catch (error) {

View file

@ -40,7 +40,7 @@ export default function LoginAdminPage() {
setLoading(true); setLoading(true);
login(user.access_token); login(user.access_token, "login");
navigate("/admin/"); navigate("/admin/");
}) })
.catch((error) => { .catch((error) => {

View file

@ -39,10 +39,8 @@ export default function LoginPage(): JSX.Element {
}; };
onLogin(user) onLogin(user)
.then((user) => { .then((user) => {
console.log("login page");
setLoading(true); setLoading(true);
login(user.access_token); login(user.access_token, "login");
navigate("/"); navigate("/");
}) })
.catch((error) => { .catch((error) => {

View file

@ -25,7 +25,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
(folder) => folder.name === STARTER_FOLDER_NAME, (folder) => folder.name === STARTER_FOLDER_NAME,
); );
set({ starterProjectId: starterProjects!.id ?? "" }); set({ starterProjectId: starterProjects?.id ?? "" });
set({ folders: foldersWithoutStarterProjects }); set({ folders: foldersWithoutStarterProjects });
const myCollectionId = res?.find( const myCollectionId = res?.find(
@ -72,7 +72,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
(folder) => folder.name === STARTER_FOLDER_NAME, (folder) => folder.name === STARTER_FOLDER_NAME,
); );
set({ starterProjectId: starterProjects!.id ?? "" }); set({ starterProjectId: starterProjects?.id ?? "" });
set({ folders: foldersWithoutStarterProjects }); set({ folders: foldersWithoutStarterProjects });
const myCollectionId = res?.find( const myCollectionId = res?.find(

View file

@ -5,7 +5,7 @@ export type AuthContextType = {
setIsAdmin: (isAdmin: boolean) => void; setIsAdmin: (isAdmin: boolean) => void;
isAuthenticated: boolean; isAuthenticated: boolean;
accessToken: string | null; accessToken: string | null;
login: (accessToken: string) => void; login: (accessToken: string, autoLogin: string) => void;
logout: () => Promise<void>; logout: () => Promise<void>;
userData: Users | null; userData: Users | null;
setUserData: (userData: Users | null) => void; setUserData: (userData: Users | null) => void;