Skip to main content
Web DevelopmentSoftware Engineering

Understanding OAuth 2.0 and OpenID Connect for Web Apps

Demystify OAuth 2.0 flows, tokens, and OpenID Connect. Learn which flow to use for your app and how to implement it securely.

Mbeah Essilfie

Mbeah Essilfie

May 28, 2026 at 06:03 AM

11 min read 487 views
Understanding OAuth 2.0 and OpenID Connect for Web Apps

OAuth 2.0 in Plain English

OAuth 2.0 answers: "How can App A access User's data on Service B, without User giving App A their password?"

The analogy: a hotel key card (token) grants access to specific rooms (scopes) for a limited time, without giving away the master key (password).

The Players

  • Resource Owner — The user
  • Client — Your application
  • Authorization Server — Issues tokens (Google, Auth0, etc.)
  • Resource Server — Has the data (API)

For server-rendered apps and SPAs with a backend:

1. User clicks "Login with Google"
2. App redirects to Google's auth endpoint
3. User logs in and consents
4. Google redirects back with an authorization CODE
5. App's backend exchanges code for tokens (server-to-server)
6. App receives access_token + refresh_token + id_token
// Step 2: Redirect to auth server
const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth')
authUrl.searchParams.set('client_id', CLIENT_ID)
authUrl.searchParams.set('redirect_uri', 'https://myapp.com/callback')
authUrl.searchParams.set('response_type', 'code')
authUrl.searchParams.set('scope', 'openid email profile')
authUrl.searchParams.set('state', generateRandomState()) // CSRF protection

// Step 5: Exchange code for tokens (backend)
const tokens = await fetch('https://oauth2.googleapis.com/token', {
  method: 'POST',
  body: new URLSearchParams({
    code: authorizationCode,
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    redirect_uri: 'https://myapp.com/callback',
    grant_type: 'authorization_code',
  }),
})
Never expose client_secret to the browser. The code-to-token exchange MUST happen server-side. For SPAs without a backend, use PKCE.

PKCE: For Public Clients

// Generate a code verifier and challenge
const codeVerifier = generateRandomString(64)
const codeChallenge = base64url(sha256(codeVerifier))

// Include in auth request
authUrl.searchParams.set('code_challenge', codeChallenge)
authUrl.searchParams.set('code_challenge_method', 'S256')

// Include verifier in token exchange (proves you started the flow)
tokenRequest.set('code_verifier', codeVerifier)

OpenID Connect: Identity Layer

OAuth 2.0 = Authorization (access to resources) OIDC = Authentication (who is the user?)

OIDC adds:

  • id_token — JWT with user identity claims
  • /userinfo endpoint — Additional user data
  • Standard scopes: openid, email, profile
// Decode the id_token (after verification!)
{
  "sub": "110169484474386276334",  // Unique user ID
  "email": "mbeah@example.com",
  "name": "Mbeah Essilfie",
  "picture": "https://...",
  "iss": "https://accounts.google.com",
  "exp": 1700000000
}

Token Storage

TokenStore WhereLifetime
Access TokenMemory / httpOnly cookie5-60 min
Refresh TokenhttpOnly cookieDays-weeks
ID TokenMemory (for display only)Same as access

Which Flow to Use?

  • Server-rendered app (Nuxt SSR) → Authorization Code
  • SPA with backend → Authorization Code
  • SPA without backend → Authorization Code + PKCE
  • Machine-to-machine → Client Credentials
  • Mobile app → Authorization Code + PKCE

Never use the Implicit flow — it's deprecated for security reasons.

Node.jsSecurityAPI Design
Mbeah Essilfie

Written by Mbeah Essilfie

Fullstack Software Developer

Read next

The Complete Guide to Vue 3 Composables
12 min read

The Complete Guide to Vue 3 Composables

Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.

Mbeah EssilfieMbeah Essilfie
984