OAuth Authentication System
The Everybody Eats Volunteer Portal implements a comprehensive OAuth authentication system using NextAuth.js, supporting multiple providers alongside traditional credential-based authentication.
Architecture Overview
Section titled “Architecture Overview”Authentication Stack
Section titled “Authentication Stack”- NextAuth.js v4: Session management and OAuth provider integration
- JWT Strategy: Stateless session handling with encrypted tokens
- Multi-Provider Support: Google, Facebook, and Apple OAuth providers
- Profile Completion Flow: Post-OAuth user onboarding for volunteer-specific data
Core Components
Section titled “Core Components”| Component | Location | Purpose |
|---|---|---|
auth-options.ts | /src/lib/auth-options.ts | NextAuth configuration and provider setup |
login/page.tsx | /src/app/login/page.tsx | Authentication UI with OAuth buttons |
[...nextauth].ts | /src/app/api/auth/[...nextauth]/route.ts | NextAuth API route handler |
OAuth Provider Configuration
Section titled “OAuth Provider Configuration”Supported Providers
Section titled “Supported Providers”The system currently supports three OAuth providers with customized configurations:
Google OAuth
Section titled “Google OAuth”GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, profile(profile) { return { id: profile.sub, name: profile.name, email: profile.email, // Request higher quality image (400x400) image: profile.picture?.replace(/=s\d+-c$/, "=s400-c") || profile.picture, }; },})Facebook OAuth
Section titled “Facebook OAuth”FacebookProvider({ clientId: process.env.FACEBOOK_CLIENT_ID!, clientSecret: process.env.FACEBOOK_CLIENT_SECRET!, // Request highest quality profile picture (800x800) profileUrl: "https://graph.facebook.com/me?fields=id,name,email,picture.width(800).height(800)", profile(profile: FacebookProfile) { return { id: profile.id, name: profile.name, email: profile.email, image: profile.picture?.data?.url, }; },})Apple OAuth
Section titled “Apple OAuth”⚠️ Requires paid Apple Developer Account ($99/year USD)
// Apple provider configuration in environment variables// Requires more complex setup with certificates and JWT generationAPPLE_ID="your-apple-app-id"APPLE_SECRET="your-apple-secret" // Generated JWT tokenCost Considerations:
- Apple Developer Program membership required: $99/year USD
- Both Individual and Organization memberships cost the same
- Free alternatives: Google and Facebook OAuth are free to implement
Required Environment Variables
Section titled “Required Environment Variables”# NextAuth CoreNEXTAUTH_URL="http://localhost:3000"NEXTAUTH_SECRET="your-secret-here"
# Google OAuthGOOGLE_CLIENT_ID="your-google-client-id"GOOGLE_CLIENT_SECRET="your-google-client-secret"
# Facebook OAuthFACEBOOK_CLIENT_ID="your-facebook-client-id"FACEBOOK_CLIENT_SECRET="your-facebook-client-secret"
# Apple OAuthAPPLE_ID="your-apple-app-id"APPLE_SECRET="your-apple-secret"Authentication Flow
Section titled “Authentication Flow”OAuth Sign-In Process
Section titled “OAuth Sign-In Process”sequenceDiagram
participant U as User
participant L as Login Page
participant N as NextAuth
participant P as OAuth Provider
participant D as Database
participant S as Session
U->>L: Click OAuth Provider Button
L->>N: signIn(providerId)
N->>P: Redirect to OAuth Provider
P->>U: Present Login/Consent Screen
U->>P: Authenticate & Authorize
P->>N: Redirect with Authorization Code
N->>P: Exchange Code for User Profile
P->>N: Return User Profile Data
N->>D: Create/Update User Record
D->>N: User Data with Defaults
N->>S: Create JWT Session
S->>U: Redirect to Profile Completion
Step-by-Step Flow
Section titled “Step-by-Step Flow”- Provider Selection: User clicks OAuth provider button in login UI
- OAuth Redirect: NextAuth redirects to provider’s authorization server
- Provider Authentication: User authenticates with OAuth provider
- Callback Processing: Provider redirects back with authorization code
- User Creation/Update: System creates or updates user in database
- Session Establishment: JWT token created with user data
- Profile Completion: OAuth users redirected to complete volunteer profile
Database User Creation
Section titled “Database User Creation”When an OAuth user signs in for the first time:
// New user creation in signIn callbackconst existingUser = await prisma.user.create({ data: { email: user.email, name: user.name || "", firstName: nameParts[0] || "", lastName: nameParts.slice(1).join(" ") || "", role: "VOLUNTEER", // Default role for OAuth users profilePhotoUrl: user.image || null, hashedPassword: "", // OAuth users don't need passwords volunteerAgreementAccepted: false, // Must complete profile healthSafetyPolicyAccepted: false, },});Profile Completion Flow
Section titled “Profile Completion Flow”OAuth users are automatically redirected to /profile/edit?oauth=true to complete:
- Emergency contact information
- Medical conditions and dietary restrictions
- Availability preferences
- Policy agreement acceptance
Session Management
Section titled “Session Management”JWT Token Structure
Section titled “JWT Token Structure”The JWT token includes extended user information:
interface JWT { role?: "ADMIN" | "VOLUNTEER"; phone?: string | null; firstName?: string | null; lastName?: string | null;}Session Object
Section titled “Session Object”Client-side session includes:
interface Session { user: { id?: string; email?: string; name?: string; image?: string; role?: "ADMIN" | "VOLUNTEER"; phone?: string; firstName?: string; lastName?: string; }}Session Updates
Section titled “Session Updates”Profile changes trigger session updates using the update trigger:
// In profile update componentsimport { useSession } from "next-auth/react";const { update } = useSession();
// After profile updateawait update(); // Refreshes session with latest database dataFrontend Implementation
Section titled “Frontend Implementation”OAuth Button Rendering
Section titled “OAuth Button Rendering”The login page dynamically renders OAuth provider buttons:
// Get available providersconst providers = await getProviders();const oauthProviders = Object.values(providers).filter( (provider) => provider.type === "oauth");
// Handle OAuth sign-inasync function handleOAuthSignIn(providerId: string) { await signIn(providerId, { callbackUrl: "/dashboard", });}Provider-Specific Styling
Section titled “Provider-Specific Styling”Each OAuth provider has custom styling and icons:
const getProviderButtonStyle = (providerId: string) => { switch (providerId) { case "google": return "bg-white hover:bg-gray-50 text-gray-900 border border-gray-300"; case "facebook": return "bg-[#1877F2] hover:bg-[#166FE5] text-white"; case "apple": return "bg-black hover:bg-gray-900 text-white"; }};Security Considerations
Section titled “Security Considerations”CSRF Protection
Section titled “CSRF Protection”NextAuth.js provides built-in CSRF protection for all authentication flows.
Secure Callbacks
Section titled “Secure Callbacks”OAuth callback URLs are validated and must match configured domains:
// Developmenthttp://localhost:3000/api/auth/callback/[provider]
// Productionhttps://yourdomain.com/api/auth/callback/[provider]Profile Photo Security
Section titled “Profile Photo Security”OAuth profile photos are stored as URLs, not downloaded files, reducing security risks while providing high-quality images.
Error Handling
Section titled “Error Handling”OAuth Flow Errors
Section titled “OAuth Flow Errors”try { await signIn(providerId, { callbackUrl: "/dashboard" });} catch (error) { console.error("OAuth sign in error:", error); setError("Authentication failed. Please try again.");}Database Errors
Section titled “Database Errors”OAuth sign-in callback includes comprehensive error handling:
try { // User creation/update logic} catch (error) { console.error("Error handling OAuth sign-in:", error); return false; // Prevents sign-in}Testing OAuth Integration
Section titled “Testing OAuth Integration”Local Development
Section titled “Local Development”- Configure OAuth providers with localhost callback URLs
- Set environment variables in
.env.local - Test each provider’s authentication flow
- Verify profile completion redirect works
End-to-End Testing
Section titled “End-to-End Testing”OAuth flows are tested using Playwright with provider-specific test accounts:
// Test OAuth button presenceawait expect(page.getByTestId('oauth-google-button')).toBeVisible();await expect(page.getByTestId('oauth-facebook-button')).toBeVisible();
// Test OAuth provider separation from credentialsawait expect(page.getByTestId('oauth-divider')).toBeVisible();Extending OAuth Providers
Section titled “Extending OAuth Providers”Adding New Providers
Section titled “Adding New Providers”- Install Provider: Add NextAuth provider package
npm install @auth/[provider-name]- Configure Provider: Add to
auth-options.ts
import ProviderName from "next-auth/providers/provider-name";
providers: [ ProviderName({ clientId: process.env.PROVIDER_CLIENT_ID!, clientSecret: process.env.PROVIDER_CLIENT_SECRET!, }), // ... existing providers]- Update UI: Add provider button to login page
- Add Environment Variables: Include in
.env.localand deployment - Configure Callback URLs: Set in provider dashboard
Custom Profile Mapping
Section titled “Custom Profile Mapping”Override the default profile mapping to extract additional data:
ProviderName({ // ... config profile(profile) { return { id: profile.id, name: profile.display_name, email: profile.email, image: profile.avatar_url, // Custom fields customField: profile.custom_data, }; },})Troubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”| Issue | Solution |
|---|---|
| ”Invalid redirect URI” | Verify callback URLs match provider configuration exactly |
| ”App not in development mode” | Some providers require app review for production |
| ”OAuth provider not appearing” | Check environment variables and provider configuration |
| ”Profile completion not triggering” | Verify volunteerAgreementAccepted is false for OAuth users |
| ”Apple OAuth setup failing” | Ensure you have a paid Apple Developer account ($99/year) and proper certificates |
Debug Mode
Section titled “Debug Mode”Enable NextAuth debugging in development:
NEXTAUTH_DEBUG=true npm run devProvider-Specific Testing
Section titled “Provider-Specific Testing”Test each provider independently by temporarily disabling others in the configuration.
This OAuth authentication system provides a secure, user-friendly way for volunteers to join the platform while maintaining the flexibility to add additional providers or customize the authentication flow as needed.