A standalone React application providing a secure, beautiful login interface for OAuth 2.0 authentication flows with Cerberus IAM.
Features • Installation • Usage • API Documentation • Deployment
- Overview
- Features
- Architecture
- Prerequisites
- Installation
- Configuration
- Development
- Project Structure
- Usage
- API Integration
- CORS Configuration
- Security
- Building for Production
- Deployment
- Troubleshooting
- Contributing
- Technologies
- License
The Cerberus IAM Authentication UI is a purpose-built React application that serves as the frontend authentication layer for the Cerberus Identity and Access Management system. It provides a secure, user-friendly login interface that seamlessly integrates with OAuth 2.0 authorization flows.
Traditional IAM systems often couple authentication UI with the backend, making it difficult to customize and maintain. This application separates concerns by:
- Decoupling: Independent frontend that can be hosted separately from your API
- Flexibility: Easy to customize and brand without touching backend code
- Security: Built-in protections against common vulnerabilities (CSRF, XSS)
- Modern UX: Beautiful, responsive interface using modern design patterns
Unlike standard login pages, this application cannot be accessed directly. It's designed specifically for OAuth flows and requires valid OAuth parameters, providing an additional layer of security by preventing unauthorized access attempts.
- OAuth 2.0 with PKCE Support: Industry-standard authorization framework
- CSRF Protection: Automatic token fetching and validation
- Protected Routes: Login page requires OAuth redirect parameters
- Session-Based Auth: Secure cookie-based authentication with HttpOnly flags
- XSS Prevention: React's built-in protection + TypeScript type safety
- Beautiful UI: Clean, modern design using shadcn/ui components
- Responsive Design: Works seamlessly on desktop, tablet, and mobile
- Loading States: Clear visual feedback during authentication
- Error Handling: User-friendly error messages
- Accessibility: WCAG 2.1 compliant components
- Fast Load Times: Vite-powered build with optimized chunks
- Minimal Bundle Size: Tree-shaking and code splitting
- Lazy Loading: Components loaded on demand
- Optimized Assets: Compressed CSS and JavaScript
- TypeScript: Full type safety throughout the application
- Hot Module Replacement: Instant updates during development
- ESLint Integration: Code quality and consistency
- Component Library: Reusable shadcn/ui components
- Well-Documented: Comprehensive inline comments and documentation
This application operates as part of a three-component OAuth 2.0 ecosystem:
┌─────────────────────┐
│ Client App │ 1. User initiates login
│ (Your Laravel) │──────────────────────────┐
└─────────────────────┘ │
▲ │
│ ▼
│ 7. Returns ┌─────────────────────┐
│ authenticated │ Cerberus IAM API │
│ │ (OAuth Provider) │
│ └─────────────────────┘
│ │
│ │ 2. Redirects to
│ │ login UI
│ ▼
│ ┌─────────────────────┐
│ │ This App │
│ │ (Login Frontend) │
│ └─────────────────────┘
│ │
└───────────────────────────────────────┘
6. OAuth callback with code
User Action │ Client App │ Cerberus API │ This App
─────────────────────┼─────────────────────────┼────────────────────────┼──────────────────
1. Visit /login │ │ │
│ Redirect to OAuth │ │
2. │ ─────────────────────> │ │
│ │ Check authentication │
3. │ │ ─────────────────────> │
│ │ │ Show login form
4. Enter credentials │ │ │
│ │ <───── POST /login ────│
5. │ │ Authenticate user │
│ │ ─────────────────────> │
6. │ │ Redirect to OAuth │
│ <──── Callback ─────────│ │
7. Authenticated! │ │ │
Before you begin, ensure you have the following installed:
- Node.js: Version 18.x or higher (Download)
- npm: Version 9.x or higher (comes with Node.js)
- Cerberus IAM API: Running and accessible
- Git: For version control (optional but recommended)
- Operating System: macOS, Linux, or Windows with WSL2
- Memory: Minimum 4GB RAM
- Disk Space: At least 500MB free space
cd cerberus-auth-uinpm installThis will install all required packages including:
- React and React DOM
- React Router for routing
- Axios for HTTP requests
- Tailwind CSS for styling
- shadcn/ui components
- TypeScript and type definitions
npm run devIf successful, you'll see:
VITE v7.2.1 ready in 123 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
Create a .env.local file in the root directory:
cp .env.example .env.localEdit .env.local:
# Cerberus IAM API Base URL
# This is where your Cerberus IAM API is hosted
VITE_API_URL=https://api.cerberus-iam.com
# Optional: For local development, you might use:
# VITE_API_URL=http://localhost:8001Development (.env.local):
VITE_API_URL=http://localhost:8001Staging (.env.staging):
VITE_API_URL=https://staging-api.cerberus-iam.comProduction (set in hosting platform):
VITE_API_URL=https://api.cerberus-iam.com- ✅ Never commit
.env.localto version control - ✅ Use different API URLs for different environments
- ✅ Always use HTTPS in production
- ❌ Don't hardcode API URLs in components
- ❌ Don't expose sensitive data in environment variables
npm run devThe application will be available at http://localhost:5173
- Hot Module Replacement (HMR): Changes reflect instantly without page reload
- Fast Refresh: Preserves component state during edits
- Source Maps: Easy debugging with original source code
- Error Overlay: Clear error messages in the browser
Linting:
npm run lintType Checking:
npm run type-check- VSCode: Recommended IDE
- Extensions:
- ESLint
- Prettier
- Tailwind CSS IntelliSense
- TypeScript Vue Plugin (Volar)
cerberus-auth-ui/
│
├── src/ # Source code
│ ├── components/ # React components
│ │ ├── ui/ # shadcn/ui components
│ │ │ ├── alert.tsx # Alert component
│ │ │ ├── button.tsx # Button component
│ │ │ ├── card.tsx # Card components
│ │ │ ├── input.tsx # Input component
│ │ │ └── label.tsx # Label component
│ │ └── ProtectedRoute.tsx # Route protection HOC
│ │
│ ├── context/ # React context providers
│ │ └── AuthContext.tsx # Authentication state management
│ │
│ ├── lib/ # Utility libraries
│ │ ├── api.ts # Axios instance & interceptors
│ │ └── utils.ts # Utility functions (cn, etc.)
│ │
│ ├── pages/ # Page components
│ │ ├── Login.tsx # Login page
│ │ └── Unauthorized.tsx # Unauthorized access page
│ │
│ ├── App.tsx # Root application component
│ ├── main.tsx # Application entry point
│ └── index.css # Global styles & Tailwind imports
│
├── public/ # Static assets
│ └── vite.svg # Vite logo
│
├── dist/ # Production build (generated)
│
├── .env.example # Environment variables template
├── .env.local # Local environment (gitignored)
├── .gitignore # Git ignore rules
├── eslint.config.js # ESLint configuration
├── index.html # HTML entry point
├── package.json # Dependencies & scripts
├── postcss.config.js # PostCSS configuration
├── tailwind.config.js # Tailwind CSS configuration
├── tsconfig.json # TypeScript configuration
├── tsconfig.app.json # App-specific TypeScript config
├── tsconfig.node.json # Node-specific TypeScript config
├── vite.config.ts # Vite build configuration
├── README.md # This file
└── SETUP.md # Quick setup guide
src/context/AuthContext.tsx
- Manages authentication state globally
- Provides login and auth check functions
- Handles OAuth redirect logic
src/lib/api.ts
- Configures Axios with base URL and credentials
- Implements request interceptor for CSRF tokens
- Centralized API error handling
src/components/ProtectedRoute.tsx
- Higher-order component for route protection
- Validates OAuth redirect_uri parameter
- Redirects unauthorized access attempts
src/pages/Login.tsx
- Main login interface
- Form validation and submission
- Loading and error states
Attempting to visit http://localhost:5173/login will redirect you to the "Unauthorized" page. This is by design.
The application must be accessed through an OAuth authorization flow:
http://localhost:5173/login?redirect_uri=/oauth2/authorize?client_id=xxx&...
User visits your client application:
http://localhost:8000/login
Your Laravel app redirects to:
https://api.cerberus-iam.com/oauth2/authorize?
response_type=code&
client_id=client_abcd1234&
redirect_uri=http://localhost:8000/cerberus/callback&
scope=openid+profile+email&
state=random-state-token&
code_challenge=base64-challenge&
code_challenge_method=S256
If user is not authenticated, Cerberus redirects to this app:
http://localhost:5173/login?redirect_uri=/oauth2/authorize?...
User enters credentials in the login form
POSTs to Cerberus IAM API:
POST /api/auth/login
{
"email": "user@example.com",
"password": "password"
}After successful authentication, redirects to:
https://api.cerberus-iam.com/oauth2/authorize?...
Cerberus redirects back to client with authorization code:
http://localhost:8000/cerberus/callback?code=auth-code&state=...
Client exchanges code for access token, user is logged in!
This application expects your Cerberus IAM API to implement the following endpoints:
Authenticates a user with email and password.
Endpoint: /api/auth/login
Method: POST
Authentication: None (public)
Content-Type: application/json
Request Body:
{
"email": "user@example.com",
"password": "securePassword123"
}Success Response (200 OK):
{
"success": true,
"user": {
"id": "user_123456",
"email": "user@example.com",
"name": "John Doe",
"email_verified_at": "2025-01-15T10:30:00Z"
}
}Error Response (401 Unauthorized):
{
"success": false,
"message": "Invalid credentials"
}Error Response (422 Validation Error):
{
"success": false,
"message": "Validation failed",
"errors": {
"email": ["The email field is required."],
"password": ["The password field is required."]
}
}Checks if the current user is authenticated.
Endpoint: /api/auth/me
Method: GET
Authentication: Session cookie
Content-Type: application/json
Success Response (200 OK):
{
"authenticated": true,
"user": {
"id": "user_123456",
"email": "user@example.com",
"name": "John Doe",
"email_verified_at": "2025-01-15T10:30:00Z"
}
}Unauthenticated Response (401 Unauthorized):
{
"authenticated": false
}Initializes CSRF protection for the session.
Endpoint: /sanctum/csrf-cookie
Method: GET
Authentication: None
Response: No body, sets XSRF-TOKEN cookie
Success Response (204 No Content):
Headers:
Set-Cookie: XSRF-TOKEN=...; Path=/; Secure; HttpOnly
// routes/api.php
Route::post('/auth/login', function (Request $request) {
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return response()->json([
'success' => true,
'user' => Auth::user(),
]);
}
return response()->json([
'success' => false,
'message' => 'Invalid credentials',
], 401);
});
Route::get('/auth/me', function (Request $request) {
if (Auth::check()) {
return response()->json([
'authenticated' => true,
'user' => Auth::user(),
]);
}
return response()->json([
'authenticated' => false,
], 401);
});Cross-Origin Resource Sharing (CORS) must be configured on your Cerberus IAM API to allow this application to make requests.
Edit config/cors.php:
<?php
return [
'paths' => [
'api/*',
'sanctum/csrf-cookie',
'auth/*',
'oauth2/*',
],
'allowed_methods' => ['*'],
'allowed_origins' => [
'http://localhost:5173', // Development
'https://auth.cerberus-iam.com', // Production
'https://staging-auth.cerberus-iam.com', // Staging
],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true, // CRITICAL: Must be true
];supports_credentials: true: Required for session-based authenticationallowed_origins: Add all environments where this app is hostedpaths: Include all API endpoints this app will call
Edit .env on your Cerberus IAM API:
SESSION_DRIVER=cookie
SESSION_DOMAIN=.cerberus-iam.com # Note the leading dot
SESSION_SECURE_COOKIE=true # Use true in production
SESSION_SAME_SITE=none # Required for cross-domainTest CORS configuration:
curl -X OPTIONS http://api.cerberus-iam.com/api/auth/login \
-H "Origin: http://localhost:5173" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type" \
-vLook for these headers in the response:
Access-Control-Allow-Origin: http://localhost:5173
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POST, OPTIONS
- Automatically fetches CSRF tokens before state-changing requests
- Tokens are stored in HttpOnly cookies
- Validated on the server side
// Login page requires OAuth redirect_uri
<ProtectedRoute requireOAuthRedirect={true}>
<Login />
</ProtectedRoute>All API requests include credentials:
axios.defaults.withCredentials = true;- React's built-in XSS protection
- TypeScript type safety
- Input sanitization
- HttpOnly cookies prevent JavaScript access
- Secure flag ensures HTTPS-only transmission
- SameSite=None for cross-domain support
✅ DO:
- Always use HTTPS in production
- Rotate secrets regularly
- Implement rate limiting on API
- Use strong password policies
- Enable 2FA for privileged accounts
- Keep dependencies updated
- Monitor for security vulnerabilities
❌ DON'T:
- Hardcode secrets or API keys
- Disable CSRF protection
- Allow weak passwords
- Store sensitive data in localStorage
- Ignore security warnings
Add these headers in your hosting platform:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self' https://api.cerberus-iam.com;
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
npm run buildThis creates an optimized production build in the dist/ directory.
Output:
dist/
├── index.html
├── assets/
│ ├── index-[hash].js
│ └── index-[hash].css
└── vite.svg
The build process includes:
- Tree Shaking: Removes unused code
- Minification: Compresses JavaScript and CSS
- Code Splitting: Separates vendor and app code
- Asset Optimization: Compresses images and fonts
- Source Maps: Generated for debugging (optional)
npm run previewServes the production build locally at http://localhost:4173
Edit vite.config.ts to customize build settings:
export default defineConfig({
build: {
outDir: 'dist',
sourcemap: true, // Enable source maps
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom', 'react-router-dom'],
ui: ['lucide-react', 'axios'],
},
},
},
},
});- Install Vercel CLI:
npm i -g vercel- Login and Deploy:
vercel login
vercel --prod- Set Environment Variables:
vercel env add VITE_API_URL production
# Enter: https://api.cerberus-iam.com- Automatic Deployments:
- Connect your GitHub repository
- Vercel auto-deploys on push to main
- Install Netlify CLI:
npm i -g netlify-cli- Build and Deploy:
npm run build
netlify deploy --prod --dir=dist- Environment Variables:
- Go to Site settings > Environment variables
- Add
VITE_API_URL
- Build the Application:
npm run build-
Upload
dist/folder to your web server -
Configure Web Server:
Nginx:
server {
listen 80;
server_name auth.cerberus-iam.com;
root /var/www/cerberus-auth-ui/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# Security headers
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}Apache (.htaccess):
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
# Security headers
<IfModule mod_headers.c>
Header set X-Frame-Options "DENY"
Header set X-Content-Type-Options "nosniff"
Header set X-XSS-Protection "1; mode=block"
</IfModule>Dockerfile:
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]Build and Run:
docker build -t cerberus-auth-ui .
docker run -p 8080:80 -e VITE_API_URL=https://api.cerberus-iam.com cerberus-auth-uiCause: Accessing /login without OAuth redirect_uri parameter.
Solution: This is expected behavior. The login page must be accessed through an OAuth flow with a redirect_uri parameter.
Test:
http://localhost:5173/login?redirect_uri=/oauth2/authorize
Symptoms:
Access to XMLHttpRequest at 'https://api.cerberus-iam.com/api/auth/login'
from origin 'http://localhost:5173' has been blocked by CORS policy
Solution:
- Check
config/cors.phpon API includeshttp://localhost:5173 - Ensure
supports_credentialsistrue - Verify API is running and accessible
Possible Causes:
- Incorrect email/password
- API endpoint not implemented correctly
- Session not being created on API
Debug Steps:
# Test API directly
curl -X POST https://api.cerberus-iam.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"password"}'Cause: API response doesn't match expected format.
Solution: Ensure API returns:
{
"success": true,
"user": { ... }
}Solution:
# Clear cache and reinstall
rm -rf node_modules package-lock.json
npm install
# Rebuild
npm run buildEnable debug logging:
// src/lib/api.ts
api.interceptors.request.use((config) => {
console.log('Request:', config.method?.toUpperCase(), config.url);
return config;
});
api.interceptors.response.use(
(response) => {
console.log('Response:', response.status, response.data);
return response;
},
(error) => {
console.error('Error:', error.response?.status, error.response?.data);
return Promise.reject(error);
}
);- Create a Feature Branch:
git checkout -b feature/your-feature-name-
Make Changes: Follow the existing code style and patterns
-
Test Thoroughly:
npm run dev # Manual testing
npm run build # Ensure build works- Commit Changes:
git add .
git commit -m "feat: add your feature description"- Push and Create PR:
git push origin feature/your-feature-name- Use TypeScript for all new files
- Follow existing component patterns
- Add JSDoc comments for complex functions
- Use meaningful variable names
- Keep components small and focused
type(scope): subject
body
footer
Types: feat, fix, docs, style, refactor, test, chore
Examples:
feat(login): add remember me checkbox
fix(api): handle 500 errors gracefully
docs(readme): update CORS configuration section
- React 18: UI library with hooks and concurrent features
- TypeScript 5: Type-safe JavaScript
- Vite 7: Next-generation frontend tooling
- React Router 6: Declarative routing
- React Context API: Global state management
- Axios: Promise-based HTTP client
- Custom Interceptors: CSRF token management
- Tailwind CSS 4: Utility-first CSS framework
- shadcn/ui: Re-usable component library
- Lucide React: Beautiful icon library
- class-variance-authority: Component variants
- clsx: Conditional classnames
- tailwind-merge: Merge Tailwind classes
- ESLint: Code linting
- TypeScript ESLint: TypeScript-specific linting
- PostCSS: CSS transformation
- Autoprefixer: Vendor prefixes
Private - Cerberus IAM Project
This software is proprietary and confidential. Unauthorized copying, distribution, or use is strictly prohibited.
© 2025 Cerberus IAM. All rights reserved.
For questions, issues, or contributions:
- Documentation: See
SETUP.mdfor quick start guide - Issues: Create an issue in the repository
- Email: support@cerberus-iam.com (if applicable)
Built with ❤️ using React, TypeScript, and Tailwind CSS