React SSO Integration

Production-ready React application with FusionAuth Single Sign-On, featuring TypeScript, modern hooks, and enterprise authentication patterns

Overview

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.

⚛️ Modern React

Built with React 18+, TypeScript, and modern hooks for clean, maintainable code

🔐 Enterprise SSO

Complete SSO implementation with PKCE, silent refresh, and cross-domain authentication

🎨 Beautiful UI

Responsive design with loading states, error handling, and smooth transitions

Quick Start

Terminal
# 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

Core Implementation

🚀 FusionAuth React SDK

This example uses the official @fusionauth/react-sdk for streamlined integration

App.js
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>
  );
}

Environment Configuration

.env.local
# 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

Advanced Features

Protected Routes with React Router

ProtectedRoute.tsx
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}</>;
}

Key Features

Next Steps