Metronic React
PreviewPurchase
Getting Started

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

  1. Navigate to the login page
  2. Use the demo credentials:
    • Email: demo@kt.com
    • Password: demo123
  3. Verify you can access protected routes
  4. 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

  1. Build errors about missing Supabase types

    • Remove any remaining Supabase imports
    • Check for @supabase/supabase-js references
  2. Protected routes not working

    • Ensure the AuthProvider wraps your app in App.tsx
    • Check that useAuth is called within the provider
  3. Existing UI components breaking

    • The auth context interface remains the same
    • All existing auth pages should work without changes
  4. TypeScript errors

    • The auth models remain the same
    • No type changes should be needed

Getting Help

If you encounter issues:

  1. Check the browser console for errors
  2. Verify all Supabase references are removed from imports
  3. Ensure your auth provider is properly configured
  4. 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:

  1. Removed Supabase and Auth0 dependencies
  2. Created a simple auth provider with mock authentication
  3. Kept all existing auth pages (signin, signup, etc.)
  4. No changes needed to existing auth forms
  5. Mock auth system for immediate testing
  6. 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!