Google OAuth

Implement Google sign-in for your application

Google OAuth allows users to sign in with their Google account. New users are automatically registered without email verification.

Prerequisites

Before implementing Google OAuth, complete the Google OAuth setup in the Environment Setup guide.

OAuth Flow

User clicks Google button

The login page includes a Google sign-in button that redirects to Google's authorization page.

const googleAuthUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');
googleAuthUrl.searchParams.set('client_id', clientId);
googleAuthUrl.searchParams.set('redirect_uri', redirectUri);
googleAuthUrl.searchParams.set('response_type', 'code');
googleAuthUrl.searchParams.set('scope', 'openid email profile');
window.location.href = googleAuthUrl.toString();

User selects Google account

Google shows account selection. The user chooses which account to use.

Google redirects with code

Google redirects to /api/oauth/google?code=xxx with an authorization code.

Backend exchanges code for token

const tokenResponse = 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.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
    client_secret: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_SECRET,
    redirect_uri: `${appUrl}/api/oauth/google`,
    grant_type: 'authorization_code',
  }),
});

Fetch user data from Google

const userResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
  headers: { Authorization: `Bearer ${accessToken}` },
});

const userData = await userResponse.json();
// { id, email, verified_email, name, given_name, family_name, picture }

Validate email verification

Google provides verified_email field. Only verified emails are accepted.

if (!userData.verified_email) {
  return redirect('/log-in?error=email_not_verified');
}

Create or login user

The backend checks if a user exists with this email:

  • Existing Google user: Logs them in
  • New user: Creates account (auto-verified) and profile
  • Email exists with different auth: Returns error

Redirect with tokens

const redirectUrl = new URL('/oauth-callback', appUrl);
redirectUrl.searchParams.set('accessToken', accessToken);
redirectUrl.searchParams.set('refreshToken', refreshToken);
redirectUrl.searchParams.set('sessionId', session.sessionId);
return NextResponse.redirect(redirectUrl);

Callback Handler

The callback handler at src/app/api/oauth/google/route.ts processes the OAuth response.

User Data Mapping

Google FieldFastly Field
given_name or email prefixfirstName
family_namelastName
emailemail, username (prefix)
pictureavatar

Account Linking Rules

ScenarioResult
New emailCreate new account with authMethod: 'google'
Existing Google accountLogin to existing account
Email exists with password authError: use_email_login
Email exists with GitHub authError: use_github_login

Google requires email verification on their platform. Only verified emails can be used for OAuth.

Error Handling

The callback may redirect with error codes in the URL:

Error CodeDescription
oauth_cancelledUser closed the consent screen
missing_codeNo authorization code in callback URL
invalid_grantAuthorization code expired or already used
email_not_verifiedGoogle email is not verified
use_email_loginEmail registered with password authentication
use_github_loginEmail registered with GitHub OAuth
token_exchange_failedFailed to exchange code for access token
fetch_user_failedFailed to fetch user profile from Google

Frontend Error Handling

src/app/(auth)/oauth-callback/page.tsx
'use client';

import { useSearchParams, useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { useAuth } from '@/providers/auth-provider';

export default function OAuthCallback() {
  const searchParams = useSearchParams();
  const router = useRouter();
  const { login } = useAuth();

  useEffect(() => {
    const error = searchParams.get('error');
    const accessToken = searchParams.get('accessToken');
    const refreshToken = searchParams.get('refreshToken');
    const sessionId = searchParams.get('sessionId');

    if (error) {
      router.push(`/log-in?error=${error}`);
      return;
    }

    if (accessToken && refreshToken) {
      login(accessToken, refreshToken, null, { sessionId });
      router.push('/dashboard');
    }
  }, [searchParams]);

  return <div>Processing...</div>;
}

Frontend Implementation

Google Sign-In Button

src/app/(auth)/components/google-button.tsx
'use client';

import { Button } from '@/components/ui/button';
import { FcGoogle } from 'react-icons/fc';

export function GoogleButton() {
  const handleClick = () => {
    const clientId = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID;
    const redirectUri = `${window.location.origin}/api/oauth/google`;

    const url = new URL('https://accounts.google.com/o/oauth2/v2/auth');
    url.searchParams.set('client_id', clientId);
    url.searchParams.set('redirect_uri', redirectUri);
    url.searchParams.set('response_type', 'code');
    url.searchParams.set('scope', 'openid email profile');
    url.searchParams.set('access_type', 'offline');
    url.searchParams.set('prompt', 'consent');

    window.location.href = url.toString();
  };

  return (
    <Button variant="outline" onClick={handleClick} className="w-full">
      <FcGoogle className="mr-2 h-4 w-4" />
      Continue with Google
    </Button>
  );
}

Session Metadata

Google OAuth sessions include the following metadata:

Prop

Type

On this page