All Articles
Tutorial10 min read

Implementing OAuth 2.0: A Developer's Complete Guide

Understand OAuth 2.0 flows and implement Google, GitHub, and custom OAuth in your applications properly.

T

TechGyanic

November 24, 2025

Implementing OAuth 2.0: A Developer's Complete Guide

OAuth confused me for years. "Why are there so many redirects? What's the difference between tokens?" If you've asked these questions, this guide is for you.

OAuth In Simple Terms

OAuth lets users log into your app using their Google/GitHub/etc. account without sharing their password with you.

The flow:

  1. User clicks "Login with Google"
  2. User goes to Google, logs in there
  3. Google asks "Allow this app to see your email?"
  4. User says yes
  5. Google redirects back to your app with a special code
  6. Your app exchanges code for tokens
  7. User is logged in

The Authorization Code Flow

Most common for web apps:

User → Your App → Google Authorization
                         ↓
User authorizes → Redirect to Your App (with code)
                         ↓
Your Backend → Exchange code for tokens → Google Token Endpoint
                         ↓
Got access_token + id_token → User authenticated

Implementation with Next.js

1. Setup Google OAuth

Go to Google Cloud Console:

  1. Create OAuth 2.0 credentials
  2. Add authorized redirect URI: http://localhost:3000/api/auth/callback/google

2. Environment Variables

GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-random-secret

3. NextAuth Configuration

// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';

export default NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
  ],
  callbacks: {
    async session({ session, token }) {
      session.user.id = token.sub;
      return session;
    },
  },
});

4. Using in Components

import { useSession, signIn, signOut } from 'next-auth/react';

function LoginButton() {
  const { data: session } = useSession();
  
  if (session) {
    return (
      <>
        <p>Signed in as {session.user.email}</p>
        <button onClick={() => signOut()}>Sign out</button>
      </>
    );
  }
  
  return <button onClick={() => signIn('google')}>Sign in with Google</button>;
}

Understanding Tokens

Access Token

  • Short-lived (minutes to hours)
  • Used to call APIs
  • Include in requests: Authorization: Bearer <access_token>

Refresh Token

  • Long-lived (days to months)
  • Used to get new access tokens
  • Store securely (server-side only)

ID Token (OpenID Connect)

  • Contains user info
  • JWT format
  • Only for authentication, not API calls

Implementing Without NextAuth

// 1. Redirect to Google
app.get('/auth/google', (req, res) => {
  const params = new URLSearchParams({
    client_id: process.env.GOOGLE_CLIENT_ID,
    redirect_uri: 'http://localhost:3000/auth/callback',
    response_type: 'code',
    scope: 'openid email profile',
    state: generateRandomState() // CSRF protection
  });
  
  res.redirect(`https://accounts.google.com/o/oauth2/v2/auth?${params}`);
});

// 2. Handle callback
app.get('/auth/callback', async (req, res) => {
  const { code, state } = req.query;
  
  // Verify state matches
  if (state !== req.session.oauthState) {
    return res.status(400).send('Invalid state');
  }
  
  // Exchange code for tokens
  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.GOOGLE_CLIENT_ID,
      client_secret: process.env.GOOGLE_CLIENT_SECRET,
      redirect_uri: 'http://localhost:3000/auth/callback',
      grant_type: 'authorization_code'
    })
  });
  
  const tokens = await tokenResponse.json();
  // tokens.access_token, tokens.id_token, tokens.refresh_token
  
  // Decode id_token to get user info
  const payload = JSON.parse(Buffer.from(tokens.id_token.split('.')[1], 'base64').toString());
  // payload.email, payload.name, payload.picture
});

Security Checklist

  • Use HTTPS in production
  • Validate state parameter (CSRF protection)
  • Store refresh tokens server-side only
  • Validate id_token signature
  • Set proper token expiration
  • Handle token refresh gracefully

OAuth is standard across platforms. Learn it once, use it everywhere.

oauthauthenticationsecurityapibackend
Share this article
T

Written by

TechGyanic

Sharing insights on technology, software architecture, and development best practices.