Metronic NextJS
PurchasePreview
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 Prisma auth dependencies
  • Remove NextAuth backend 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:
"@auth/core": "^0.39.1",
"@auth/prisma-adapter": "^2.9.1",
"@next-auth/prisma-adapter": "^1.0.7",
"next-auth": "^4.24.11",
"bcrypt": "^6.0.0",
"nodemailer": "^7.0.3",
 
// Remove these from devDependencies:
"@prisma/client": "^6.9.0",
"@types/bcrypt": "^5.0.2",
"@types/nodemailer": "^6.4.17",
"prisma": "^6.9.0",

Remove Prisma scripts

Remove the following from your package.json:

// Remove this entire section:
"prisma": {
  "seed": "node prisma/seed.js",
  "setup": "node prisma/setup.js"
},

Install dependencies

Run the following command to update your dependencies:

npm install

Remove Prisma Files and Configuration

Delete Prisma directory

rm -rf prisma/

Remove Prisma client

Delete the Prisma client file:

rm -f lib/prisma.ts

Remove NextAuth API Routes

Delete NextAuth API directory

rm -rf app/api/auth/

Remove other auth API routes

rm -rf app/api/user-management/

Remove User Management Module

Delete user management pages

Remove the complex user management pages that depend on the database:

rm -rf app/(protected)/user-management/

This removes:

  • User listing and management pages
  • Role and permission management
  • User profile editing with complex fields
  • User deletion and restoration features
  • Account management with database integration

Update menu configuration (Optional)

If you want to remove user-management menu items, edit config/menu.config.tsx and remove or comment out the "User Management" sections:

// Remove or comment out this section:
/*
{
  title: 'User Management',
  icon: ShieldUser,
  children: [
    {
      title: 'Users',
      path: '/user-management/users',
    },
    {
      title: 'Roles',
      path: '/user-management/roles',
    },
    // ... other user management menu items
  ],
},
*/

Note: The menu references won't break the app - they'll just show as 404 pages if clicked.

Update Auth Provider

Create a simple auth context

Replace the content of providers/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;
  // Keep these for compatibility with existing UI
  data: { user: User | null } | null;
  status: 'loading' | 'authenticated' | 'unauthenticated';
}
 
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');
  };
 
  // Compatibility properties for existing UI components
  const data = user ? { user } : null;
  const status = isLoading ? 'loading' : user ? 'authenticated' : 'unauthenticated';
 
  return (
    <AuthContext.Provider value={{
      user,
      login,
      logout,
      isLoading,
      data,
      status
    }}>
      {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;
}
 
// For compatibility with NextAuth useSession hook
export function useSession() {
  const { data, status } = useAuth();
  return { data, status };
}
 
// Mock signIn function for compatibility
export async function signIn(provider: string, options?: Record<string, unknown>) {
  // Suppress unused parameter warning
  void options;
 
  if (provider === 'credentials') {
    // This will be handled by your login form
    return { error: null };
  }
  if (provider === 'google') {
    // Mock Google sign in - replace with your implementation
    console.log('Google sign in clicked - implement your Google auth here');
    return { error: null };
  }
  return { error: 'Provider not supported' };
}
 
// Mock signOut function for compatibility
export function signOut() {
  // For compatibility, we'll handle logout through the context directly
  const authUser = localStorage.getItem('auth-user');
  if (authUser) {
    localStorage.removeItem('auth-user');
    window.location.reload(); // Force a reload to update the auth state
  }
}

Update Protected Layout

Update the protected layout

Replace the content of app/(protected)/layout.tsx:

'use client';
 
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useSession } from '@/providers/auth-provider';
import { ScreenLoader } from '@/components/common/screen-loader';
import { Demo1Layout } from '../components/layouts/demo1/layout';
 
export default function ProtectedLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const { data: session, status } = useSession();
  const router = useRouter();
 
  useEffect(() => {
    if (status === 'unauthenticated') {
      router.push('/signin');
    }
  }, [status, router]);
 
  if (status === 'loading') {
    return <ScreenLoader />;
  }
 
  return session ? <Demo1Layout>{children}</Demo1Layout> : null;
}

Update Sign In Page

Update only the auth logic in signin page

Replace the onSubmit function in app/(auth)/signin/page.tsx:

// Find the onSubmit function and replace it with this:
async function onSubmit(values: SigninSchemaType) {
  setIsProcessing(true);
  setError(null);
 
  try {
    // Use your custom auth instead of NextAuth
    const { login } = useAuth();
    const success = await login(values.email, values.password);
 
    if (success) {
      router.push('/');
    } else {
      setError('Invalid credentials. Please try again.');
    }
  } catch (err) {
    setError(
      err instanceof Error
        ? err.message
        : 'An unexpected error occurred. Please try again.',
    );
  } finally {
    setIsProcessing(false);
  }
}

And add the import at the top:

// Add this import at the top of the file
import { useAuth } from '@/providers/auth-provider';

Update User Model

Create simplified user model

Replace the content of app/models/user.ts (or create it if it doesn't exist):

// Simple user types for UI display only
export interface User {
  id: string;
  email: string;
  name: string;
  avatar?: string;
}
 
// Mock data for development
export const MOCK_USER: User = {
  id: '1',
  email: 'demo@kt.com',
  name: 'Demo User',
  avatar: '/media/avatars/300-2.png'
};

This removes complex user properties like:

  • role, roleId, roleName - Complex role system
  • status, permissions - User status and permission management
  • createdAt, updatedAt, lastSignInAt - Database timestamps
  • emailVerifiedAt, isTrashed, isProtected - Database-specific fields

Remove Services (Optional)

rm -f services/send-email.ts
rm -f services/system-log.ts

These services were used for:

  • Email notifications (password reset, verification)
  • System logging and audit trails

Clean Up Environment Variables

Remove or comment out these variables from your .env.local:

# Remove these NextAuth variables:
# NEXTAUTH_URL=
# NEXTAUTH_SECRET=
# GOOGLE_CLIENT_ID=
# GOOGLE_CLIENT_SECRET=

# Remove these database variables:
# DATABASE_URL=
# DIRECT_URL=

Test Your Implementation

Start the development server

npm run dev

Test the mock login

  1. Navigate to /signin
  2. Use the demo credentials:
    • Email: demo@kt.com
    • Password: demo123
  3. Verify you can access protected routes
  4. Test the logout functionality
  5. All other auth pages should still display correctly

Connect to Your Backend (When Ready)

Update the login function

In providers/auth-provider.tsx, replace the mock login with your API call:

const login = async (email: string, password: string): Promise<boolean> => {
  setIsLoading(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) {
      const userData = await response.json();
      setUser(userData.user);
      localStorage.setItem('auth-user', JSON.stringify(userData.user));
      setIsLoading(false);
      return true;
    }
 
    setIsLoading(false);
    return false;
  } catch (error) {
    setIsLoading(false);
    return false;
  }
};

Add token management

For JWT tokens, update the auth provider:

// Add token management
const [token, setToken] = useState<string | null>(null);
 
// Update login function
const login = async (email: string, password: string): Promise<boolean> => {
  // ... API call
  if (response.ok) {
    const { user, token } = await response.json();
    setUser(user);
    setToken(token);
    localStorage.setItem('auth-user', JSON.stringify(user));
    localStorage.setItem('auth-token', token);
    return true;
  }
  // ...
};

Demo Credentials

For testing the mock authentication:

  • Email: demo@kt.com
  • Password: demo123

Summary

This guide removes only the backend auth dependencies while keeping all your existing UI screens intact. The key changes are:

  1. Removed Prisma and NextAuth dependencies
  2. Removed database and API routes
  3. Created a compatible auth provider
  4. Kept all existing auth pages (signin, signup, etc.)
  5. Minimal changes to existing components
  6. Mock auth system for immediate testing
  7. 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!