Production-ready React application with FusionAuth Single Sign-On, featuring TypeScript, modern hooks, and enterprise authentication patterns
This example demonstrates a complete Single Sign-On (SSO) implementation using React 18+ with FusionAuth. Includes TypeScript support, modern React patterns, and production-ready authentication flows.
Built with React 18+, TypeScript, and modern hooks for clean, maintainable code
Complete SSO implementation with PKCE, silent refresh, and cross-domain authentication
Responsive design with loading states, error handling, and smooth transitions
# Clone the repository
git clone https://github.com/JustinArndtAI/AIgent.git
cd AIgent/example-apps/react-sso
# Install dependencies
npm install
# Configure environment
cp .env.example .env.local
# Edit .env.local with your FusionAuth configuration
# Start development server
npm run dev
# Build for production
npm run build
This example uses the official @fusionauth/react-sdk for streamlined integration
import React from 'react';
import { FusionAuthProvider, useAuth } from '@fusionauth/react-sdk';
import './App.css';
// Configure FusionAuth Provider
const fusionAuthConfig = {
clientId: process.env.REACT_APP_FUSIONAUTH_CLIENT_ID,
serverUrl: process.env.REACT_APP_FUSIONAUTH_URL,
redirectUri: window.location.origin + '/oauth-callback',
postLogoutRedirectUri: window.location.origin,
scope: 'openid profile email offline_access',
responseType: 'code',
shouldAutoRefresh: true,
autoRefreshSecondsBeforeExpiry: 60,
onRedirectSuccess: (state) => {
console.log('Login successful', state);
},
onRedirectFailure: (error) => {
console.error('Login failed', error);
}
};
// Protected Dashboard Component
function Dashboard({ user, onLogout }) {
return (
<div className="dashboard">
<div className="user-card">
<img
src={user.picture || '/default-avatar.png'}
alt={user.name}
className="user-avatar"
/>
<h2>Welcome, {user.name}!</h2>
<p className="user-email">{user.email}</p>
<div className="user-details">
<h3>Profile Information</h3>
<dl>
<dt>User ID:</dt>
<dd>{user.sub}</dd>
<dt>Roles:</dt>
<dd>{user.roles?.join(', ') || 'None'}</dd>
<dt>Last Login:</dt>
<dd>{new Date().toLocaleString()}</dd>
</dl>
</div>
<button onClick={onLogout} className="btn-logout">
Sign Out
</button>
</div>
</div>
);
}
// Login Page Component
function LoginPage({ onLogin }) {
return (
<div className="login-page">
<div className="login-card">
<h1>Welcome to FusionAuth SSO</h1>
<p>Sign in to access your account</p>
<button onClick={onLogin} className="btn-login">
<svg className="icon" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M18 8a6 6 0 01-7.743 5.743L10 14l-1 1-1 1H6v2H2v-4l4.257-4.257A6 6 0 1118 8zm-6-4a1 1 0 100 2 2 2 0 012 2 1 1 0 102 0 4 4 0 00-4-4z" clipRule="evenodd" />
</svg>
Sign in with FusionAuth
</button>
<div className="login-features">
<div className="feature">
<span className="feature-icon">🔒</span>
<span>Secure Authentication</span>
</div>
<div className="feature">
<span className="feature-icon">⚡</span>
<span>Single Sign-On</span>
</div>
<div className="feature">
<span className="feature-icon">🛡️</span>
<span>Enterprise Security</span>
</div>
</div>
</div>
</div>
);
}
// Main App Component
function App() {
const { user, login, logout, isAuthenticated, isLoading, error } = useAuth();
// Show loading state
if (isLoading) {
return (
<div className="loading-container">
<div className="spinner"></div>
<p>Authenticating...</p>
</div>
);
}
// Show error state
if (error) {
return (
<div className="error-container">
<h2>Authentication Error</h2>
<p>{error.message}</p>
<button onClick={() => window.location.reload()}>
Try Again
</button>
</div>
);
}
// Render based on authentication state
return (
<div className="app">
{isAuthenticated ? (
<Dashboard user={user} onLogout={logout} />
) : (
<LoginPage onLogin={login} />
)}
</div>
);
}
// Wrap App with FusionAuth Provider
export default function AppWithAuth() {
return (
<FusionAuthProvider {...fusionAuthConfig}>
<App />
</FusionAuthProvider>
);
}
# FusionAuth Configuration
REACT_APP_FUSIONAUTH_URL=http://localhost:9011
REACT_APP_FUSIONAUTH_CLIENT_ID=your-client-id-here
REACT_APP_FUSIONAUTH_TENANT_ID=your-tenant-id-here
# Optional: API Backend
REACT_APP_API_URL=http://localhost:3001/api
# Feature Flags
REACT_APP_ENABLE_MFA=true
REACT_APP_ENABLE_SOCIAL_LOGIN=true
import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from '@fusionauth/react-sdk';
interface ProtectedRouteProps {
children: React.ReactNode;
requiredRoles?: string[];
}
export function ProtectedRoute({ children, requiredRoles }: ProtectedRouteProps) {
const { isAuthenticated, user, isLoading } = useAuth();
const location = useLocation();
if (isLoading) {
return <LoadingSpinner />;
}
if (!isAuthenticated) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
if (requiredRoles && requiredRoles.length > 0) {
const hasRequiredRole = requiredRoles.some(role =>
user?.roles?.includes(role)
);
if (!hasRequiredRole) {
return <Navigate to="/unauthorized" replace />;
}
}
return <>{children}</>;
}