Authentication
Complete guide to setting up authentication with Auth.js v5, GitHub OAuth, and Google OAuth.
Overview
ShipSecure uses Auth.js v5 (formerly NextAuth.js) for authentication. It comes pre-configured with GitHub and Google OAuth providers, ready to use once you add your credentials.
Quick Start
- Generate AUTH_SECRET
- Configure OAuth Providers (GitHub and/or Google)
- Set Environment Variables
- Test Authentication
Step 1: Generate AUTH_SECRET
The AUTH_SECRET is used to encrypt session tokens. Generate one using:
npx auth secret
Or manually:
openssl rand -base64 32
Copy the generated secret to your .env file:
AUTH_SECRET=your-generated-secret-here
Step 2: Configure GitHub OAuth
Create a GitHub OAuth App
- Go to GitHub Developer Settings
- Click "New OAuth App"
- Fill in the details:
- Application name: Your app name (e.g., "My SaaS")
- Homepage URL:
http://localhost:3000(for development) - Authorization callback URL:
http://localhost:3000/api/auth/callback/github
- Click "Register application"
- Copy the Client ID and generate a Client Secret
Add to Environment Variables
Add the credentials to your .env file:
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
Production Setup
Important: After deploying to production, update the callback URL in your GitHub OAuth App settings:
- Go to GitHub Developer Settings
- Click on your OAuth App
- Update Authorization callback URL to:
Replace
https://your-domain.com/api/auth/callback/githubyour-domain.comwith your actual production domain (e.g.,myapp.vercel.appormyapp.com)
Note: You can add multiple callback URLs (one for development, one for production) by clicking "Add another callback URL".
Step 3: Configure Google OAuth
Create a Google OAuth Client
-
Go to Google Cloud Console
-
Create a new project or select an existing one
-
Navigate to APIs & Services > Credentials
-
Click "Create Credentials" > "OAuth client ID"
-
Configure the consent screen if prompted:
- Choose "External" (unless you have a Google Workspace)
- Fill in app name, user support email, and developer contact
-
Create OAuth client:
- Application type: Web application
- Name: Your app name
- Authorized JavaScript origins:
http://localhost:3000(development)https://your-domain.com(production - add after deployment)
- Authorized redirect URIs:
http://localhost:3000/api/auth/callback/google(development)https://your-domain.com/api/auth/callback/google(production - add after deployment)
Important: Replace
your-domain.comwith your actual production domain. You can add multiple URIs by clicking the "+ Add URI" button. -
Click "Create"
-
Copy the Client ID and Client Secret
Add to Environment Variables
Add the credentials to your .env file:
AUTH_GOOGLE_ID=your_google_client_id
AUTH_GOOGLE_SECRET=your_google_client_secret
Step 4: Complete Environment Setup
Your .env.local file should now include all authentication variables:
# Authentication - Replace all placeholder values with your actual credentials
AUTH_SECRET=your-generated-secret-here
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
AUTH_GOOGLE_ID=your_google_client_id
AUTH_GOOGLE_SECRET=your_google_client_secret
# Database (required) - Replace with your PostgreSQL connection string
DATABASE_URL=your_postgresql_connection_string
Checklist:
AUTH_SECRETgenerated and addedGITHUB_CLIENT_IDandGITHUB_CLIENT_SECRETfrom GitHub OAuth AppAUTH_GOOGLE_IDandAUTH_GOOGLE_SECRETfrom Google Cloud Console (if using Google)DATABASE_URLconfigured (required for sessions to work)
Step 5: Test Authentication
- Start your development server (if not already running):
npm run dev
-
Navigate to the sign-in page:
http://localhost:3000/api/auth/signin -
Click on "Sign in with GitHub" or "Sign in with Google"
-
Complete the OAuth flow:
- You'll be redirected to GitHub/Google
- Authorize the app
- You'll be redirected back to your app
-
Success! You should now be logged in. Check the top-right corner for your user menu.
Troubleshooting: If sign-in fails, check:
- OAuth credentials are correct in
.env.local - Callback URLs match exactly (including
httpvshttps) - Database is initialized (
npx prisma db push)
How It Works
Auth.js v5 Configuration
The authentication is configured in src/lib/auth.ts:
export const { handlers, signIn, signOut, auth } = NextAuth({
adapter: PrismaAdapter(db),
providers: [
GitHub({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
}),
Google({
clientId: process.env.AUTH_GOOGLE_ID,
clientSecret: process.env.AUTH_GOOGLE_SECRET,
}),
],
// ... other config
});
Using Authentication in Your Code
Server Components
import { auth } from "@/lib/auth";
export default async function Page() {
// Get the current session (works in Server Components)
const session = await auth();
// Check if user is authenticated
if (!session) {
return <div>Please sign in to view this page</div>;
}
// User is authenticated - access user data
return <div>Hello {session.user?.name}!</div>;
}
When to use: Server Components are the default in Next.js App Router. Use this pattern for pages that need authentication.
Client Components
"use client";
import { useSession } from "next-auth/react";
export default function ClientPage() {
// Get session in Client Component (requires "use client" directive)
const { data: session, status } = useSession();
// Handle loading state
if (status === "loading") return <div>Loading...</div>;
// Handle unauthenticated state
if (!session) return <div>Please sign in</div>;
// User is authenticated
return <div>Hello {session.user?.name}!</div>;
}
When to use: Use this pattern in Client Components (components with interactivity, hooks, or browser APIs). Requires wrapping your app with SessionProvider (already configured in ShipSecure).
API Routes
import { auth } from "@/lib/auth";
import { NextResponse } from "next/server";
export async function GET() {
// Get session in API route
const session = await auth();
// Protect the route - return 401 if not authenticated
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// User is authenticated - return protected data
return NextResponse.json({ user: session.user });
}
When to use: Protect API endpoints that require authentication. Place this file in app/api/your-endpoint/route.ts.
Server Actions
"use server";
import { auth } from "@/lib/auth";
export async function myServerAction() {
// Get session in Server Action
const session = await auth();
// Protect the action - throw error if not authenticated
if (!session) {
throw new Error("Unauthorized");
}
// User is authenticated - perform protected operation
// Example: Update user data, create records, etc.
return { success: true, userId: session.user.id };
}
When to use: Protect Server Actions that modify data or perform sensitive operations. Call these from Client Components using form actions or button onClick handlers.
Security Features
ShipSecure's authentication includes:
- CSRF Protection: Built into Auth.js v5
- Secure Sessions: HTTP-only cookies
- Database Sessions: Stored securely in PostgreSQL
- Email Account Linking: Enabled for GitHub (allows linking accounts with same email)
Troubleshooting
"Invalid credentials" error
Check these:
-
OAuth credentials are correct:
- Verify
GITHUB_CLIENT_IDandGITHUB_CLIENT_SECRETin.env.local - Verify
AUTH_GOOGLE_IDandAUTH_GOOGLE_SECRET(if using Google) - Make sure there are no extra spaces or quotes around values
- Verify
-
Callback URLs match exactly:
- Development:
http://localhost:3000/api/auth/callback/github(note:http, nothttps) - Production:
https://your-domain.com/api/auth/callback/github(note:https) - Check OAuth provider settings match your app URLs exactly
- Development:
-
AUTH_SECRET is set:
- Run
npx auth secretto generate a new one if needed - Make sure it's in
.env.local(not.env.example)
- Run
Redirect loop
This happens when OAuth can't complete the flow. Check:
-
Callback URLs in OAuth provider settings:
- GitHub: Settings → Developer settings → OAuth Apps → Your app → Authorization callback URL
- Google: Cloud Console → APIs & Services → Credentials → Your OAuth client → Authorized redirect URIs
- Must match exactly:
http://localhost:3000/api/auth/callback/github(development) orhttps://your-domain.com/api/auth/callback/github(production)
-
AUTH_SECRET is consistent:
- Same
AUTH_SECRETmust be used across all environments - If you changed it, clear browser cookies and try again
- Verify it's set in
.env.local(not just.env.example)
- Same
-
Database is initialized:
- Run
npx prisma db pushif you haven't already - Sessions are stored in database - if tables don't exist, auth will fail
- Run
Session not persisting
Sessions are stored in the database. Check:
-
Database is configured:
# Verify DATABASE_URL is set in .env.local echo $DATABASE_URL -
Prisma migrations have been run:
# Initialize database schema npx prisma db push # Or for migrations: npx prisma migrate dev --name init -
Database tables exist:
# Check if tables were created npx prisma studio # Should see: User, Account, Session tables -
Session strategy is correct:
- ShipSecure uses
"database"strategy by default (configured insrc/lib/auth.ts) - This stores sessions in PostgreSQL, not cookies
- If sessions aren't persisting, database connection is likely the issue
- ShipSecure uses
Next Steps
- Billing & Payments - Set up license verification
- Database & Prisma - Learn about data models
- Security Features - Learn about built-in security
- Deployment - Deploy to production
- FAQ - Common questions answered
- Read the Auth.js v5 Documentation for advanced usage