Custom Auth
Learn how to remove default auth dependencies.
This guide helps you quickly remove the default auth dependencies and start your own auth implementation.
Overview
This guide will help you:
- Remove Supabase and Auth0 auth dependencies
- Remove backend auth logic
- Keep all existing UI screens (login, signup, etc.)
- Set up a working mock auth system
- Maintain protected routes and layouts
- Keep everything functional for your custom implementation
Steps to Remove Auth Dependencies
Remove Auth Dependencies
Update package.json
Remove the following auth-related dependencies from your package.json
:
// Remove these from dependencies:
"@auth0/auth0-spa-js": "^2.2.0",
"@supabase/supabase-js": "^2.49.9",
Remove auth scripts
Remove the following auth-related scripts from your package.json
:
// Remove these scripts:
"create-demo-user": "tsx scripts/create-demo-user.js",
"debug-auth": "tsx scripts/debug-auth.js"
Install dependencies
Run the following command to update your dependencies:
npm install
Remove Auth Configuration Files
Remove Supabase files
rm -f src/lib/supabase.ts
rm -f src/components/supabase/SupabaseStatus.tsx
Remove auth adapters and providers
rm -f src/auth/adapters/supabase-adapter.ts
rm -f src/auth/providers/supabase-provider.tsx
Remove any auth config files
# Remove any .env variables related to Supabase
# VITE_SUPABASE_URL=
# VITE_SUPABASE_ANON_KEY=
Remove User Management Module
Remove user management pages
rm -rf src/pages/user-management/
rm -rf src/components/user-management/
Note: The Vite version may not have complex user management pages like the Next.js version, but if they exist, remove them to simplify the application.
Create Simple Auth Provider
Create a new auth provider
Create a new file src/auth/auth-provider.tsx
:
'use client';
import {
createContext,
useContext,
useState,
useEffect,
ReactNode,
} from 'react';
interface User {
id: string;
email: string;
name: string;
avatar?: string;
}
interface AuthContextType {
user: User | null;
login: (email: string, password: string) => Promise<boolean>;
logout: () => void;
isLoading: boolean;
isAuthenticated: boolean;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);
// Check for existing session on mount
useEffect(() => {
const savedUser = localStorage.getItem('auth-user');
if (savedUser) {
setUser(JSON.parse(savedUser));
}
setIsLoading(false);
}, []);
const login = async (email: string, password: string): Promise<boolean> => {
setIsLoading(true);
// Mock authentication - replace with your API call
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate API delay
if (email === 'demo@kt.com' && password === 'demo123') {
const mockUser: User = {
id: '1',
email: 'demo@kt.com',
name: 'Demo User',
avatar: '/media/avatars/300-2.png',
};
setUser(mockUser);
localStorage.setItem('auth-user', JSON.stringify(mockUser));
setIsLoading(false);
return true;
}
setIsLoading(false);
return false;
};
const logout = () => {
setUser(null);
localStorage.removeItem('auth-user');
};
const isAuthenticated = !!user;
return (
<AuthContext.Provider
value={{
user,
login,
logout,
isLoading,
isAuthenticated,
}}
>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
Update App.tsx to Use New Provider
Update the import in App.tsx
Find and update the import in src/App.tsx
:
// Before
import { AuthProvider } from './auth/providers/supabase-provider';
// After
import { AuthProvider } from './auth/auth-provider';
Create Auth Routing Components
Create auth routing file
Create src/auth/auth-routing.tsx
:
import { Navigate, Route, Routes } from 'react-router';
/**
* Handles all authentication related routes.
* This component is mounted at /auth/* in the main application router.
* Since we've simplified the auth system, we redirect all auth routes to the main dashboard.
*/
export function AuthRouting() {
return (
<Routes>
{/* Index route to redirect to dashboard */}
<Route index element={<Navigate to="/" replace />} />
{/* Catch all auth routes and redirect to dashboard */}
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
);
}
Create require auth component
Create src/auth/require-auth.tsx
:
import { useAuth } from './auth-provider';
import { ScreenLoader } from '@/components/common/screen-loader';
import { Outlet } from 'react-router';
/**
* Component to protect routes that require authentication.
* Since we've simplified the auth system, this just checks if user is logged in.
*/
export const RequireAuth = () => {
const { isLoading, isAuthenticated } = useAuth();
// Show screen loader while checking auth status
if (isLoading) {
return <ScreenLoader />;
}
// For demo purposes, we allow access even if not authenticated
// In a real app, you'd redirect to login page or show login prompt
return <Outlet />;
};
Keep All Auth Pages As-Is
No changes needed for auth pages
Keep all your existing auth pages - they will continue to work with the new auth provider:
- Login page - Uses the
useAuth()
hook - Signup page - Uses the
useAuth()
hook - Reset password page - Uses the
useAuth()
hook - Other auth pages - All continue to work
All forms will continue to work
Your existing auth forms will continue to work because they use the same useAuth()
hook interface.
Update Environment Variables
Remove auth-related environment variables
Remove or comment out these variables from your .env
:
# Remove these Supabase variables:
# VITE_SUPABASE_URL=
# VITE_SUPABASE_ANON_KEY=
# Remove these Auth0 variables:
# VITE_AUTH0_DOMAIN=
# VITE_AUTH0_CLIENT_ID=
Testing Your Implementation
Start the development server
npm run dev
Test the mock login
- Navigate to the login page
- Use the demo credentials:
- Email:
demo@kt.com
- Password:
demo123
- Email:
- Verify you can access protected routes
- Test the logout functionality
Connect to Your Backend (When Ready)
Update the login function
In src/auth/auth-provider.tsx
, replace the mock login with your API call:
const login = async (email: string, password: string) => {
setLoading(true);
try {
// Replace this with your actual API call
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('Login failed');
}
const data = await response.json();
const user = data.user;
setUser(user);
localStorage.setItem('auth-user', JSON.stringify(user));
setLoading(false);
return true;
} catch (error) {
setLoading(false);
throw error;
}
};
Update the register function
const register = async (
email: string,
password: string,
password_confirmation: string,
firstName?: string,
lastName?: string,
) => {
setLoading(true);
try {
const response = await fetch('/api/auth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
password,
password_confirmation,
first_name: firstName,
last_name: lastName,
}),
});
if (!response.ok) {
throw new Error('Registration failed');
}
const data = await response.json();
const user = data.user;
setUser(user);
localStorage.setItem('auth-user', JSON.stringify(user));
setLoading(false);
return true;
} catch (error) {
setLoading(false);
throw error;
}
};
Add API interceptors
Create an API client with automatic token handling in src/lib/api.ts
:
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000';
export const apiClient = {
async request(endpoint: string, options: RequestInit = {}) {
const auth = JSON.parse(localStorage.getItem('auth') || '{}');
const token = auth?.access_token;
const config: RequestInit = {
...options,
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
...options.headers,
},
};
const response = await fetch(`${API_BASE_URL}${endpoint}`, config);
if (response.status === 401) {
// Token expired, redirect to login
localStorage.removeItem('auth');
window.location.href = '/auth/signin';
return;
}
return response;
},
};
Optional Cleanup
Remove unused scripts
Remove any auth-related scripts from your package.json
:
// Remove these if they exist:
"create-demo-user": "tsx scripts/create-demo-user.js",
"debug-auth": "tsx scripts/debug-auth.js"
Remove script files
rm -f scripts/create-demo-user.js
rm -f scripts/debug-auth.js
Demo Credentials
For testing the mock authentication:
- Email:
demo@kt.com
- Password:
demo123
Troubleshooting
Common Issues
-
Build errors about missing Supabase types
- Remove any remaining Supabase imports
- Check for
@supabase/supabase-js
references
-
Protected routes not working
- Ensure the
AuthProvider
wraps your app inApp.tsx
- Check that
useAuth
is called within the provider
- Ensure the
-
Existing UI components breaking
- The auth context interface remains the same
- All existing auth pages should work without changes
-
TypeScript errors
- The auth models remain the same
- No type changes should be needed
Getting Help
If you encounter issues:
- Check the browser console for errors
- Verify all Supabase references are removed from imports
- Ensure your auth provider is properly configured
- Test with the provided demo credentials first
Summary
This guide removes only the backend auth dependencies while keeping all your existing UI screens intact. The key changes are:
- Removed Supabase and Auth0 dependencies
- Created a simple auth provider with mock authentication
- Kept all existing auth pages (signin, signup, etc.)
- No changes needed to existing auth forms
- Mock auth system for immediate testing
- Clear path to connect your own backend
Your existing auth UI screens will continue to work exactly as before, but now they're ready to connect to your custom backend implementation!