A Next.js application with complete authentication flow using Supabase, including email verification and admin approval system.
✅ Email/Password Authentication - Secure user registration and login
✅ Email Verification - Built-in Supabase email verification
✅ Admin Approval System - Manual approval required for new users
✅ Role-Based Access - Admin and User roles with different permissions
✅ Protected Routes - Middleware-based route protection
✅ Row Level Security - Database-level security policies
✅ Modern UI - Clean, responsive design with dark mode support
✅ TypeScript - Fully typed for better developer experience
- Framework: Next.js 16 (App Router)
- Authentication: Supabase Auth
- Database: Supabase PostgreSQL
- Styling: Tailwind CSS 4.0
- Language: TypeScript
npm install- Create a free account at supabase.com
- Create a new project
- Get your project URL and anon key from Settings > API
Create .env.local in the root directory:
NEXT_PUBLIC_SUPABASE_URL=your-project-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-keyRun the following SQL in your Supabase SQL Editor:
-- Create user_profiles table
CREATE TABLE user_profiles (
user_id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
email TEXT UNIQUE NOT NULL,
is_approved BOOLEAN DEFAULT FALSE,
is_admin BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Enable Row Level Security
ALTER TABLE user_profiles ENABLE ROW LEVEL SECURITY;
-- Allow users to read their own profile
CREATE POLICY "Users can read own profile"
ON user_profiles FOR SELECT
USING (auth.uid() = user_id);
-- Allow admins to read all profiles
CREATE POLICY "Admins can read all profiles"
ON user_profiles FOR SELECT
USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE user_id = auth.uid() AND is_admin = TRUE
)
);
-- Allow admins to update profiles
CREATE POLICY "Admins can update profiles"
ON user_profiles FOR UPDATE
USING (
EXISTS (
SELECT 1 FROM user_profiles
WHERE user_id = auth.uid() AND is_admin = TRUE
)
);
-- Create function to auto-create profile on signup
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.user_profiles (user_id, email)
VALUES (NEW.id, NEW.email);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- Trigger to create profile on signup
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
-- Create himalayas_jobs table (optional - for job listings feature)
CREATE TABLE himalayas_jobs (
guid TEXT PRIMARY KEY,
title TEXT NOT NULL,
company_name TEXT NOT NULL,
company_logo TEXT,
employment_type TEXT,
min_salary NUMERIC,
max_salary NUMERIC,
currency TEXT,
pub_date TIMESTAMP WITH TIME ZONE,
expiry_date TIMESTAMP WITH TIME ZONE,
application_link TEXT,
job_url TEXT,
excerpt TEXT,
description_html TEXT,
locations TEXT[],
timezones INTEGER[],
categories TEXT[],
parent_categories TEXT[],
seniority TEXT[],
is_us BOOLEAN,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Enable RLS for jobs table
ALTER TABLE himalayas_jobs ENABLE ROW LEVEL SECURITY;
-- Allow all authenticated users to read jobs
CREATE POLICY "Authenticated users can read jobs"
ON himalayas_jobs FOR SELECT
TO authenticated
USING (TRUE);npm run devVisit http://localhost:3000
- Sign up through the app
- Verify your email
- In Supabase SQL Editor, run:
UPDATE user_profiles
SET is_admin = TRUE, is_approved = TRUE
WHERE email = 'your-email@example.com';job-hub/
├── app/
│ ├── sign-up/ # User registration page
│ ├── sign-in/ # User login page
│ ├── verify-email/ # Email verification status
│ ├── pending-approval/ # Admin approval waiting page
│ ├── dashboard/ # Main dashboard (authenticated users)
│ ├── admin/ # Admin panel (admins only)
│ └── auth/callback/ # OAuth callback handler
├── components/
│ ├── Dashboard.tsx # Dashboard component
│ └── AdminDashboard.tsx # Admin dashboard component
├── lib/
│ ├── supabase/
│ │ ├── client.ts # Client-side Supabase client
│ │ ├── server.ts # Server-side Supabase client
│ │ └── middleware.ts # Supabase middleware utilities
│ └── types/
│ └── database.types.ts # Database type definitions
└── middleware.ts # Next.js middleware for route protection
- User signs up with email/password →
/sign-up - Email verification link sent →
/verify-email - User clicks verification link → redirected back
- Account pending admin approval →
/pending-approval - Admin approves account → user can access
/dashboard
- Admin signs in → automatically redirected to
/dashboard - Admin can access
/adminpanel - Admin can approve/revoke user access
- Admin has full platform access
| Route | Description | Access |
|---|---|---|
/ |
Landing page | Public |
/sign-up |
User registration | Public |
/sign-in |
User login | Public |
/verify-email |
Email verification status | Authenticated |
/pending-approval |
Waiting for admin approval | Authenticated |
/dashboard |
Main dashboard | Approved users |
/admin |
Admin panel | Admins only |
- Row Level Security (RLS): Database-level access control
- Email Verification: Ensures valid email addresses
- Admin Approval: Manual approval prevents unauthorized access
- Middleware Protection: Server-side route guarding
- Secure Cookies: HTTP-only cookies for session management
- Password Hashing: Supabase handles secure password storage
npm run dev # Start development server
npm run build # Build for production
npm run start # Start production server
npm run lint # Run ESLint
npm run lint:fix # Fix ESLint errors
npm run format # Format code with Prettier
npm run type-check # Check TypeScript types| Variable | Description | Required |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Your Supabase project URL | Yes |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Your Supabase anon/public key | Yes |
| Column | Type | Description |
|---|---|---|
user_id |
UUID | Primary key, references auth.users |
email |
TEXT | User email (unique) |
is_approved |
BOOLEAN | Admin approval status |
is_admin |
BOOLEAN | Admin role flag |
created_at |
TIMESTAMP | Account creation time |
updated_at |
TIMESTAMP | Last update time |
- Check Supabase email settings
- Verify SMTP configuration
- Free tier has email limits
- Ensure you ran the SQL to set
is_admin = TRUE - Sign out and sign back in
- Admin needs to approve at
/admin - Check database for approval status
- Push your code to GitHub
- Import project to Vercel
- Add environment variables
- Update Supabase Site URL and Redirect URLs
- Deploy
- Build the project:
npm run build - Set environment variables
- Update Supabase settings with production URL
- Deploy
Contributions are welcome! Please feel free to submit a Pull Request.
MIT License
If you encounter any issues:
- Check that your Supabase environment variables are correct
- Verify database tables and policies are created
- Check browser console and terminal logs for errors
- Ensure your Supabase project is active and not paused
Built with ❤️ using Next.js and Supabase