Skip to main content

Overview

The LSAT Training Platform uses NextAuth.js for authentication, integrated with Firebase for user management and data storage. The authentication system supports multiple providers and uses JWT-based sessions.

Authentication Flow

The platform implements a hybrid authentication approach:
  1. NextAuth.js handles the authentication logic and session management
  2. Firebase Authentication manages credential validation
  3. Firestore stores user profile data
  4. JWT tokens maintain session state

Supported Authentication Methods

1. Google OAuth

Users can sign in using their Google account through OAuth 2.0.
import { signIn } from 'next-auth/react';

// Initiate Google sign-in
await signIn('google', { 
  callbackUrl: '/dashboard' 
});

2. Email/Password Credentials

Traditional email and password authentication backed by Firebase.
import { signIn } from 'next-auth/react';

const result = await signIn('credentials', {
  email: '[email protected]',
  password: 'securepassword',
  redirect: false
});

if (result?.error) {
  console.error('Authentication failed:', result.error);
}

Session Management

JWT Strategy

The platform uses JWT (JSON Web Token) strategy for session management:
  • Token Storage: HTTP-only cookies (secure)
  • Token Lifetime: Configurable via NextAuth
  • User Data: Fetched from Firestore on each session validation

Session Object Structure

interface Session {
  user: {
    id: string;          // Firebase UID
    email: string;       // User email
    name: string;        // Display name
    // Additional fields from Firestore user document
  };
  expires: string;       // ISO timestamp
}

Accessing Current Session

import { useSession } from 'next-auth/react';

export default function Component() {
  const { data: session, status } = useSession();
  
  if (status === 'loading') {
    return <div>Loading...</div>;
  }
  
  if (status === 'authenticated') {
    return <div>Welcome, {session.user.name}!</div>;
  }
  
  return <div>Please sign in</div>;
}

Firebase Integration

Client-Side Firebase

The platform initializes Firebase on the client for authentication operations:
// Initialized services
import { auth, db, storage } from '@/lib/firebase';

// Available services:
// - auth: Firebase Authentication
// - db: Firestore Database
// - storage: Firebase Storage

Server-Side Firebase Admin

Firebase Admin SDK is used for server-side operations:
import { db } from '@/lib/firebaseAdmin';

// Server-side Firestore access with admin privileges
const userDoc = await db.collection('users').doc(userId).get();

Required Environment Variables

NEXT_PUBLIC_FIREBASE_API_KEY
string
required
Firebase project API key (client-side)
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN
string
required
Firebase authentication domain (client-side)
NEXT_PUBLIC_FIREBASE_PROJECT_ID
string
required
Firebase project ID (client-side)
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET
string
required
Firebase storage bucket (client-side)
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID
string
required
Firebase messaging sender ID (client-side)
NEXT_PUBLIC_FIREBASE_APP_ID
string
required
Firebase app ID (client-side)
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID
string
Firebase Analytics measurement ID (client-side, optional)
FIREBASE_CLIENT_EMAIL
string
required
Firebase service account email (server-side)
FIREBASE_PRIVATE_KEY
string
required
Firebase service account private key (server-side)
NEXTAUTH_SECRET
string
required
Secret key for NextAuth JWT encryption
GOOGLE_CLIENT_ID
string
required
Google OAuth client ID
GOOGLE_CLIENT_SECRET
string
required
Google OAuth client secret

User Data Storage

Firestore User Document

When a user authenticates, their data is stored in Firestore:
// Collection: users
// Document ID: Firebase UID
{
  id: "firebase_uid_here",
  email: "[email protected]",
  name: "User Name"
}

Data Synchronization

  • On Sign-In: User data is created/updated in Firestore
  • On Session Load: User data is fetched from Firestore and attached to session
  • On Profile Update: Changes are persisted to Firestore

Security Best Practices

The NEXTAUTH_SECRET should be a strong, randomly generated string:
# Generate a secure secret
openssl rand -base64 32
Never commit this secret to version control.
The FIREBASE_PRIVATE_KEY contains newline characters that must be properly escaped:
// The code automatically handles this
privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, "\n")
Store the key with literal \n strings in your .env file.
  • NEXT_PUBLIC_: Exposed to the browser, use for client-side Firebase
  • No prefix: Server-only, use for sensitive credentials
Never prefix sensitive keys like FIREBASE_PRIVATE_KEY with NEXT_PUBLIC_.
Always validate sessions on protected routes:
const session = await getServerSession(authOptions);
if (!session) {
  return new Response('Unauthorized', { status: 401 });
}

Error Handling

Common Authentication Errors

Invalid email or password
error
Returned when credentials don’t match any Firebase user or the password is incorrect.
{
  error: "Invalid email or password"
}
Email and password are required
error
Returned when the sign-in request is missing required credentials.
{
  error: "Email and password are required"
}
Configuration Error
error
Occurs when required environment variables are missing or invalid.Check that all required Firebase and OAuth configuration is set.

Error Handling Example

import { signIn } from 'next-auth/react';

try {
  const result = await signIn('credentials', {
    email: email,
    password: password,
    redirect: false
  });
  
  if (result?.error) {
    // Handle authentication error
    if (result.error === 'Invalid email or password') {
      setError('Invalid credentials. Please try again.');
    } else {
      setError('An unexpected error occurred.');
    }
  } else if (result?.ok) {
    // Success - redirect to dashboard
    router.push('/dashboard');
  }
} catch (error) {
  console.error('Sign-in error:', error);
  setError('Failed to sign in. Please try again.');
}

Sign Out

To sign out a user and clear their session:
import { signOut } from 'next-auth/react';

// Sign out with redirect
await signOut({ callbackUrl: '/login' });

// Sign out without redirect
await signOut({ redirect: false });

Next Steps

NextAuth API

Detailed NextAuth endpoint documentation

User Profile

Managing user profiles and data

User Onboarding

Complete user onboarding workflow

Update User Data

Update user information and preferences

Build docs developers (and LLMs) love