Authentication
Learn how RankThis handles user authentication with NextAuth.js 5, Google OAuth, Magic Links, and secure session management.
RankThis uses NextAuth.js 5 (Auth.js) for a complete authentication solution with multiple sign-in methods and secure session management.
✅ What's Included
- • Google OAuth sign-in
- • Magic Link email authentication
- • Secure session management
- • Server-side session validation
- • Automatic user creation
- • Database session storage
🔧 Built-in Features
- • Email verification
- • Account linking
- • CSRF protection
- • Admin role detection
- • Welcome emails
- • Session persistence
Configuration
The main authentication configuration is in src/auth.ts:
1import NextAuth from "next-auth";2import Google from "next-auth/providers/google";3import Resend from "next-auth/providers/resend";4import { PrismaAdapter } from "@auth/prisma-adapter";5import { db } from "~/server/db";67export const { handlers, signIn, signOut, auth } = NextAuth({8 adapter: PrismaAdapter(db),9 providers: [10 Google({11 clientId: process.env.GOOGLE_CLIENT_ID,12 clientSecret: process.env.GOOGLE_CLIENT_SECRET,13 }),14 Resend({15 apiKey: process.env.RESEND_API_KEY,16 from: process.env.FROM_EMAIL,17 }),18 ],19 pages: {20 signIn: "/auth/signin",21 verifyRequest: "/auth/verify-request",22 },23 session: {24 strategy: "database",25 },26 callbacks: {27 session: ({ session, user }) => ({28 ...session,29 user: {30 ...session.user,31 id: user.id,32 },33 }),34 },35});
Required environment variables for authentication:
# NextAuth ConfigurationNEXTAUTH_SECRET="your-secret-key-here"NEXTAUTH_URL="http://localhost:3000"# Google OAuth (optional)GOOGLE_CLIENT_ID="your-google-client-id.googleusercontent.com"GOOGLE_CLIENT_SECRET="your-google-client-secret"# Email Provider (for Magic Links)RESEND_API_KEY="re_..."FROM_EMAIL="noreply@yourdomain.com"
See the Configuration guidefor detailed setup instructions.
Sign-in Methods
Users can sign in with their Google account for quick authentication:
1// Google sign-in component2import { signIn } from "next-auth/react";34export function GoogleSignIn() {5 return (6 <button7 onClick={() => signIn("google")}8 className="flex items-center gap-2 rounded-lg border px-4 py-2"9 >10 <GoogleIcon />11 Continue with Google12 </button>13 );14}
🔗 Setup Required
Configure Google OAuth in Google Cloud Console and add the client credentials to your environment variables.
Passwordless authentication via email links:
1// Email sign-in component2import { signIn } from "next-auth/react";3import { useState } from "react";45export function EmailSignIn() {6 const [email, setEmail] = useState("");7 const [isLoading, setIsLoading] = useState(false);89 const handleSubmit = async (e: React.FormEvent) => {10 e.preventDefault();11 setIsLoading(true);1213 await signIn("resend", {14 email,15 redirect: false16 });1718 setIsLoading(false);19 // Show success message20 };2122 return (23 <form onSubmit={handleSubmit}>24 <input25 type="email"26 value={email}27 onChange={(e) => setEmail(e.target.value)}28 placeholder="Enter your email"29 required30 />31 <button type="submit" disabled={isLoading}>32 {isLoading ? "Sending..." : "Send Magic Link"}33 </button>34 </form>35 );36}
Magic links are sent via Resend and expire after 24 hours for security.
Using Sessions
Access user sessions in Server Components and API routes:
1import { auth } from "~/auth";23// In Server Components4export default async function ProtectedPage() {5 const session = await auth();67 if (!session) {8 redirect("/auth/signin");9 }1011 return (12 <div>13 <h1>Welcome, {session.user?.name}!</h1>14 <p>Email: {session.user?.email}</p>15 </div>16 );17}1819// In API Routes20export async function GET() {21 const session = await auth();2223 if (!session) {24 return new Response("Unauthorized", { status: 401 });25 }2627 // Access user data28 const userId = session.user?.id;2930 return Response.json({ userId });31}
Access sessions in Client Components with the session hook:
1"use client";23import { useSession, signOut } from "next-auth/react";45export function UserProfile() {6 const { data: session, status } = useSession();78 if (status === "loading") {9 return <div>Loading...</div>;10 }1112 if (!session) {13 return <div>Please sign in</div>;14 }1516 return (17 <div>18 <h2>User Profile</h2>19 <p>Name: {session.user?.name}</p>20 <p>Email: {session.user?.email}</p>21 <button onClick={() => signOut()}>22 Sign Out23 </button>24 </div>25 );26}
Remember to wrap your app with SessionProvider in the root layout.
Protecting Routes
Protect routes using NextAuth.js middleware:
1import { auth } from "~/auth";2import { NextResponse } from "next/server";34export default auth((req) => {5 const isAuth = !!req.auth;6 const isAuthPage = req.nextUrl.pathname.startsWith("/auth");7 const isProtectedPage = req.nextUrl.pathname.startsWith("/account") ||8 req.nextUrl.pathname.startsWith("/admin");910 // Redirect unauthenticated users from protected pages11 if (isProtectedPage && !isAuth) {12 return NextResponse.redirect(new URL("/auth/signin", req.url));13 }1415 // Redirect authenticated users from auth pages16 if (isAuthPage && isAuth) {17 return NextResponse.redirect(new URL("/account", req.url));18 }1920 return NextResponse.next();21});2223export const config = {24 matcher: ["/account/:path*", "/admin/:path*", "/auth/:path*"],25};
Create reusable components for protected content:
1import { auth } from "~/auth";2import { redirect } from "next/navigation";34interface ProtectedPageProps {5 children: React.ReactNode;6 requireAdmin?: boolean;7}89export async function ProtectedPage({10 children,11 requireAdmin = false12}: ProtectedPageProps) {13 const session = await auth();1415 if (!session) {16 redirect("/auth/signin");17 }1819 // Check admin permissions if required20 if (requireAdmin) {21 const isAdmin = await checkAdminStatus(session.user?.id);22 if (!isAdmin) {23 redirect("/account");24 }25 }2627 return <>{children}</>;28}2930// Usage in pages31export default async function AdminPage() {32 return (33 <ProtectedPage requireAdmin>34 <h1>Admin Dashboard</h1>35 {/* Admin content */}36 </ProtectedPage>37 );38}
Database Integration
NextAuth.js automatically manages users and accounts in your database:
1// Prisma schema for auth tables2model Account {3 id String @id @default(cuid())4 userId String5 type String6 provider String7 providerAccountId String8 refresh_token String? @db.Text9 access_token String? @db.Text10 expires_at Int?11 token_type String?12 scope String?13 id_token String? @db.Text14 session_state String?15 user User @relation(fields: [userId], references: [id], onDelete: Cascade)1617 @@unique([provider, providerAccountId])18}1920model User {21 id String @id @default(cuid())22 name String?23 email String @unique24 emailVerified DateTime?25 image String?26 accounts Account[]27 sessions Session[]28 subscription Subscription?2930 @@map(name: "users")31}
Users are created automatically on first sign-in. You can extend the User model with custom fields as needed.
Security
- • Always validate sessions server-side
- • Use middleware for route protection
- • Implement admin role checks
- • Rotate NEXTAUTH_SECRET regularly
User Experience
- • Provide clear sign-in options
- • Show loading states during auth
- • Handle auth errors gracefully
- • Send welcome emails to new users
Authentication Configured!
Your authentication system is ready. Next, set up payments to monetize your SaaS.