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 happens when someone clicks "Sign in with Google":
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.
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.
In the project, APIs & Services → OAuth consent screen. Pick External user type (Internal is only for Google Workspace domains). Configure:
yourapp.com).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).
APIs & Services → Credentials → Create Credentials → OAuth client ID.
http://localhost:3000. For production: https://yourapp.com. Add all environments.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-...
www if applicable) up front.
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-auth (now Auth.js). Configure providers in one file.passport-google-oauth20 strategy.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.
When the OAuth callback succeeds, you have a verified email and a Google user ID. The logic in your handler:
Store the Google user ID separately from the email. Email addresses can theoretically change at Google; the Google ID never does.
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.
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.