Tutorials Search / Backend integrations / Sign in with Google (OAuth)
📝 Written ● Intermediate Updated 2026-05-13

Sign in with Google (OAuth)

Google OAuth is the path of least resistance to "your users can sign in to your app." They click a button, Google handles the credentials, your app gets a verified email and a stable user ID. The setup is a one-time dance in Google Cloud Console; the runtime is a few lines of code.

Every app that has users has to answer "how do they prove they're them." The traditional answer — email plus password — is also the most expensive. You're now responsible for password storage (hashing, salting, hopefully not rolling your own), password reset flows (email-based, with token expiry, with rate-limiting), forgot-password UIs, and the eternal support burden of "I can't log in." Half the support tickets at most consumer apps trace back to passwords. None of it adds value to your product.

OAuth sidesteps all of this by delegating identity to an existing identity provider — Google, in this case. Your user is already signed in to Google. They click "Sign in with Google," Google asks them once "let this app see your email," they say yes, your app gets back a JWT containing their email and a stable user ID. You never see their password. You never store their password. Forgot password isn't your problem anymore.

The implementation has two distinct parts. The boring part: register your app with Google, get a client ID and a client secret, configure authorized redirect URLs. The interesting part: handle the redirect flow in your code, exchange the temporary auth code for a real token, look up the user, create or update a session. This tutorial covers both, with notes on the specific places first-time OAuth integrations break.

What you'll learn

Step 1: The flow in plain English

1

Four parties: user, browser, your server, Google

What happens when someone clicks "Sign in with Google":

  1. Your server redirects the browser to a Google URL with your client ID and a callback URL.
  2. Google asks the user "do you want to let this app see your email?" The user clicks Allow.
  3. Google redirects the browser back to your callback URL with a temporary authorization code.
  4. Your server takes that code, POSTs it back to Google with your client secret, and gets back an ID token — a JWT containing the user's email, name, Google user ID, etc.
  5. Your server verifies the JWT signature (proving it actually came from Google), creates a session, sets a cookie, redirects to your app.

The client secret never leaves your server. The authorization code is one-time-use. The session cookie is what your app uses going forward. Each piece has a specific security property.

Step 2: Create a Google Cloud project

2

console.cloud.google.com

Sign in to the Google Cloud Console. Create a new project (top-left "Select a project" → "New project"). Name it after your app. The project is the container for everything related to it on Google Cloud.

You don't need to enable billing for OAuth — only paid Google APIs need billing. Sign-in is free.

Step 3: Configure the OAuth consent screen

3

What users see when granting permission

In the project, APIs & Services → OAuth consent screen. Pick External user type (Internal is only for Google Workspace domains). Configure:

  • App name: what users see ("Sign in to AppName").
  • User support email: a real address you check.
  • Authorized domains: your production domain (e.g., yourapp.com).
  • Developer contact email: usually you.
  • Scopes: for basic sign-in, request email, profile, and openid. Don't request more than you need.

Initially the app is in Testing status — only listed test users can sign in. You can add up to 100 test users in the OAuth consent screen settings. For production, you'll move to "In Production" (Step 8).

Step 4: Create OAuth client credentials

4

The client ID and client secret

APIs & Services → Credentials → Create Credentials → OAuth client ID.

  • Application type: Web application.
  • Name: internal; users don't see this.
  • Authorized JavaScript origins: your app's URLs. For dev: http://localhost:3000. For production: https://yourapp.com. Add all environments.
  • Authorized redirect URIs: where Google sends the code back. Typically http://localhost:3000/api/auth/callback/google for dev, https://yourapp.com/api/auth/callback/google for production. Must match exactly — trailing slashes, http vs https, port numbers all matter.

Click Create. Google shows your Client ID (long string ending in .apps.googleusercontent.com) and Client Secret (shorter, starts with GOCSPX-). Copy both into your backend's env file:

GOOGLE_CLIENT_ID=...apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-...
The redirect URI mismatch is the most common bug. If your code sends a redirect URI that doesn't exactly match one in the authorized list, Google returns "redirect_uri_mismatch" — no fuzzy matching, no helpful guidance. Add every variant you'll use (dev, staging, production, both with and without www if applicable) up front.

Step 5: Implement the flow (or use a library)

5

NextAuth / Auth.js / Passport / clerk — pick one

You can implement OAuth manually with HTTP calls, but you almost certainly shouldn't. The standard libraries handle the state parameter (anti-CSRF), JWT verification, session management, and dozens of edge cases. For most stacks:

  • Next.js: next-auth (now Auth.js). Configure providers in one file.
  • Express / Node: Passport.js with the passport-google-oauth20 strategy.
  • Django: django-allauth.
  • Rails: omniauth + omniauth-google-oauth2.
  • Anything modern: Clerk, Auth0, Supabase Auth — auth-as-a-service that handles Google plus a dozen other providers.

For Auth.js in Next.js, the configuration is roughly:

import GoogleProvider from "next-auth/providers/google";

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
  ],
};

That plus the standard Auth.js route handler is enough. Twenty lines of code; a working sign-in button.

Step 6: Handle the user record

6

First sign-in creates; subsequent sign-ins look up

When the OAuth callback succeeds, you have a verified email and a Google user ID. The logic in your handler:

  1. Look up a user in your DB by Google ID. If they exist, that's the session user.
  2. If not, check by email. If they exist, link the Google account to that user (this handles "they signed up with email/password first, then later switched to Google").
  3. If neither matches, create a new user record with the email, name, and Google ID.

Store the Google user ID separately from the email. Email addresses can theoretically change at Google; the Google ID never does.

Step 7: Test on localhost

7

Use the test-user list while developing

With the consent screen in Testing status, only emails added to the Test Users list can sign in. Add your own Google account; you can now click "Sign in with Google" on your local dev server and complete the flow.

Google's first-time consent screen shows your app name, the scopes requested, and a "This app isn't verified" warning (in Testing mode). Click "Advanced → Go to AppName (unsafe)" to proceed. Real users won't see this once you publish; until then, every test user sees it.

Step 8: Verification for production

8

The "unsafe" warning is for testing only

To remove the unverified-app warning for real users, submit your app for verification. From the OAuth consent screen, click Publish App → Submit for verification. Google reviews your homepage, privacy policy, and the scopes you've requested.

For basic scopes (email, profile, openid) the verification is automatic — usually instant. For sensitive scopes (Gmail access, Drive access, etc.) verification can take weeks and requires a security assessment. Stick to basic scopes unless you have a real product reason for more.

Once verified, "unsafe" goes away, the user-cap of 100 testers lifts, and any Google user worldwide can sign in.

One project for all environments. Use a single Google Cloud project with all your redirect URIs (dev, staging, prod) added at once, rather than separate projects per environment. Easier to manage; same client ID across environments; less verification overhead.

What's next