Skip to content

React SPA

A complete React SPA with cookie-based auth, protected routes, login page, and profile page.

  1. Install packages

    npm install authfort-client react-router-dom
    npm install authfort-client react-router-dom
  2. Create the auth client

    src/auth.ts
    import { createAuthClient } from 'authfort-client';
    
    export const auth = createAuthClient({
      baseUrl: '/auth',
      tokenMode: 'cookie',
    });
    import { createAuthClient } from 'authfort-client';
    
    export const auth = createAuthClient({
      baseUrl: '/auth',
      tokenMode: 'cookie',
    });
  3. Wrap your app with AuthProvider

    src/main.tsx
    import { AuthProvider } from 'authfort-client/react';
    import { BrowserRouter } from 'react-router-dom';
    import { auth } from './auth';
    import App from './App';
    
    auth.initialize();
    
    ReactDOM.createRoot(document.getElementById('root')!).render(
      <BrowserRouter>
        <AuthProvider client={auth}>
          <App />
        </AuthProvider>
      </BrowserRouter>
    );
    import { AuthProvider } from 'authfort-client/react';
    import { BrowserRouter } from 'react-router-dom';
    import { auth } from './auth';
    import App from './App';
    
    auth.initialize();
    
    ReactDOM.createRoot(document.getElementById('root')!).render(
      <BrowserRouter>
        <AuthProvider client={auth}>
          <App />
        </AuthProvider>
      </BrowserRouter>
    );
src/components/ProtectedRoute.tsx
import { useAuth } from 'authfort-client/react';
import { Navigate } from 'react-router-dom';

export function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { isAuthenticated, isLoading } = useAuth();

  if (isLoading) return <div>Loading...</div>;
  if (!isAuthenticated) return <Navigate to="/login" replace />;

  return children;
}
import { useAuth } from 'authfort-client/react';
import { Navigate } from 'react-router-dom';

export function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { isAuthenticated, isLoading } = useAuth();

  if (isLoading) return <div>Loading...</div>;
  if (!isAuthenticated) return <Navigate to="/login" replace />;

  return children;
}
src/pages/Login.tsx
import { useState } from 'react';
import { useAuth } from 'authfort-client/react';
import { useNavigate } from 'react-router-dom';

export function LoginPage() {
  const { client } = useAuth();
  const navigate = useNavigate();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError('');
    try {
      await client.signIn({ email, password });
      navigate('/');
    } catch (err: any) {
      setError(err.message);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <h1>Sign In</h1>
      <input
        value={email}
        onChange={e => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input
        type="password"
        value={password}
        onChange={e => setPassword(e.target.value)}
        placeholder="Password"
      />
      <button type="submit">Sign In</button>
      {error && <p style={{ color: 'red' }}>{error}</p>}

      <hr />
      <button type="button" onClick={() => client.signInWithProvider('google')}>
        Sign in with Google
      </button>
      <button type="button" onClick={() => client.signInWithProvider('github')}>
        Sign in with GitHub
      </button>
    </form>
  );
}
import { useState } from 'react';
import { useAuth } from 'authfort-client/react';
import { useNavigate } from 'react-router-dom';

export function LoginPage() {
  const { client } = useAuth();
  const navigate = useNavigate();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError('');
    try {
      await client.signIn({ email, password });
      navigate('/');
    } catch (err: any) {
      setError(err.message);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <h1>Sign In</h1>
      <input
        value={email}
        onChange={e => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input
        type="password"
        value={password}
        onChange={e => setPassword(e.target.value)}
        placeholder="Password"
      />
      <button type="submit">Sign In</button>
      {error && <p style={{ color: 'red' }}>{error}</p>}

      <hr />
      <button type="button" onClick={() => client.signInWithProvider('google')}>
        Sign in with Google
      </button>
      <button type="button" onClick={() => client.signInWithProvider('github')}>
        Sign in with GitHub
      </button>
    </form>
  );
}
src/pages/Profile.tsx
import { useAuth } from 'authfort-client/react';

export function ProfilePage() {
  const { user, client } = useAuth();

  return (
    <div>
      <h1>Profile</h1>
      <p>Email: {user?.email}</p>
      <p>Name: {user?.name}</p>
      <p>Roles: {user?.roles.join(', ') || 'none'}</p>
      <button onClick={() => client.signOut()}>Sign Out</button>
    </div>
  );
}
import { useAuth } from 'authfort-client/react';

export function ProfilePage() {
  const { user, client } = useAuth();

  return (
    <div>
      <h1>Profile</h1>
      <p>Email: {user?.email}</p>
      <p>Name: {user?.name}</p>
      <p>Roles: {user?.roles.join(', ') || 'none'}</p>
      <button onClick={() => client.signOut()}>Sign Out</button>
    </div>
  );
}
src/App.tsx
import { Routes, Route } from 'react-router-dom';
import { ProtectedRoute } from './components/ProtectedRoute';
import { LoginPage } from './pages/Login';
import { ProfilePage } from './pages/Profile';

export default function App() {
  return (
    <Routes>
      <Route path="/login" element={<LoginPage />} />
      <Route path="/" element={
        <ProtectedRoute>
          <ProfilePage />
        </ProtectedRoute>
      } />
    </Routes>
  );
}
import { Routes, Route } from 'react-router-dom';
import { ProtectedRoute } from './components/ProtectedRoute';
import { LoginPage } from './pages/Login';
import { ProfilePage } from './pages/Profile';

export default function App() {
  return (
    <Routes>
      <Route path="/login" element={<LoginPage />} />
      <Route path="/" element={
        <ProtectedRoute>
          <ProfilePage />
        </ProtectedRoute>
      } />
    </Routes>
  );
}
import { useAuth } from 'authfort-client/react';
import { useEffect, useState } from 'react';

function Orders() {
  const { client } = useAuth();
  const [orders, setOrders] = useState([]);

  useEffect(() => {
    client.fetch('/api/orders')
      .then(res => res.json())
      .then(setOrders);
  }, [client]);

  return (
    <ul>
      {orders.map((o: any) => <li key={o.id}>{o.name}</li>)}
    </ul>
  );
}
import { useAuth } from 'authfort-client/react';
import { useEffect, useState } from 'react';

function Orders() {
  const { client } = useAuth();
  const [orders, setOrders] = useState([]);

  useEffect(() => {
    client.fetch('/api/orders')
      .then(res => res.json())
      .then(setOrders);
  }, [client]);

  return (
    <ul>
      {orders.map((o: any) => <li key={o.id}>{o.name}</li>)}
    </ul>
  );
}