First Auth Flow

Complete walkthrough of implementing Google OAuth authentication.

Complete Authentication Implementation

This guide walks you through implementing a complete Google OAuth authentication flow with @warpy-auth-sdk/core, including protected routes and session management.

Project Structure

Here's the recommended project structure for a Next.js app with authentication:

my-auth-app/
├── app/
│   ├── layout.tsx          # Root layout with AuthProvider
│   ├── page.tsx            # Home page
│   ├── login/
│   │   └── page.tsx        # Login page
│   ├── dashboard/
│   │   └── page.tsx       # Protected dashboard
│   └── api/
│       └── auth/
│           └── session/
│               └── route.ts # Session API endpoint
├── proxy.ts                # Next.js 16 Proxy for auth routes
├── .env.local             # Environment variables
└── package.json

Step 1: Configure the Proxy

The proxy handles all authentication routes automatically:

proxy.ts

Main authentication configuration

import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { authMiddleware } from "@warpy-auth-sdk/core/next";
import { google } from "@warpy-auth-sdk/core";

const handler = authMiddleware(
  {
    secret: process.env.AUTH_SECRET!,
    provider: google({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      redirectUri: process.env.GOOGLE_REDIRECT_URI!,
    }),
    callbacks: {
      // Resolve/upsert your user with smallest overhead
      async user(u) {
        // In a real app, you'd save this to your database
        // For now, we'll just return the user data
        return { 
          id: u.id || u.email, 
          email: u.email, 
          name: u.name, 
          picture: u.picture 
        };
      },
      // Customize JWT token
      jwt: (token) => {
        // Add any custom claims here
        return token;
      },
      // Customize session data
      session: (session) => {
        // Add any custom session data here
        return session;
      },
    },
  },
  {
    basePath: "/api/auth",
    successRedirect: "/dashboard",
    errorRedirect: "/login",
  }
);

export function proxy(request: NextRequest) {
  const p = request.nextUrl.pathname;
  if (p.startsWith("/api/auth")) return handler(request);
  return NextResponse.next();
}

export const config = {
  matcher: [
    "/((?!_next|[^?]*\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
    "/(api|trpc)(.*)",
  ],
};

Step 2: Set Up the Auth Provider

Wrap your app with the AuthProvider to enable authentication context:

app/layout.tsx

Root layout with authentication provider

import { AuthProvider } from "@warpy-auth-sdk/core/hooks";
import "./globals.css";

export const metadata = {
  title: "My Auth App",
  description: "A Next.js app with authentication",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <AuthProvider>
          {children}
        </AuthProvider>
      </body>
    </html>
  );
}

Step 3: Create the Home Page

A simple home page that shows authentication status:

app/page.tsx

Home page with authentication status

'use client';

import { useAuth } from "@warpy-auth-sdk/core/hooks";
import Link from "next/link";

export default function HomePage() {
  const { session, loading } = useAuth();

  if (loading) {
    return (
      <div className="min-h-screen flex items-center justify-center">
        <div className="text-center">
          <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900"></div>
          <p className="mt-4 text-gray-600">Loading...</p>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-gray-50">
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div className="py-16">
          <div className="text-center">
            <h1 className="text-4xl font-bold text-gray-900 mb-8">
              Welcome to My Auth App
            </h1>
            
            {session ? (
              <div className="space-y-4">
                <div className="bg-white p-6 rounded-lg shadow">
                  <h2 className="text-2xl font-semibold mb-4">
                    Hello, {session.user.name}!
                  </h2>
                  <p className="text-gray-600 mb-4">
                    Email: {session.user.email}
                  </p>
                  {session.user.picture && (
                    <img 
                      src={session.user.picture} 
                      alt="Profile" 
                      className="w-16 h-16 rounded-full mx-auto"
                    />
                  )}
                </div>
                <div className="space-x-4">
                  <Link
                    href="/dashboard"
                    className="inline-block bg-blue-600 text-white px-6 py-3 rounded-md hover:bg-blue-700"
                  >
                    Go to Dashboard
                  </Link>
                </div>
              </div>
            ) : (
              <div className="space-y-4">
                <p className="text-xl text-gray-600 mb-8">
                  Please sign in to access your dashboard
                </p>
                <Link
                  href="/login"
                  className="inline-block bg-blue-600 text-white px-6 py-3 rounded-md hover:bg-blue-700"
                >
                  Sign In
                </Link>
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

Step 4: Create the Login Page

A dedicated login page with Google OAuth:

app/login/page.tsx

Login page with Google OAuth button

'use client';

import { useAuth } from "@warpy-auth-sdk/core/hooks";
import { useRouter } from "next/navigation";
import { useEffect } from "react";

export default function LoginPage() {
  const { session, loading } = useAuth();
  const router = useRouter();

  useEffect(() => {
    if (session) {
      router.push("/dashboard");
    }
  }, [session, router]);

  if (loading) {
    return (
      <div className="min-h-screen flex items-center justify-center">
        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900"></div>
      </div>
    );
  }

  if (session) {
    return null; // Will redirect to dashboard
  }

  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-50">
      <div className="max-w-md w-full space-y-8">
        <div>
          <h2 className="mt-6 text-center text-3xl font-bold text-gray-900">
            Sign in to your account
          </h2>
          <p className="mt-2 text-center text-sm text-gray-600">
            Use your Google account to sign in
          </p>
        </div>
        <div>
          <a
            href="/api/auth/signin/google"
            className="group relative w-full flex justify-center py-3 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
          >
            <svg className="w-5 h-5 mr-2" viewBox="0 0 24 24">
              <path
                fill="currentColor"
                d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
              />
              <path
                fill="currentColor"
                d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
              />
              <path
                fill="currentColor"
                d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
              />
              <path
                fill="currentColor"
                d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
              />
            </svg>
            Continue with Google
          </a>
        </div>
      </div>
    </div>
  );
}

Step 5: Create the Dashboard

A protected dashboard that requires authentication:

app/dashboard/page.tsx

Protected dashboard page

'use client';

import { useAuth } from "@warpy-auth-sdk/core/hooks";
import { useRouter } from "next/navigation";
import { useEffect } from "react";

export default function DashboardPage() {
  const { session, loading, signOut } = useAuth();
  const router = useRouter();

  useEffect(() => {
    if (!loading && !session) {
      router.push("/login");
    }
  }, [session, loading, router]);

  if (loading) {
    return (
      <div className="min-h-screen flex items-center justify-center">
        <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-gray-900"></div>
      </div>
    );
  }

  if (!session) {
    return null; // Will redirect to login
  }

  return (
    <div className="min-h-screen bg-gray-50">
      <div className="bg-white shadow">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
          <div className="flex justify-between items-center py-6">
            <h1 className="text-3xl font-bold text-gray-900">Dashboard</h1>
            <div className="flex items-center space-x-4">
              <span className="text-sm text-gray-700">
                Welcome, {session.user.name}
              </span>
              <button
                onClick={signOut}
                className="bg-red-600 text-white px-4 py-2 rounded-md hover:bg-red-700"
              >
                Sign Out
              </button>
            </div>
          </div>
        </div>
      </div>

      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
          <div className="bg-white p-6 rounded-lg shadow">
            <h3 className="text-lg font-semibold mb-4">Profile Information</h3>
            <div className="space-y-2">
              <p><strong>Name:</strong> {session.user.name}</p>
              <p><strong>Email:</strong> {session.user.email}</p>
              {session.user.picture && (
                <div className="mt-4">
                  <img 
                    src={session.user.picture} 
                    alt="Profile" 
                    className="w-16 h-16 rounded-full"
                  />
                </div>
              )}
            </div>
          </div>

          <div className="bg-white p-6 rounded-lg shadow">
            <h3 className="text-lg font-semibold mb-4">Session Details</h3>
            <div className="space-y-2 text-sm">
              <p><strong>User ID:</strong> {session.user.id}</p>
              <p><strong>Session Type:</strong> {session.type || 'standard'}</p>
              <p><strong>Expires:</strong> {new Date(session.expires).toLocaleString()}</p>
            </div>
          </div>

          <div className="bg-white p-6 rounded-lg shadow">
            <h3 className="text-lg font-semibold mb-4">Quick Actions</h3>
            <div className="space-y-2">
              <button className="w-full bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700">
                Update Profile
              </button>
              <button className="w-full bg-gray-600 text-white px-4 py-2 rounded-md hover:bg-gray-700">
                Settings
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

Step 6: Add Session API Endpoint

Create an API endpoint for session management:

app/api/auth/session/route.ts

Session API endpoint

import { getSession } from "@warpy-auth-sdk/core";
import { NextRequest } from "next/server";

export async function GET(request: NextRequest) {
  try {
    const session = await getSession(request, process.env.AUTH_SECRET!);
    
    return Response.json({ 
      session,
      success: true 
    });
  } catch (error) {
    return Response.json({ 
      session: null,
      success: false,
      error: error instanceof Error ? error.message : 'Unknown error'
    }, { status: 500 });
  }
}

Step 7: Test the Authentication Flow

Start your development server and test the complete flow:

npm run dev
  1. Visit http://localhost:3000
  2. Click "Sign In" to go to the login page
  3. Click "Continue with Google" to start OAuth
  4. Complete the Google OAuth flow
  5. You should be redirected to the dashboard
  6. Test the sign-out functionality

Congratulations!

You've successfully implemented a complete authentication flow with @warpy-auth-sdk/core. The system handles OAuth, session management, and protected routes automatically.

What You've Built

Your authentication system now includes:

  • OAuth Integration: Google OAuth with automatic token handling
  • Session Management: JWT-based sessions with secure cookies
  • Protected Routes: Automatic redirects for unauthenticated users
  • User Context: React hooks for accessing session data
  • API Endpoints: Session management via API routes

Next Steps

Now that you have basic authentication working, you can explore:

First Auth Flow | @warpy-auth-sdk/core