Integrating Google OAuth Login with Next.js + NextAuth.js

73 Views
No Comments

Recently, I integrated Google OAuth login functionality into a project and encountered numerous pitfalls from configuration to deployment. I’ve organized my experience into this article, hoping it will help other developers facing similar challenges.

🎯 Project Background

  • Tech Stack: Next.js 13 + NextAuth.js + Vercel Deployment
  • Requirement: Add “One-Click Google Login” to a web app. After users log in, their accounts should be automatically created, and they should receive bonus credits.

📋 Complete Configuration Process

Step 1: Detailed Configuration in Google Cloud Console

This is the first and most error-prone step—many developers stumble here!

1. Create a Google Cloud Project

  • Step 1: Access the Google Cloud ConsoleOpen the Google Cloud Console, then log in with your Google account.
  • Step 2: Create a new project
    1. Click the project selector at the top of the page.
    2. Click the “New Project” button.
    3. Enter a project name (e.g., “my-nextjs-app”).
    4. Select an organization (individual developers can choose “No Organization”).
    5. Click “Create”.

💡 Tip: Use an English project name to avoid encoding issues in subsequent configurations.

Integrating Google OAuth Login with Next.js + NextAuth.js

2. Enable Google+ API (Critical!)

Many developers skip this step, leading to authentication failures later:

Integrating Google OAuth Login with Next.js + NextAuth.js
  • Step 1: Navigate to the API Library
    1. In the left sidebar, go to “APIs & Services” → “Library”.
    2. Alternatively, search directly for “Google+ API”.
  • Step 2: Enable Required APIsYou need to enable the following APIs:
    • ✅ Google+ API (deprecated, but still required for certain features)
    • ✅ People API (recommended for retrieving user information)
    • ✅ Gmail API (only if you need access to email data)
  • Step 3: Enable the APIs
    1. Click on each API card.
    2. Click the “Enable” button.
    3. Wait for the enablement to complete (usually takes a few seconds).

⚠️ Note: If you don’t enable these APIs, you’ll encounter an “access_denied” error later!

3. Configure the OAuth Consent Screen (Key Step)

This step determines the login authorization page users see:

Integrating Google OAuth Login with Next.js + NextAuth.js
  • Step 1: Access OAuth Consent Screen settingsIn the left sidebar, go to “APIs & Services” → “OAuth Consent Screen”.
  • Step 2: Select User Type
    • 🔸 Internal: For users within your organization (requires Google Workspace).
    • 🔸 External: For any Google user (recommended).Choose “External” if you’re an individual developer or building a public app.
  • Step 3: Fill in App InformationBasic Information:
    • App Name: The name users see on the authorization page.
    • User Support Email: Your contact email address.
    • App Logo (optional): Recommended to upload a 120x120px PNG.
    • App Homepage: Your app’s homepage URL.
    • App Privacy Policy: Link to your privacy policy page.
    • App Terms of Service (optional): Link to your terms of service page.

    Authorized Domains (Important):

    • Development: localhost
    • Production: yourdomain.com (without https://)
    • Testing: test.yourdomain.com

    💡 Example Setup:

  • Step 4: Configure ScopesFor basic Google login, add the following scopes:
    • ✅ ../auth/userinfo.email – Retrieve user’s email
    • ✅ ../auth/userinfo.profile – Retrieve user’s basic profile
    • ✅ openid – OpenID identifier
  • Step 5: Test Users (Optional)If your app is in “Testing” status, add email addresses of test users:

4. Create OAuth 2.0 Client ID (Core Configuration)

  • Step 1: Access the Credentials page
    1. In the left sidebar, go to “APIs & Services” → “Credentials”.
    2. Click “+ Create Credentials” → “OAuth 2.0 Client ID”.
  • Step 2: Select App TypeChoose “Web application”.
  • Step 3: Configure Client DetailsName:
    • Enter a descriptive name (e.g., “NextJS App OAuth Client”).

    Authorized JavaScript Origins:

    • Development: http://localhost:3000
    • Production: https://yourdomain.com

    Authorized Redirect URIs (Most Critical!):

    • Development: http://localhost:3000/api/auth/callback/google
    • Production: https://yourdomain.com/api/auth/callback/google

    ⚠️ Critical Reminder: The redirect URI must strictly follow NextAuth.js’s format: /api/auth/callback/[provider-name]

Integrating Google OAuth Login with Next.js + NextAuth.js
  • Step 4: Save Configuration
    1. Click “Create”.
    2. The system will display your Client ID and Client Secret.
    3. Copy and save both values immediately—you’ll need them later!
Integrating Google OAuth Login with Next.js + NextAuth.js

5. Retrieve Configuration Details

After configuration, you’ll have:

  • Client ID: 12345678-abcdefg.apps.googleusercontent.com
  • Client Secret: GOCSPX-abcdefghijklmnopqrstuvwxyz

🔥 Common Google Console Configuration Errors

Error 1: redirect_uri_mismatch

  • Error MessageError 400: redirect_uri_mismatch
  • Cause: Incorrect redirect URI configuration.
  • Solutions:
    • ✅ Correct format: http://localhost:3000/api/auth/callback/google
    • ❌ Wrong format: http://localhost:3000/api/auth/google/callback
    • ❌ Wrong format: http://localhost:3000/auth/callback/google

Error 2: access_denied

  • Error MessageError: access_denied
  • Causes:
    1. Required APIs not enabled.
    2. Incomplete OAuth Consent Screen configuration.
    3. Missing test users (if the app is in Testing status).
  • Solutions:
    1. Ensure the People API is enabled.
    2. Fill in all required fields on the OAuth Consent Screen.
    3. Add test user emails.

Error 3: invalid_client

  • Error MessageError: invalid_client
  • Cause: Incorrect Client ID or Client Secret.
  • Solutions:
    1. Verify the GOOGLE_CLIENT_ID in your environment variables.
    2. Confirm the Client Secret was copied correctly.
    3. Check for hidden characters or extra spaces in the values.

🔥 Pitfalls I Encountered

Pitfall 1: Server Error – Server Configuration Issue

  • Symptom: After clicking “Sign in with Google”, the page shows: “Server error – There is a problem with the server configuration”.
  • Root Cause: Incomplete or incorrect environment variable configuration.
  • Solution:Ensure your .env.local file includes the following:env# .env.local (required configuration) NEXTAUTH_SECRET="your_generated_secret" NEXTAUTH_URL="https://your-domain.com" GOOGLE_CLIENT_ID="your_google_client_id" GOOGLE_CLIENT_SECRET="your_google_client_secret"
    • Key Reminders:
      1. Generate NEXTAUTH_SECRET using the command: openssl rand -base64 32.
      2. Reconfigure environment variables in Vercel after deployment.
      3. Ensure NEXTAUTH_URL matches the correct domain for local/production environments.

Pitfall 2: “This action with HTTP GET is not supported”

  • Symptom: The URL redirects to /api/auth/error with a “GET method not supported” message.
  • Troubleshooting Process:
    1. First suspected a file path issue.
    2. Checked the NextAuth configuration file location.
    3. Finally identified an incorrect callback URL.
  • Root Cause: Wrong Google OAuth redirect URL configuration!javascript// ❌ Incorrect configuration GOOGLE_REDIRECT_URI="https://domain.com/api/auth/google/callback" // ✅ Correct configuration GOOGLE_REDIRECT_URI="https://domain.com/api/auth/callback/google"
  • Key Discovery: NextAuth.js uses a fixed callback URL format: /api/auth/callback/[provider]

📋 NextAuth.js Code Configuration

Below is the complete NextAuth.js configuration file for App Router (Next.js 13+). Save it at: app/api/auth/[...nextauth]/route.ts

typescript

import {Provider} from "next-auth/providers/index";
import GoogleProvider from "next-auth/providers/google";
import {AuthOptions} from "next-auth";
import NextAuth from "next-auth";
import {genUniSeq, getIsoTimestr} from "@/backend/utils";
import {saveUser} from "@/backend/service/user";
import {User} from "@/backend/type/type";
import {createCreditUsage} from "@/backend/service/credit_usage";
import {getCreditUsageByUserId} from "@/backend/service/credit_usage";

/**
 * Initialize the array of authentication providers
 * Stores all available login methods (Google, GitHub, Facebook, etc.)
 */
let providers: Provider[] = [];

/**
 * Configure Google OAuth provider
 * Load Google app client configuration from environment variables
 */
providers.push(
  GoogleProvider({
    clientId: process.env.GOOGLE_CLIENT_ID || "", // Google app client ID
    clientSecret: process.env.GOOGLE_CLIENT_SECRET || "", // Google app client secret
    // 🚨 Important: Do NOT manually set redirect_uri—NextAuth.js handles this automatically
  })
);

/**
 * NextAuth.js configuration options
 * Contains callbacks and parameters for the authentication flow
 */
const authOptions: AuthOptions = {
  secret: process.env.NEXTAUTH_SECRET, // Secret for encrypting JWT tokens
  providers, // List of authentication providers
  callbacks: {
    /**
     * Sign-in verification callback
     * Called when a user attempts to log in; determines if login is allowed
     */
    async signIn({user, account, profile, email, credentials}) {// Add login restriction logic here (e.g., whitelist checks, ban status)
      const isAllowedToSignIn = true;
      if (isAllowedToSignIn) {return true;} else {
        // Return false to deny login, or a URL to redirect to an error page
        return false;
      }
    },

    /**
     * Redirect callback
     * Controls page navigation after the authentication flow completes
     */
    async redirect({url, baseUrl}) {
      // Redirect to homepage after successful login
      // Customize to redirect to role-specific pages if needed
      return `${baseUrl}/`;
    },

    /**
     * Session callback
     * Called each time a session is fetched; customizes the session object sent to the client
     */
    async session({session, token, user}) {
      // Merge user data from JWT token into the session
      // Allows the frontend to access full user info via useSession()
      if (token && token.user) {session.user = token.user;}
      return session;
    },

    /**
     * JWT callback
     * Called when a JWT token is created or updated
     * Core function for user registration and database operations
     */
    async jwt({token, user, account}) {// Only run on initial login (user/account params exist only on first login)
      if (user && user.email && account) {
        try {
          // Create a database user object
          const dbUser: User = {uuid: genUniSeq(), // Generate unique user ID
            email: user.email, // Google account email
            nickname: user.name || "", // Google account display name
            avatar_url: user.image || "", // Google account avatar URL
            signin_type: account.type, // Login type (oauth)
            signin_provider: account.provider, // Login provider (google)
            signin_openid: account.providerAccountId, // Unique Google user ID
            created_at: getIsoTimestr(), // Creation time (ISO format)
            signin_ip: "", // Login IP (leave empty; add IP-fetching logic later if needed)
          };

          // Save user to database
          // The saveUser function typically handles updates if the user already exists
          await saveUser(dbUser);

          // Check if the user already has a credit record
          const creditUsage = await getCreditUsageByUserId(dbUser.uuid);

          // If new user: create a credit record and grant initial bonus credits
          if (!creditUsage) {
            await createCreditUsage({
              user_id: dbUser.uuid, // User ID
              user_subscriptions_id: -1, // Subscription ID (-1 = no subscription)
              is_subscription_active: false, // Subscription status (inactive)
              used_count: 0, // Number of credits used
              period_remain_count: 20, // Remaining credits (20 for new users)
              period_start: new Date(), // Credit period start date
              period_end: new Date(// Credit period end date (1 month later)
                new Date().setMonth(new Date().getMonth() + 1)
              ),
              created_at: new Date(), // Record creation time});
          }

          // Store user data in JWT token
          // This data will be used in the session callback later
          token.user = {
            uuid: dbUser.uuid,
            nickname: dbUser.nickname,
            email: dbUser.email,
            avatar_url: dbUser.avatar_url,
            created_at: dbUser.created_at,
          };
        } catch (error) {console.error("Error processing user data in JWT callback:", error);
          // Continue authentication even if database operations fail
          // Avoid disrupting the user login experience
        }
      }

      return token;
    },
  },
};

/**
 * Create NextAuth handler
 * Initialize NextAuth with the configuration options above
 */
const handler = NextAuth(authOptions);

/**
 * Export the handler
 * For App Router (Next.js 13+), export both GET and POST methods
 * Handles all HTTP requests for NextAuth.js
 */
export {handler as GET, handler as POST};

Full Environment Variables Setup

env

# NextAuth Basic Configuration
NEXTAUTH_SECRET=your_openssl_generated_secret
NEXTAUTH_URL=https://your-domain.com

# Google OAuth Configuration  
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret

# Database Configuration (if required)
DATABASE_URL=your_database_connection_string

🐛 Debugging Tips

1. Step-by-Step Verification

plaintext

# Step 1: Verify NextAuth routes
http://localhost:3000/api/auth/providers

# Step 2: Test the login page
http://localhost:3000/api/auth/signin  

# Step 3: Check callback handling
Monitor URL redirects during the login flow

2. Log Debugging

Add logs to the JWT callback to track data flow:

javascript

async jwt({token, user, account}) {console.log('JWT Callback Data:', { user, account});
  // Business logic...
  return token;
}

3. Vercel Logs

After deployment, view detailed error logs in the Vercel Dashboard:Go to Vercel Dashboard > Functions

✅ Best Practices Summary

Configuration

  • Use precise environment variable names: NextAuth is highly sensitive to variable naming.
  • Follow the fixed callback URL format: Stick to /api/auth/callback/[provider].
  • Separate environments: Use domain-specific configurations for local/dev/production.

Development

  • Debug locally first: Ensure the local environment works perfectly before deployment.
  • Troubleshoot layer by layer: Check Google Console → Environment Variables → Code Configuration.
  • Keep logs: Add logs in key callbacks for easier issue diagnosis.

Deployment

  • Reconfigure environment variables: Always re-set all variables in Vercel after deployment.
  • Match domains: Ensure NEXTAUTH_URL matches your actual deployed domain.
  • Trigger re-deployment: Re-deploy after updating environment variables.

💡 Final Thoughts

Integrating Google OAuth may seem simple, but it involves many easily overlooked details. The most common pitfalls are:

  1. Incomplete Google Cloud Console configuration
  2. Typos in environment variables
  3. Incorrect callback URL format
  4. Conflicting configurations between local and production environments

When debugging, use your browser’s DevTools to inspect network requests—error details are often hidden in HTTP responses.

Don’t panic if you hit issues! Go through the configuration checklist step by step—99% of problems can be resolved this way.

If this article helped you, please give it a like!Feel free to leave questions in the comments—I’ll do my best to respond. Let

END
 0
Pa2sw0rd
Copyright Notice: Our original article was published by Pa2sw0rd on 2025-09-21, total 12348 words.
Reproduction Note: Unless otherwise specified, all articles are published by cc-4.0 protocol. Please indicate the source of reprint.
Comment(No Comments)