Authentication

Jumbo React Vite ships a complete authentication context with cookie-based session persistence and a HOC for protecting routes. The included auth service is a mock — it is designed to be swapped for your real backend with minimal changes.

Architecture

Authentication state is managed by AuthProvider in src/components/AuthProvider/. It exposes an AuthContext with four values:

typescript
interface AuthContextType {
  isAuthenticated: boolean;
  loading: boolean;
  login: (credentials: { email: string; password: string }) => Promise<unknown>;
  logout: () => void;
}

The provider is mounted at the top of the app in App.tsx, wrapping the entire component tree.

How the mock service works

The included mock auth service simulates a network request with a setTimeout. It accepts a single hardcoded credential pair and stores the session in a browser cookie:

typescript
// src/components/AuthProvider/AuthProvider.tsx (simplified)
const iAuthService = async (email: string, password: string) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (email === 'demo@example.com' && password === 'zab#723') {
        resolve({ token: 'auth-user', email, password });
      } else {
        reject('Invalid email or password');
      }
    }, 3000);
  });
};

On successful login, the session object is JSON-encoded, URL-encoded, and stored in the auth-user cookie for one day. On page reload, useEffect reads the cookie and restores isAuthenticated.

Using the auth hook

Any component inside AuthProvider can read auth state and call auth methods:

typescript
import { useAuth } from '@/hooks/useAuth';
 
export function ProfileMenu() {
  const { isAuthenticated, loading, logout } = useAuth();
 
  if (loading) return <Spinner />;
 
  return isAuthenticated
    ? <Button onClick={logout}>Sign out</Button>
    : null;
}

Route protection with withAuth

The withAuth HOC in src/hoc/withAuth.tsx guards any component from unauthenticated access:

typescript
// src/hoc/withAuth.tsx
const withAuth = (Component: React.ComponentType) => {
  return (): React.JSX.Element | null => {
    const { isAuthenticated, loading } = useAuth();
 
    if (loading)          return <Spinner />;
    if (!isAuthenticated) return <Navigate to='/auth/login-1' />;
 
    return <Component />;
  };
};

Apply it in the route definition via the Page wrapper:

typescript
// src/routes/demo-routes.tsx
{
  path: '/dashboards/misc',
  element: <Page Component={MiscPage} hoc={withAuth} />,
},

Auth pages

The following auth pages are included under the /auth prefix:

RouteComponentDescription
/auth/login-1Login1PageDefault login page (form + hero image)
/auth/login-2Login2PageAlternate login layout
/auth/signup-1Signup1PageRegistration page
/auth/signup-2Signup2PageAlternate registration layout
/auth/forgot-passwordForgotPasswordPagePassword reset request
/auth/reset-passwordResetPasswordPageNew password entry

Replacing the mock with a real backend

  1. Create your auth service

    Replace iAuthService inside AuthProvider.tsx with a real API call:

    typescript
    // src/components/AuthProvider/AuthProvider.tsx
    const loginUser = async (email: string, password: string) => {
      const response = await fetch('/api/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password }),
      });
     
      if (!response.ok) throw new Error('Invalid credentials');
     
      return response.json(); // expects { token: string, ... }
    };
  2. Store the token securely

    Replace the cookie storage with httpOnly cookies set by the server, or store the JWT in memory and refresh via a silent request. Update AuthProvider to read the token from its new location in the useEffect boot check.

  3. Add token to API requests

    Create an API client that attaches the authorization header to every request:

    typescript
    // src/services/apiClient.ts
    export async function apiFetch(url: string, options?: RequestInit) {
      const token = getCookieValue('auth-user')?.token;
      return fetch(url, {
        ...options,
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json',
          ...options?.headers,
        },
      });
    }
  4. Update the logout handler

    Call your server's logout endpoint to invalidate the session before clearing the cookie:

    typescript
    const logout = async () => {
      await fetch('/api/auth/logout', { method: 'POST' });
      eraseCookie('auth-user');
      setIsAuthenticated(false);
    };