CodeNotes
Back to library
authenticationMay 31, 20267 min read

Google OAuth Integration

Configure Google login inside a TypeScript codebase, with step-by-step route endpoints configurations.

Google OAuth Integration

Why Google OAuth is used

Google OAuth is one of the most widely adopted identity verification standards on the web. It provides:

  1. Low Friction: Users can log in with a single click, boosting user conversion rates.
  2. Verified Identity: Google handles bot detection and account verification, ensuring you deal with genuine email profiles.
  3. Security Standards: Features like Multi-Factor Authentication (MFA) and account recovery are managed by Google.

Implementing this requires exchanging authorization codes for identity credentials (OpenID Connect ID Tokens).


Mental Model

Think of Google OAuth as Hotel Card Key Verification.

  • Traditional Login: You buy a house and install a custom lock. You have to craft physical keys (passwords) and manage who holds them.
  • OAuth Login: You check into a hotel. Instead of giving you a permanent key to the building, the receptionist verifies your ID card, registers your stay, and gives you a temporary Key Card (Access/ID Token). This card only unlocks your specific room (email scope) and automatically deactivates when you check out (token expiry).
1. User Signs In
Browser redirects to accounts.google.com
2. Auth Code Return
Google redirects back with authorization code
3. ID Token Issued
Server validates token and reads email

Core Specifications

  • Why it exists: It was created based on OpenID Connect (OIDC) standards built on top of OAuth 2.0 to offer a unified, federated user identity and authentication framework.
  • What problem it solves: It eliminates user sign-up drop-offs with single-click authentication while validating emails and outsourcing password storage, MFA, and account recovery to Google.
  • How it works internally: The user grants access on Google's consent screen. Google redirects back with an authorization code. The application backend exchanges this code via a POST request for an Access Token and a cryptographically signed ID Token (JWT), validating its signature using Google's dynamic public keys.
  • When to use it: Use it in consumer SaaS apps, booking apps, and client portals where zero-friction sign-ups and verified email identities are essential.
  • How it is used in real projects: Production web portals extract the OIDC profile data from verified ID tokens, synchronizing it into their local user databases via transactional upserts and initializing default dashboards.

Codebase Integration

Here is a step-by-step guide to integrating Google Login via OpenID Connect (OIDC) APIs.

Step 1: Configure credentials in Google Cloud Console

  1. Go to Google Cloud Console > API & Services > Credentials.
  2. Create an OAuth Client ID for a Web Application.
  3. Set your Authorized JavaScript origins (e.g. http://localhost:3000).
  4. Set your Authorized redirect URIs (e.g. http://localhost:3000/api/auth/google/callback).
  5. Copy your Client ID and Client Secret.

Step 2: Configure Environment Credentials

Open your .env file and save the variables.

# .env (Environment config)
GOOGLE_CLIENT_ID="your_google_client_id_here"
GOOGLE_CLIENT_SECRET="your_google_client_secret_here"

Step 3: Redirect Client to Google Consent Screen

Create a route to compile parameters and redirect the browser to Google.

// filepath: src/app/api/auth/google/route.ts
// Purpose: Redirect user to Google OAuth endpoint.

import { NextResponse } from 'next/server';

export async function GET() {
  const rootUrl = 'https://accounts.google.com/o/oauth2/v2/auth';
  
  const options = {
    redirect_uri: 'http://localhost:3000/api/auth/google/callback',
    client_id: process.env.GOOGLE_CLIENT_ID || '',
    access_type: 'offline', // Request refresh tokens
    response_type: 'code',
    prompt: 'consent',
    scope: [
      'https://www.googleapis.com/auth/userinfo.profile',
      'https://www.googleapis.com/auth/userinfo.email',
    ].join(' '),
  };

  const qs = new URLSearchParams(options).toString();
  return NextResponse.redirect(`${rootUrl}?${qs}`);
}

Step 4: Handle the Token Callback Endpoint

Examine the authorization code parameter and fetch the Google profile.

// filepath: src/app/api/auth/google/callback/route.ts
// Purpose: Exchange authorization code, read profile info, and establish session cookie.

import { NextRequest, NextResponse } from 'next/server';

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

  if (!code) {
    return NextResponse.json({ error: 'Auth code not provided' }, { status: 400 });
  }

  try {
    // 1. Request OAuth Token from Google token service
    const response = await fetch('https://oauth2.googleapis.com/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        code,
        client_id: process.env.GOOGLE_CLIENT_ID || '',
        client_secret: process.env.GOOGLE_CLIENT_SECRET || '',
        redirect_uri: 'http://localhost:3000/api/auth/google/callback',
        grant_type: 'authorization_code',
      }),
    });

    const data = await response.json();

    if (!data.id_token) {
      return NextResponse.json({ error: 'OAuth exchange returned no ID Token' }, { status: 401 });
    }

    // 2. Fetch User Info using Google Access Token
    const userInfoResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
      headers: {
        Authorization: `Bearer ${data.access_token}`,
      },
    });
    const userInfo = await userInfoResponse.json();

    // 3. Set secure session token cookie containing Google login email details
    const redirectResponse = NextResponse.redirect(new URL('/dashboard', request.url));
    redirectResponse.cookies.set('session_token', data.access_token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      maxAge: 60 * 60, // 1 hour
    });

    return redirectResponse;
  } catch (error) {
    console.error('Google Callback Error:', error);
    return NextResponse.json({ error: 'Google authentication failed' }, { status: 500 });
  }
}

Practical Project Use Cases

1. User Registration Schema Synchronization

When a user logs in via Google, your database schema must reconcile the returning OAuth identity. If the user already exists, you should refresh their profile image and last-login timestamps. If they are a new sign-up, you must atomically create a user record and initialize default data (like a personal workspace folder).

  • Real-World Example: We handle the profile sync inside a database transaction right after validating the Google callback credentials, ensuring database operations execute reliably.
// filepath: src/services/userSyncService.ts
// Purpose: Upsert user details from Google profile details and compile default workspace schemas.

import { db } from '@/lib/db';

interface GoogleProfile {
  id: string;
  email: string;
  name: string;
  picture: string;
}

export async function synchronizeGoogleUser(profile: GoogleProfile) {
  // Use a transaction to ensure user creation and default workspace setup succeed together
  return await db.$transaction(async (tx) => {
    // 1. Upsert user records keyed by the verified Google email
    const user = await tx.user.upsert({
      where: { email: profile.email },
      update: {
        name: profile.name,
        avatarUrl: profile.picture,
        lastActiveAt: new Date(),
      },
      create: {
        email: profile.email,
        name: profile.name,
        avatarUrl: profile.picture,
        provider: 'GOOGLE',
        providerId: profile.id,
      },
    });

    // 2. Check if the user needs a default workspace setup (e.g. on new signups)
    const existingWorkspaces = await tx.workspace.findFirst({
      where: { ownerId: user.id },
    });

    if (!existingWorkspaces) {
      await tx.workspace.create({
        data: {
          name: `${profile.name || 'My'}'s Workspace`,
          ownerId: user.id,
        },
      });
    }

    return user;
  });
}

2. Live Calendar Event Syncing

For productivity or booking applications (like Calendly), Google OAuth tokens are requested with https://www.googleapis.com/auth/calendar.events scopes, letting your application query and append calendar invites when a customer books a meeting.

  • Real-World Example: Using the persistent offline refresh token, a backend worker runs cron cycles to query the Google Calendar API, checking for conflicts before booking a demo meeting on your system.

Common Pitfalls

1. Inconsistent Callback URL Configuration

If the callback URI in your code (http://localhost:3000/api/auth/google/callback) differs by even a single slash from the redirect URI registered in Google Console, Google will block the transaction with a redirect_uri_mismatch error.

  • Fix: Copy and paste the redirect URLs exactly as they are defined in your Google Cloud dashboard.

2. Not Verifying ID Tokens correctly

In high-security codebases, decoding tokens locally without validation is dangerous.

  • Fix: Use Google OAuth SDKs or fetch Google's public JSON Web Keys (JWKs) at https://www.googleapis.com/oauth2/v3/certs to verify the cryptographic signature of returning ID tokens before writing session profiles to the database.

Interview Questions

What is the difference between Access Tokens and ID Tokens?

An Access Token is an authorization credential used by the application to request API data from Google servers on behalf of the user. An ID Token is an identity credential (a signed JWT) that contains claims about the authenticated user (like email, profile name, and picture), verifying their log in session.


Summary

  • Scope: Requests specific profile permissions using scopes.
  • OIDC: Leverages OpenID Connect to verify identity profiles securely.
  • Verification: Checks certificates dynamically when high security is needed.

Keep Learning

authentication

GitHub OAuth Integration

Secure your codebase with GitHub authentication, including step-by-step route handler codes.

7 min readRead
authentication

Clerk Authentication

Implement managed Clerk authentication inside your Next.js application, including client and route middleware configurations.

6 min readRead