How to Integrate Google APIs with a Next.js App (OAuth 2.0 Guide)

In this post, we’ll dive into integrating Google APIs with a Next.js app. We’ll walk through how to:

  • Use the official Google API client library for authentication and API calls.
  • Understand OAuth 2.0 authentication.
  • Set up Google Cloud Console for API access, credentials, and OAuth consent.
  • Build a Next.js app from login to authorization using cookies.
  • Finally, we’ll make a Google Drive API call to list the first 10 documents in your Google Drive.

Ready? Let’s get started!

Step 1: Set Up Google Cloud Console

Head over to console.cloud.google.com.Create a New Project.Under API & Services:

  • Click on Enable APIs and Services.
  • Enable the Google Drive API.

Next, Create Credentials:

  • Select User Data as the type.
  • Choose OAuth Client ID.
  • Set Web Application as the app type.
  • In Authorized JavaScript Origins, add:
    • http://localhost:3000
    • http://localhost
  • For Authorized Redirect URIs, add:
    • http://localhost:3000
    • http://localhost:3000/api/oauth2/callback

Step 2: Install Dependencies

npm install googleapis
npx shadcn@latest init -d
npx shadcn@latest add button

Step 3: Set Up Your .env File

GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REDIRECT_URI=http://localhost:3000/api/oauth2/callback

These values are required to authenticate users via Google OAuth.

Step 4: Building the OAuth2 Flow

Create a global oauth2client utility that’s accessible throughout your app:

import { google } from 'googleapis';
const oauth2Client = new google.auth.OAuth2(
  process.env.GOOGLE_CLIENT_ID,
  process.env.GOOGLE_CLIENT_SECRET,
  process.env.GOOGLE_REDIRECT_URI
);

Create a login button on your Page.tsx and get the authorizationURL by passing the access_type and scope

/app/Page.tsx

import { Button } from "@/components/ui/button";
import oauth2Client from "./utils/google-auth";
import Link from "next/link";

export default function Home() {
  const SCOPE = ["https://www.googleapis.com/auth/drive.metadata.readonly"];

  const authorizationURL = oauth2Client.generateAuthUrl({
    access_type: "offline",
    scope: SCOPE,
  });

  return (
    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
      <Link href={authorizationURL}>
        <Button>Login To Google</Button>
      </Link>
    </div>
  );
}

Step 5: Handle the Callback

Next, let’s handle that callback and exchange the authorization code for an access token.

  • Create the API route for your OAuth2 callback: /app/api/oauth2/callback/route.ts
  • Save the access token in a cookie so it can be used for subsequent API calls.
  • Redirect to the dashboard
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import oauth2Client from '@/app/utils/google-auth';

export async function GET(req: Request) {
    const { searchParams } = new URL(req.url);
    const code = searchParams.get('code');
    const error = searchParams.get('error');

    if (error) {
        return NextResponse.json({ error: 'Google OAuth Error: ' + error });
      }
    
    if (!code) {
        return NextResponse.json({ error: 'Authorization code not found' });
    }

    try {
        const { tokens } = await oauth2Client.getToken(code);
        cookies().set({
            name: 'google_access_token',
            value: tokens.access_token || '',  // the access token
            httpOnly: true,  // for security, the cookie is accessible only by the server
            secure: process.env.NODE_ENV === 'production',  // send cookie over HTTPS only in production
            path: '/',  // cookie is available on every route
            maxAge: 60 * 60 * 24 * 7,  // 1 week
        });

        return NextResponse.redirect(new URL("/dashboard", req.url));
    } catch (error) {

        return NextResponse.json({ error: 'Google OAuth Error failed to exchange code: ' + error });
    }
}

Step 6: Call Google APIs (Google Drive Example)

Now that the user is authenticated, we can use the OAuth2 client to make API calls. For example, let’s fetch the first 10 documents from Google Drive:

  • Call the Google Drive API: /app/dashboard/Page.tsx
import oauth2Client from "../utils/google-auth";
import { cookies } from "next/headers";
import { google } from "googleapis";

export default async function Page() {
  const cookieStore = cookies();
  const accessToken = cookieStore.get("google_access_token")?.value;
  oauth2Client.setCredentials({ access_token: accessToken });
  let files;

  const drive = google.drive("v3");

  try {
    const result = await drive.files.list({
      auth: oauth2Client,
      pageSize: 10,
      fields: "nextPageToken, files(id, name)",
    });
    files = result.data.files;
  } catch (error) {
    console.log(error);
    return (
      <div className="justify-center w-full flex text-center pt-10 flex-col items-center">
        Something went wrong! Please login again!
      </div>
    );
  }

  return (
    <div className="justify-center w-full flex text-center pt-10 flex-col items-center">
      <h1 className="text-lg font-bold">Google Drive Files</h1>
      <ul>
        {files?.map((file) => (
          <li key={file.id}>{file.name}</li>
        ))}
      </ul>
    </div>
  );
}

This code lists the first 10 files in your Google Drive, provided the OAuth2 client has valid credentials.

Conclusion

To wrap it up, here’s what we covered:

  1. Setting up a Google Cloud project and enabling the Drive API.
  2. Implementing OAuth2 in your Next.js app, including the login flow and handling callbacks.
  3. Storing the access token in cookies to maintain the session.
  4. Calling Google APIs, like Google Drive, using the authenticated OAuth2 client.

Now you have a fully functional app that integrates with Google APIs, allowing you to explore other Google services in a similar fashion. Enjoy building!

Leave a Reply

Your email address will not be published. Required fields are marked *