How to implement Google login in 15 minutes

Three steps. Standard OIDC. Any library implements it identically. No excuse for this to eat a sprint.

· LoginWith team

Google login has a reputation for being fiddly. It’s not. It’s standard OIDC, documented better than most specs, and the entire integration fits in 50 lines of code. Here’s the walkthrough.

1. Register your OAuth client (5 min)

Head to console.cloud.google.com, create a project (or use an existing one), and navigate to:

APIs & Services → Credentials → Create Credentials → OAuth Client ID

Choose “Web application.” Set:

  • Authorized JavaScript origins: https://yourapp.com (and http://localhost:3000 for dev)
  • Authorized redirect URIs: https://yourapp.com/auth/google/callback

Note: the redirect URI is matched exactly. Trailing slashes count. Capitalization counts. Save the Client ID and Client Secret.

Also configure the OAuth consent screen. Most apps start in “External, Testing” mode, which limits to 100 users until you go through verification. For basic openid profile email scopes, verification is a day or two.

2. Redirect the user to Google (5 min)

When the user clicks “Sign in with Google,” build the authorization URL:

const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth')
authUrl.searchParams.set('client_id', GOOGLE_CLIENT_ID)
authUrl.searchParams.set('redirect_uri', GOOGLE_REDIRECT_URI)
authUrl.searchParams.set('response_type', 'code')
authUrl.searchParams.set('scope', 'openid profile email')
authUrl.searchParams.set('state', randomStateValue)
authUrl.searchParams.set('code_challenge', pkceChallenge)
authUrl.searchParams.set('code_challenge_method', 'S256')

// Store state and code_verifier in user's session (HttpOnly cookie)
res.cookies.set('oauth_state', randomStateValue)
res.cookies.set('pkce_verifier', codeVerifier)
res.redirect(authUrl.toString())

state protects against CSRF on the callback. code_challenge/code_verifier is PKCE protection against code interception. Both are mandatory in 2026.

3. Handle the callback (5 min)

When Google redirects back to /auth/google/callback?code=...&state=...:

// Verify state
if (req.query.state !== req.cookies.oauth_state) {
  return res.status(400).send('Invalid state')
}

// Exchange code for tokens
const tokenRes = await fetch('https://oauth2.googleapis.com/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    code: req.query.code,
    client_id: GOOGLE_CLIENT_ID,
    client_secret: GOOGLE_CLIENT_SECRET,
    redirect_uri: GOOGLE_REDIRECT_URI,
    grant_type: 'authorization_code',
    code_verifier: req.cookies.pkce_verifier,
  }),
})
const tokens = await tokenRes.json()

// Verify the ID token (JWT) against Google's JWKS
const { payload } = await jwtVerify(
  tokens.id_token,
  createRemoteJWKSet(new URL('https://www.googleapis.com/oauth2/v3/certs')),
  {
    issuer: 'https://accounts.google.com',
    audience: GOOGLE_CLIENT_ID,
  }
)

// Extract user info
const user = {
  googleSub: payload.sub,
  email: payload.email,
  name: payload.name,
}

// Find or create the user in your DB
const dbUser = await findOrCreateUser(user)

// Create a session
await createSession(res, dbUser)

res.redirect('/app')

That’s the whole implementation

50 lines of server code. Every other OIDC provider (Microsoft, GitHub, GitLab) works identically — swap the URLs and you’ve shipped five login options in an afternoon.

For production: use a library like openid-client or your framework’s auth module. They add correctness guarantees (clock skew tolerance, algorithm pinning, etc.) that hand-rolled code tends to miss.

Want auth that just works?

Get started with LoginWith