Authentication

Learn how RankThis handles user authentication with NextAuth.js 5, Google OAuth, Magic Links, and secure session management.

Authentication Overview

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

Auth Configuration File

The main authentication configuration is in src/auth.ts:

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";
6
7export 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});
Environment Variables

Required environment variables for authentication:

.env
# NextAuth Configuration
NEXTAUTH_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

Google OAuth

Users can sign in with their Google account for quick authentication:

src/components/sign-in/Google.tsx
1// Google sign-in component
2import { signIn } from "next-auth/react";
3
4export function GoogleSignIn() {
5 return (
6 <button
7 onClick={() => signIn("google")}
8 className="flex items-center gap-2 rounded-lg border px-4 py-2"
9 >
10 <GoogleIcon />
11 Continue with Google
12 </button>
13 );
14}

🔗 Setup Required

Configure Google OAuth in Google Cloud Console and add the client credentials to your environment variables.

Magic Links

Passwordless authentication via email links:

src/components/sign-in/Email.tsx
1// Email sign-in component
2import { signIn } from "next-auth/react";
3import { useState } from "react";
4
5export function EmailSignIn() {
6 const [email, setEmail] = useState("");
7 const [isLoading, setIsLoading] = useState(false);
8
9 const handleSubmit = async (e: React.FormEvent) => {
10 e.preventDefault();
11 setIsLoading(true);
12
13 await signIn("resend", {
14 email,
15 redirect: false
16 });
17
18 setIsLoading(false);
19 // Show success message
20 };
21
22 return (
23 <form onSubmit={handleSubmit}>
24 <input
25 type="email"
26 value={email}
27 onChange={(e) => setEmail(e.target.value)}
28 placeholder="Enter your email"
29 required
30 />
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

Server-Side Sessions

Access user sessions in Server Components and API routes:

Server-side usage
1import { auth } from "~/auth";
2
3// In Server Components
4export default async function ProtectedPage() {
5 const session = await auth();
6
7 if (!session) {
8 redirect("/auth/signin");
9 }
10
11 return (
12 <div>
13 <h1>Welcome, {session.user?.name}!</h1>
14 <p>Email: {session.user?.email}</p>
15 </div>
16 );
17}
18
19// In API Routes
20export async function GET() {
21 const session = await auth();
22
23 if (!session) {
24 return new Response("Unauthorized", { status: 401 });
25 }
26
27 // Access user data
28 const userId = session.user?.id;
29
30 return Response.json({ userId });
31}
Client-Side Sessions

Access sessions in Client Components with the session hook:

Client-side usage
1"use client";
2
3import { useSession, signOut } from "next-auth/react";
4
5export function UserProfile() {
6 const { data: session, status } = useSession();
7
8 if (status === "loading") {
9 return <div>Loading...</div>;
10 }
11
12 if (!session) {
13 return <div>Please sign in</div>;
14 }
15
16 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 Out
23 </button>
24 </div>
25 );
26}

Remember to wrap your app with SessionProvider in the root layout.

Protecting Routes

Middleware Protection

Protect routes using NextAuth.js middleware:

src/middleware.ts
1import { auth } from "~/auth";
2import { NextResponse } from "next/server";
3
4export 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");
9
10 // Redirect unauthenticated users from protected pages
11 if (isProtectedPage && !isAuth) {
12 return NextResponse.redirect(new URL("/auth/signin", req.url));
13 }
14
15 // Redirect authenticated users from auth pages
16 if (isAuthPage && isAuth) {
17 return NextResponse.redirect(new URL("/account", req.url));
18 }
19
20 return NextResponse.next();
21});
22
23export const config = {
24 matcher: ["/account/:path*", "/admin/:path*", "/auth/:path*"],
25};
Component-Level Protection

Create reusable components for protected content:

Protected component pattern
1import { auth } from "~/auth";
2import { redirect } from "next/navigation";
3
4interface ProtectedPageProps {
5 children: React.ReactNode;
6 requireAdmin?: boolean;
7}
8
9export async function ProtectedPage({
10 children,
11 requireAdmin = false
12}: ProtectedPageProps) {
13 const session = await auth();
14
15 if (!session) {
16 redirect("/auth/signin");
17 }
18
19 // Check admin permissions if required
20 if (requireAdmin) {
21 const isAdmin = await checkAdminStatus(session.user?.id);
22 if (!isAdmin) {
23 redirect("/account");
24 }
25 }
26
27 return <>{children}</>;
28}
29
30// Usage in pages
31export default async function AdminPage() {
32 return (
33 <ProtectedPage requireAdmin>
34 <h1>Admin Dashboard</h1>
35 {/* Admin content */}
36 </ProtectedPage>
37 );
38}

Database Integration

User Management

NextAuth.js automatically manages users and accounts in your database:

prisma/schema.prisma
1// Prisma schema for auth tables
2model Account {
3 id String @id @default(cuid())
4 userId String
5 type String
6 provider String
7 providerAccountId String
8 refresh_token String? @db.Text
9 access_token String? @db.Text
10 expires_at Int?
11 token_type String?
12 scope String?
13 id_token String? @db.Text
14 session_state String?
15 user User @relation(fields: [userId], references: [id], onDelete: Cascade)
16
17 @@unique([provider, providerAccountId])
18}
19
20model User {
21 id String @id @default(cuid())
22 name String?
23 email String @unique
24 emailVerified DateTime?
25 image String?
26 accounts Account[]
27 sessions Session[]
28 subscription Subscription?
29
30 @@map(name: "users")
31}

Users are created automatically on first sign-in. You can extend the User model with custom fields as needed.

💡 Authentication Best Practices

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.