diff --git a/.vscode/launch.json b/.vscode/launch.json
index 55aa17b353..8d01312e36 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -21,6 +21,13 @@
       "args": ["runserver"],
       "django": true,
       "justMyCode": false
+    },
+    {
+      "name": "InvenTree Frontend - Vite",
+      "type": "chrome",
+      "request": "launch",
+      "url": "http://localhost:5173",
+      "webRoot": "${workspaceFolder}/src/frontend"
     }
   ]
 }
diff --git a/InvenTree/InvenTree/api.py b/InvenTree/InvenTree/api.py
index d47cf4b792..ad8d29666e 100644
--- a/InvenTree/InvenTree/api.py
+++ b/InvenTree/InvenTree/api.py
@@ -120,36 +120,32 @@ class InfoView(AjaxView):
             'email_configured': is_email_configured(),
             'debug_mode': settings.DEBUG,
             'docker_mode': settings.DOCKER,
+            'default_locale': settings.LANGUAGE_CODE,
+            # Following fields are only available to staff users
             'system_health': check_system_health() if is_staff else None,
             'database': InvenTree.version.inventreeDatabase() if is_staff else None,
             'platform': InvenTree.version.inventreePlatform() if is_staff else None,
             'installer': InvenTree.version.inventreeInstaller() if is_staff else None,
             'target': InvenTree.version.inventreeTarget() if is_staff else None,
-            'default_locale': settings.LANGUAGE_CODE,
         }
 
         return JsonResponse(data)
 
     def check_auth_header(self, request):
         """Check if user is authenticated via a token in the header."""
-        # TODO @matmair: remove after refacgtor of Token check is done
-        headers = request.headers.get(
-            'Authorization', request.headers.get('authorization')
-        )
-        if not headers:
-            return False
+        from InvenTree.middleware import get_token_from_request
 
-        auth = headers.strip()
-        if not (auth.lower().startswith('token') and len(auth.split()) == 2):
-            return False
+        if token := get_token_from_request(request):
+            # Does the provided token match a valid user?
+            try:
+                token = ApiToken.objects.get(key=token)
+
+                # Check if the token is active and the user is a staff member
+                if token.active and token.user and token.user.is_staff:
+                    return True
+            except ApiToken.DoesNotExist:
+                pass
 
-        token_key = auth.split()[1]
-        try:
-            token = ApiToken.objects.get(key=token_key)
-            if token.active and token.user and token.user.is_staff:
-                return True
-        except ApiToken.DoesNotExist:
-            pass
         return False
 
 
diff --git a/InvenTree/InvenTree/middleware.py b/InvenTree/InvenTree/middleware.py
index 1a25202f23..e9a272e13b 100644
--- a/InvenTree/InvenTree/middleware.py
+++ b/InvenTree/InvenTree/middleware.py
@@ -23,8 +23,6 @@ def get_token_from_request(request):
     auth_keys = ['Authorization', 'authorization']
     token_keys = ['token', 'bearer']
 
-    token = None
-
     for k in auth_keys:
         if auth_header := request.headers.get(k, None):
             auth_header = auth_header.strip().lower().split()
@@ -32,9 +30,9 @@ def get_token_from_request(request):
             if len(auth_header) > 1:
                 if auth_header[0].strip().lower().replace(':', '') in token_keys:
                     token = auth_header[1]
-                    break
+                    return token
 
-    return token
+    return None
 
 
 class AuthRequiredMiddleware(object):
diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py
index d16c322d3f..ff750a2166 100644
--- a/InvenTree/InvenTree/settings.py
+++ b/InvenTree/InvenTree/settings.py
@@ -445,9 +445,9 @@ REST_FRAMEWORK = {
     'EXCEPTION_HANDLER': 'InvenTree.exceptions.exception_handler',
     'DATETIME_FORMAT': '%Y-%m-%d %H:%M',
     'DEFAULT_AUTHENTICATION_CLASSES': (
+        'users.authentication.ApiTokenAuthentication',
         'rest_framework.authentication.BasicAuthentication',
         'rest_framework.authentication.SessionAuthentication',
-        'users.authentication.ApiTokenAuthentication',
     ),
     'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
     'DEFAULT_PERMISSION_CLASSES': (
diff --git a/docs/docs/develop/react-frontend.md b/docs/docs/develop/react-frontend.md
index 5f1d3853c1..227e12c842 100644
--- a/docs/docs/develop/react-frontend.md
+++ b/docs/docs/develop/react-frontend.md
@@ -39,6 +39,13 @@ This command does not run as a background daemon, and will occupy the window it'
 When the frontend server is running, it will be available on port 5173.
 i.e: https://localhost:5173/
 
+### Debugging
+
+You can attach the vscode debugger to the frontend server to debug the frontend code. With the frontend server running, open the `Run and Debug` view in vscode and select `InvenTree Frontend - Vite` from the dropdown. Click the play button to start debugging. This will attach the debugger to the running vite server, and allow you to place breakpoints in the frontend code.
+
+!!! info "Backend Server"
+    To debug the frontend code, the backend server must be running (in a separate process). Note that you cannot debug the backend server and the frontend server in the same vscode instance.
+
 ### Information
 
 On Windows, any Docker interaction is run via WSL. Naturally, all containers and devcontainers run through WSL.
diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx
index f7e3a19df1..a0710fdb2b 100644
--- a/src/frontend/src/App.tsx
+++ b/src/frontend/src/App.tsx
@@ -1,22 +1,42 @@
 import { QueryClient } from '@tanstack/react-query';
 import axios from 'axios';
 
+import { getCsrfCookie } from './functions/auth';
 import { useLocalState } from './states/LocalState';
 import { useSessionState } from './states/SessionState';
 
-// API
+// Global API instance
 export const api = axios.create({});
 
+/*
+ * Setup default settings for the Axios API instance.
+ *
+ * This includes:
+ * - Base URL
+ * - Authorization token (if available)
+ * - CSRF token (if available)
+ */
 export function setApiDefaults() {
   const host = useLocalState.getState().host;
   const token = useSessionState.getState().token;
 
   api.defaults.baseURL = host;
-  api.defaults.headers.common['Authorization'] = `Token ${token}`;
 
-  // CSRF support (needed for POST, PUT, PATCH, DELETE)
-  api.defaults.withCredentials = true;
-  api.defaults.xsrfCookieName = 'csrftoken';
-  api.defaults.xsrfHeaderName = 'X-CSRFToken';
+  if (!!token) {
+    api.defaults.headers.common['Authorization'] = `Token ${token}`;
+  } else {
+    api.defaults.headers.common['Authorization'] = undefined;
+  }
+
+  if (!!getCsrfCookie()) {
+    api.defaults.withCredentials = true;
+    api.defaults.xsrfCookieName = 'csrftoken';
+    api.defaults.xsrfHeaderName = 'X-CSRFToken';
+  } else {
+    api.defaults.withCredentials = false;
+    api.defaults.xsrfCookieName = undefined;
+    api.defaults.xsrfHeaderName = undefined;
+  }
 }
+
 export const queryClient = new QueryClient();
diff --git a/src/frontend/src/components/forms/AuthenticationForm.tsx b/src/frontend/src/components/forms/AuthenticationForm.tsx
index 9d01c79018..3b2019c19b 100644
--- a/src/frontend/src/components/forms/AuthenticationForm.tsx
+++ b/src/frontend/src/components/forms/AuthenticationForm.tsx
@@ -18,8 +18,9 @@ import { useNavigate } from 'react-router-dom';
 
 import { api } from '../../App';
 import { ApiEndpoints } from '../../enums/ApiEndpoints';
-import { doClassicLogin, doSimpleLogin } from '../../functions/auth';
+import { doBasicLogin, doSimpleLogin } from '../../functions/auth';
 import { apiUrl, useServerApiState } from '../../states/ApiState';
+import { useSessionState } from '../../states/SessionState';
 
 export function AuthenticationForm() {
   const classicForm = useForm({
@@ -36,19 +37,13 @@ export function AuthenticationForm() {
     setIsLoggingIn(true);
 
     if (classicLoginMode === true) {
-      doClassicLogin(
+      doBasicLogin(
         classicForm.values.username,
         classicForm.values.password
-      ).then((ret) => {
+      ).then(() => {
         setIsLoggingIn(false);
 
-        if (ret === false) {
-          notifications.show({
-            title: t`Login failed`,
-            message: t`Check your input and try again.`,
-            color: 'red'
-          });
-        } else {
+        if (useSessionState.getState().hasToken()) {
           notifications.show({
             title: t`Login successful`,
             message: t`Welcome back!`,
@@ -56,6 +51,12 @@ export function AuthenticationForm() {
             icon: 
           });
           navigate('/home');
+        } else {
+          notifications.show({
+            title: t`Login failed`,
+            message: t`Check your input and try again.`,
+            color: 'red'
+          });
         }
       });
     } else {
diff --git a/src/frontend/src/components/nav/MainMenu.tsx b/src/frontend/src/components/nav/MainMenu.tsx
index 3fb69120da..e98934e56a 100644
--- a/src/frontend/src/components/nav/MainMenu.tsx
+++ b/src/frontend/src/components/nav/MainMenu.tsx
@@ -7,13 +7,14 @@ import {
   IconUserBolt,
   IconUserCog
 } from '@tabler/icons-react';
-import { Link } from 'react-router-dom';
+import { Link, useNavigate } from 'react-router-dom';
 
-import { doClassicLogout } from '../../functions/auth';
+import { doLogout } from '../../functions/auth';
 import { InvenTreeStyle } from '../../globalStyle';
 import { useUserState } from '../../states/UserState';
 
 export function MainMenu() {
+  const navigate = useNavigate();
   const { classes, theme } = InvenTreeStyle();
   const userState = useUserState();
 
@@ -63,7 +64,7 @@ export function MainMenu() {
         
}
           onClick={() => {
-            doClassicLogout();
+            doLogout(navigate);
           }}
         >
           Logout
diff --git a/src/frontend/src/components/render/StatusRenderer.tsx b/src/frontend/src/components/render/StatusRenderer.tsx
index 4b2ff24827..21bcc549ac 100644
--- a/src/frontend/src/components/render/StatusRenderer.tsx
+++ b/src/frontend/src/components/render/StatusRenderer.tsx
@@ -2,7 +2,7 @@ import { Badge, Center, MantineSize } from '@mantine/core';
 
 import { colorMap } from '../../defaults/backendMappings';
 import { ModelType } from '../../enums/ModelType';
-import { useServerApiState } from '../../states/ApiState';
+import { useGlobalStatusState } from '../../states/StatusState';
 
 interface StatusCodeInterface {
   key: string;
@@ -72,7 +72,7 @@ export const StatusRenderer = ({
   type: ModelType | string;
   options?: renderStatusLabelOptionsInterface;
 }) => {
-  const statusCodeList = useServerApiState.getState().status;
+  const statusCodeList = useGlobalStatusState.getState().status;
 
   if (status === undefined) {
     console.log('StatusRenderer: status is undefined');
diff --git a/src/frontend/src/components/settings/SettingItem.tsx b/src/frontend/src/components/settings/SettingItem.tsx
index 4fce3cc145..ceee00f395 100644
--- a/src/frontend/src/components/settings/SettingItem.tsx
+++ b/src/frontend/src/components/settings/SettingItem.tsx
@@ -47,7 +47,6 @@ function SettingValue({
         settingsState.fetchSettings();
       })
       .catch((error) => {
-        console.log('Error editing setting', error);
         showNotification({
           title: t`Error editing setting`,
           message: error.message,
diff --git a/src/frontend/src/contexts/LanguageContext.tsx b/src/frontend/src/contexts/LanguageContext.tsx
index 4780d1006a..2c926e8ea8 100644
--- a/src/frontend/src/contexts/LanguageContext.tsx
+++ b/src/frontend/src/contexts/LanguageContext.tsx
@@ -7,6 +7,7 @@ import { useEffect, useRef, useState } from 'react';
 import { api } from '../App';
 import { useServerApiState } from '../states/ApiState';
 import { useLocalState } from '../states/LocalState';
+import { fetchGlobalStates } from '../states/states';
 
 // Definitions
 export type Locales = keyof typeof languages | 'pseudo-LOCALE';
@@ -90,8 +91,8 @@ export function LanguageContext({ children }: { children: JSX.Element }) {
         // Update default Accept-Language headers
         api.defaults.headers.common['Accept-Language'] = locales.join(', ');
 
-        // Reload server state (refresh status codes)
-        useServerApiState.getState().fetchServerApiState();
+        // Reload server state (and refresh status codes)
+        fetchGlobalStates();
 
         // Clear out cached table column names
         useLocalState.getState().clearTableColumnNames();
diff --git a/src/frontend/src/functions/auth.tsx b/src/frontend/src/functions/auth.tsx
index 8451bc9e76..ec49f7fba5 100644
--- a/src/frontend/src/functions/auth.tsx
+++ b/src/frontend/src/functions/auth.tsx
@@ -1,77 +1,90 @@
 import { t } from '@lingui/macro';
-import { notifications, showNotification } from '@mantine/notifications';
+import { notifications } from '@mantine/notifications';
 import { IconCheck } from '@tabler/icons-react';
 import axios from 'axios';
 
-import { api } from '../App';
+import { api, setApiDefaults } from '../App';
 import { ApiEndpoints } from '../enums/ApiEndpoints';
-import { apiUrl, useServerApiState } from '../states/ApiState';
+import { apiUrl } from '../states/ApiState';
 import { useLocalState } from '../states/LocalState';
 import { useSessionState } from '../states/SessionState';
-import {
-  useGlobalSettingsState,
-  useUserSettingsState
-} from '../states/SettingsState';
-import { useUserState } from '../states/UserState';
 
-export const doClassicLogin = async (username: string, password: string) => {
+const tokenName: string = 'inventree-web-app';
+
+/**
+ * Attempt to login using username:password combination.
+ * If login is successful, an API token will be returned.
+ * This API token is used for any future API requests.
+ */
+export const doBasicLogin = async (username: string, password: string) => {
   const { host } = useLocalState.getState();
+  // const apiState = useServerApiState.getState();
 
-  // Get token from server
-  const token = await axios
+  if (username.length == 0 || password.length == 0) {
+    return;
+  }
+
+  // At this stage, we can assume that we are not logged in, and we have no token
+  useSessionState.getState().clearToken();
+
+  // Request new token from the server
+  await axios
     .get(apiUrl(ApiEndpoints.user_token), {
       auth: { username, password },
       baseURL: host,
       timeout: 2000,
       params: {
-        name: 'inventree-web-app'
+        name: tokenName
       }
     })
-    .then((response) => response.data.token)
-    .catch((error) => {
-      showNotification({
-        title: t`Login failed`,
-        message: t`Error fetching token from server.`,
-        color: 'red'
-      });
-      return false;
-    });
-
-  if (token === false) return token;
-
-  // log in with token
-  doTokenLogin(token);
-  return true;
+    .then((response) => {
+      if (response.status == 200 && response.data.token) {
+        // A valid token has been returned - save, and login
+        useSessionState.getState().setToken(response.data.token);
+      }
+    })
+    .catch(() => {});
 };
 
 /**
- * Logout the user (invalidate auth token)
+ * Logout the user from the current session
+ *
+ * @arg deleteToken: If true, delete the token from the server
  */
-export const doClassicLogout = async () => {
-  // Set token in context
-  const { setToken } = useSessionState.getState();
-
-  setToken(undefined);
-
+export const doLogout = async (navigate: any) => {
   // Logout from the server session
   await api.post(apiUrl(ApiEndpoints.user_logout));
 
+  // Logout from this session
+  // Note that clearToken() then calls setApiDefaults()
+  clearCsrfCookie();
+  useSessionState.getState().clearToken();
+
+  notifications.hide('login');
   notifications.show({
+    id: 'login',
     title: t`Logout successful`,
     message: t`You have been logged out`,
     color: 'green',
     icon: 
   });
 
-  return true;
+  navigate('/login');
 };
 
 export const doSimpleLogin = async (email: string) => {
   const { host } = useLocalState.getState();
   const mail = await axios
-    .post(apiUrl(ApiEndpoints.user_simple_login), {
-      email: email
-    })
+    .post(
+      apiUrl(ApiEndpoints.user_simple_login),
+      {
+        email: email
+      },
+      {
+        baseURL: host,
+        timeout: 2000
+      }
+    )
     .then((response) => response.data)
     .catch((_error) => {
       return false;
@@ -79,21 +92,6 @@ export const doSimpleLogin = async (email: string) => {
   return mail;
 };
 
-// Perform a login using a token
-export const doTokenLogin = (token: string) => {
-  const { setToken } = useSessionState.getState();
-  const { fetchUserState } = useUserState.getState();
-  const { fetchServerApiState } = useServerApiState.getState();
-  const globalSettingsState = useGlobalSettingsState.getState();
-  const userSettingsState = useUserSettingsState.getState();
-
-  setToken(token);
-  fetchUserState();
-  fetchServerApiState();
-  globalSettingsState.fetchSettings();
-  userSettingsState.fetchSettings();
-};
-
 export function handleReset(navigate: any, values: { email: string }) {
   api
     .post(apiUrl(ApiEndpoints.user_reset), values, {
@@ -119,36 +117,96 @@ export function handleReset(navigate: any, values: { email: string }) {
 }
 
 /**
- * Check login state, and redirect the user as required
+ * Check login state, and redirect the user as required.
+ *
+ * The user may be logged in via the following methods:
+ * - An existing API token is stored in the session
+ * - An existing CSRF cookie is stored in the browser
  */
 export function checkLoginState(
   navigate: any,
   redirect?: string,
   no_redirect?: boolean
 ) {
-  api
-    .get(apiUrl(ApiEndpoints.user_token), {
-      timeout: 2000,
-      params: {
-        name: 'inventree-web-app'
-      }
-    })
-    .then((val) => {
-      if (val.status === 200 && val.data.token) {
-        doTokenLogin(val.data.token);
+  setApiDefaults();
 
-        notifications.show({
-          title: t`Already logged in`,
-          message: t`Found an existing login - using it to log you in.`,
-          color: 'green',
-          icon: 
-        });
-        navigate(redirect ?? '/home');
-      } else {
-        navigate('/login');
-      }
-    })
-    .catch(() => {
-      if (!no_redirect) navigate('/login');
+  // Callback function when login is successful
+  const loginSuccess = () => {
+    notifications.hide('login');
+    notifications.show({
+      id: 'login',
+      title: t`Logged In`,
+      message: t`Found an existing login - welcome back!`,
+      color: 'green',
+      icon: 
     });
+    navigate(redirect ?? '/home');
+  };
+
+  // Callback function when login fails
+  const loginFailure = () => {
+    useSessionState.getState().clearToken();
+    if (!no_redirect) navigate('/login');
+  };
+
+  if (useSessionState.getState().hasToken()) {
+    // An existing token is available - check if it works
+    api
+      .get(apiUrl(ApiEndpoints.user_me), {
+        timeout: 2000
+      })
+      .then((val) => {
+        if (val.status === 200) {
+          // Success: we are logged in (and we already have a token)
+          loginSuccess();
+        } else {
+          loginFailure();
+        }
+      })
+      .catch(() => {
+        loginFailure();
+      });
+  } else if (getCsrfCookie()) {
+    // Try to fetch a new token using the CSRF cookie
+    api
+      .get(apiUrl(ApiEndpoints.user_token), {
+        params: {
+          name: tokenName
+        }
+      })
+      .then((response) => {
+        if (response.status == 200 && response.data.token) {
+          useSessionState.getState().setToken(response.data.token);
+          loginSuccess();
+        } else {
+          loginFailure();
+        }
+      })
+      .catch(() => {
+        loginFailure();
+      });
+  } else {
+    // No token, no cookie - redirect to login page
+    loginFailure();
+  }
+}
+
+/*
+ * Return the value of the CSRF cookie, if available
+ */
+export function getCsrfCookie() {
+  const cookieValue = document.cookie
+    .split('; ')
+    .find((row) => row.startsWith('csrftoken='))
+    ?.split('=')[1];
+
+  return cookieValue;
+}
+
+/*
+ * Clear out the CSRF cookie (force session logout)
+ */
+export function clearCsrfCookie() {
+  document.cookie =
+    'csrftoken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
 }
diff --git a/src/frontend/src/pages/Auth/Login.tsx b/src/frontend/src/pages/Auth/Login.tsx
index 16de1b7509..f794bbcfd4 100644
--- a/src/frontend/src/pages/Auth/Login.tsx
+++ b/src/frontend/src/pages/Auth/Login.tsx
@@ -49,6 +49,7 @@ export default function Login() {
     // check if user is logged in in PUI
     checkLoginState(navigate, undefined, true);
   }, []);
+
   // Fetch server data on mount if no server data is present
   useEffect(() => {
     if (server.server === null) {
diff --git a/src/frontend/src/states/ApiState.tsx b/src/frontend/src/states/ApiState.tsx
index eb144533b4..2b284efcf4 100644
--- a/src/frontend/src/states/ApiState.tsx
+++ b/src/frontend/src/states/ApiState.tsx
@@ -2,20 +2,14 @@ import { create } from 'zustand';
 import { createJSONStorage, persist } from 'zustand/middleware';
 
 import { api } from '../App';
-import { StatusCodeListInterface } from '../components/render/StatusRenderer';
-import { statusCodeList } from '../defaults/backendMappings';
 import { emptyServerAPI } from '../defaults/defaults';
 import { ApiEndpoints } from '../enums/ApiEndpoints';
-import { ModelType } from '../enums/ModelType';
 import { AuthProps, ServerAPIProps } from './states';
 
-type StatusLookup = Record;
-
 interface ServerApiStateProps {
   server: ServerAPIProps;
   setServer: (newServer: ServerAPIProps) => void;
   fetchServerApiState: () => void;
-  status?: StatusLookup;
   auth_settings?: AuthProps;
 }
 
@@ -31,19 +25,9 @@ export const useServerApiState = create()(
           .then((response) => {
             set({ server: response.data });
           })
-          .catch(() => {});
-        // Fetch status data for rendering labels
-        await api
-          .get(apiUrl(ApiEndpoints.global_status))
-          .then((response) => {
-            const newStatusLookup: StatusLookup = {} as StatusLookup;
-            for (const key in response.data) {
-              newStatusLookup[statusCodeList[key] || key] =
-                response.data[key].values;
-            }
-            set({ status: newStatusLookup });
-          })
-          .catch(() => {});
+          .catch(() => {
+            console.error('Error fetching server info');
+          });
 
         // Fetch login/SSO behaviour
         await api
@@ -53,7 +37,9 @@ export const useServerApiState = create()(
           .then((response) => {
             set({ auth_settings: response.data });
           })
-          .catch(() => {});
+          .catch(() => {
+            console.error('Error fetching SSO information');
+          });
       },
       status: undefined
     }),
diff --git a/src/frontend/src/states/SessionState.tsx b/src/frontend/src/states/SessionState.tsx
index 54f1e58b9e..5ac12407d7 100644
--- a/src/frontend/src/states/SessionState.tsx
+++ b/src/frontend/src/states/SessionState.tsx
@@ -2,20 +2,32 @@ import { create } from 'zustand';
 import { createJSONStorage, persist } from 'zustand/middleware';
 
 import { setApiDefaults } from '../App';
+import { fetchGlobalStates } from './states';
 
 interface SessionStateProps {
   token?: string;
   setToken: (newToken?: string) => void;
+  clearToken: () => void;
+  hasToken: () => boolean;
 }
 
+/*
+ * State manager for user login information.
+ */
 export const useSessionState = create()(
   persist(
-    (set) => ({
-      token: '',
+    (set, get) => ({
+      token: undefined,
+      clearToken: () => {
+        set({ token: undefined });
+      },
       setToken: (newToken) => {
         set({ token: newToken });
+
         setApiDefaults();
-      }
+        fetchGlobalStates();
+      },
+      hasToken: () => !!get().token
     }),
     {
       name: 'session-state',
diff --git a/src/frontend/src/states/SettingsState.tsx b/src/frontend/src/states/SettingsState.tsx
index 77e9f12b6e..9ac9b21d5d 100644
--- a/src/frontend/src/states/SettingsState.tsx
+++ b/src/frontend/src/states/SettingsState.tsx
@@ -7,6 +7,7 @@ import { api } from '../App';
 import { ApiEndpoints } from '../enums/ApiEndpoints';
 import { isTrue } from '../functions/conversion';
 import { PathParams, apiUrl } from './ApiState';
+import { useSessionState } from './SessionState';
 import { Setting, SettingsLookup } from './states';
 
 export interface SettingsStateProps {
@@ -28,6 +29,10 @@ export const useGlobalSettingsState = create(
     lookup: {},
     endpoint: ApiEndpoints.settings_global_list,
     fetchSettings: async () => {
+      if (!useSessionState.getState().hasToken()) {
+        return;
+      }
+
       await api
         .get(apiUrl(ApiEndpoints.settings_global_list))
         .then((response) => {
@@ -58,6 +63,10 @@ export const useUserSettingsState = create((set, get) => ({
   lookup: {},
   endpoint: ApiEndpoints.settings_user_list,
   fetchSettings: async () => {
+    if (!useSessionState.getState().hasToken()) {
+      return;
+    }
+
     await api
       .get(apiUrl(ApiEndpoints.settings_user_list))
       .then((response) => {
diff --git a/src/frontend/src/states/StatusState.tsx b/src/frontend/src/states/StatusState.tsx
new file mode 100644
index 0000000000..51b31f851d
--- /dev/null
+++ b/src/frontend/src/states/StatusState.tsx
@@ -0,0 +1,51 @@
+import { create } from 'zustand';
+import { createJSONStorage, persist } from 'zustand/middleware';
+
+import { api } from '../App';
+import { StatusCodeListInterface } from '../components/render/StatusRenderer';
+import { statusCodeList } from '../defaults/backendMappings';
+import { ApiEndpoints } from '../enums/ApiEndpoints';
+import { ModelType } from '../enums/ModelType';
+import { apiUrl } from './ApiState';
+import { useSessionState } from './SessionState';
+
+type StatusLookup = Record;
+
+interface ServerStateProps {
+  status?: StatusLookup;
+  setStatus: (newStatus: StatusLookup) => void;
+  fetchStatus: () => void;
+}
+
+export const useGlobalStatusState = create()(
+  persist(
+    (set) => ({
+      status: undefined,
+      setStatus: (newStatus: StatusLookup) => set({ status: newStatus }),
+      fetchStatus: async () => {
+        // Fetch status data for rendering labels
+        if (!useSessionState.getState().hasToken()) {
+          return;
+        }
+
+        await api
+          .get(apiUrl(ApiEndpoints.global_status))
+          .then((response) => {
+            const newStatusLookup: StatusLookup = {} as StatusLookup;
+            for (const key in response.data) {
+              newStatusLookup[statusCodeList[key] || key] =
+                response.data[key].values;
+            }
+            set({ status: newStatusLookup });
+          })
+          .catch(() => {
+            console.error('Error fetching global status information');
+          });
+      }
+    }),
+    {
+      name: 'global-status-state',
+      storage: createJSONStorage(() => sessionStorage)
+    }
+  )
+);
diff --git a/src/frontend/src/states/UserState.tsx b/src/frontend/src/states/UserState.tsx
index ffa217febf..ef17a8bdf6 100644
--- a/src/frontend/src/states/UserState.tsx
+++ b/src/frontend/src/states/UserState.tsx
@@ -3,8 +3,8 @@ import { create } from 'zustand';
 import { api } from '../App';
 import { ApiEndpoints } from '../enums/ApiEndpoints';
 import { UserPermissions, UserRoles } from '../enums/Roles';
-import { doClassicLogout } from '../functions/auth';
 import { apiUrl } from './ApiState';
+import { useSessionState } from './SessionState';
 import { UserProps } from './states';
 
 interface UserStateProps {
@@ -35,6 +35,10 @@ export const useUserState = create((set, get) => ({
   },
   setUser: (newUser: UserProps) => set({ user: newUser }),
   fetchUserState: async () => {
+    if (!useSessionState.getState().hasToken()) {
+      return;
+    }
+
     // Fetch user data
     await api
       .get(apiUrl(ApiEndpoints.user_me), {
@@ -52,8 +56,6 @@ export const useUserState = create((set, get) => ({
       })
       .catch((error) => {
         console.error('Error fetching user data:', error);
-        // Redirect to login page
-        doClassicLogout();
       });
 
     // Fetch role data
diff --git a/src/frontend/src/states/states.tsx b/src/frontend/src/states/states.tsx
index 27db2fc39a..c2b5f760fc 100644
--- a/src/frontend/src/states/states.tsx
+++ b/src/frontend/src/states/states.tsx
@@ -1,3 +1,9 @@
+import { setApiDefaults } from '../App';
+import { useSessionState } from './SessionState';
+import { useGlobalSettingsState, useUserSettingsState } from './SettingsState';
+import { useGlobalStatusState } from './StatusState';
+import { useUserState } from './UserState';
+
 export interface Host {
   host: string;
   name: string;
@@ -111,3 +117,20 @@ export type ErrorResponse = {
 export type SettingsLookup = {
   [key: string]: string;
 };
+
+/*
+ * Refetch all global state information.
+ * Necessary on login, or if locale is changed.
+ */
+export function fetchGlobalStates() {
+  if (!useSessionState.getState().hasToken()) {
+    return;
+  }
+
+  setApiDefaults();
+
+  useUserState.getState().fetchUserState();
+  useUserSettingsState.getState().fetchSettings();
+  useGlobalSettingsState.getState().fetchSettings();
+  useGlobalStatusState.getState().fetchStatus();
+}
diff --git a/src/frontend/src/tables/Filter.tsx b/src/frontend/src/tables/Filter.tsx
index 1208ae36ca..4a61f30a12 100644
--- a/src/frontend/src/tables/Filter.tsx
+++ b/src/frontend/src/tables/Filter.tsx
@@ -1,7 +1,7 @@
 import { t } from '@lingui/macro';
 
 import { ModelType } from '../enums/ModelType';
-import { useServerApiState } from '../states/ApiState';
+import { useGlobalStatusState } from '../states/StatusState';
 
 /**
  * Interface for the table filter choice
@@ -60,7 +60,7 @@ export function StatusFilterOptions(
   model: ModelType
 ): () => TableFilterChoice[] {
   return () => {
-    const statusCodeList = useServerApiState.getState().status;
+    const statusCodeList = useGlobalStatusState.getState().status;
 
     if (!statusCodeList) {
       return [];
diff --git a/src/frontend/src/views/DesktopAppView.tsx b/src/frontend/src/views/DesktopAppView.tsx
index 410b85b94f..a48445272d 100644
--- a/src/frontend/src/views/DesktopAppView.tsx
+++ b/src/frontend/src/views/DesktopAppView.tsx
@@ -2,7 +2,7 @@ import { QueryClientProvider } from '@tanstack/react-query';
 import { useEffect, useState } from 'react';
 import { BrowserRouter } from 'react-router-dom';
 
-import { queryClient, setApiDefaults } from '../App';
+import { queryClient } from '../App';
 import { BaseContext } from '../contexts/BaseContext';
 import { defaultHostList } from '../defaults/defaultHostList';
 import { base_url } from '../main';
@@ -26,9 +26,6 @@ export default function DesktopAppView() {
     state.fetchSettings
   ]);
 
-  // Local state initialization
-  setApiDefaults();
-
   // Server Session
   const [fetchedServerSession, setFetchedServerSession] = useState(false);
   const sessionState = useSessionState.getState();
diff --git a/src/frontend/src/views/MainView.tsx b/src/frontend/src/views/MainView.tsx
index 473ff9b7bf..1233038d37 100644
--- a/src/frontend/src/views/MainView.tsx
+++ b/src/frontend/src/views/MainView.tsx
@@ -1,6 +1,7 @@
 import { useViewportSize } from '@mantine/hooks';
-import { lazy } from 'react';
+import { lazy, useEffect } from 'react';
 
+import { setApiDefaults } from '../App';
 import { Loadable } from '../functions/loading';
 
 function checkMobile() {
@@ -14,6 +15,12 @@ const DesktopAppView = Loadable(lazy(() => import('./DesktopAppView')));
 
 // Main App
 export default function MainView() {
+  // Set initial login status
+  useEffect(() => {
+    // Local state initialization
+    setApiDefaults();
+  }, []);
+
   // Check if mobile
   if (checkMobile()) {
     return ;