How Google-Like SSO Works — A Technical Deep Dive
A comprehensive breakdown of Single Sign-On architecture — from OAuth 2.0 and OpenID Connect token flows to session cookies, PKCE, token introspection, and the security engineering behind Google's identity platform.
What is SSO and Why Does it Exist?
Single Sign-On (SSO) is an authentication scheme that allows a user to log in once and gain access to multiple independent applications without re-entering credentials. When you click "Sign in with Google" on a third-party app, you are not giving that app your password. Instead, Google acts as a trusted Identity Provider (IdP), and the third-party app is a Service Provider (SP) or Relying Party (RP).
The Core Problem SSO Solves
- Credential Fatigue: Users managing dozens of username/password pairs is a security disaster. Password reuse leads to credential stuffing attacks.
- Security Centralization: Instead of every app storing password hashes (and potentially losing them in a breach), credential storage is centralized at the IdP.
- Developer Simplicity: Third-party apps don't need to build their own authentication systems. They delegate identity to a trusted, battle-tested provider.
- Unified Session Management: Log out once, log out everywhere. Revoke access centrally.
Key Participants in Every SSO Flow
- Resource Owner: The user (you).
- Identity Provider (IdP): Google, Microsoft, Okta — the entity that stores credentials and issues tokens.
- Relying Party / Client Application: The app you want to sign into (e.g., Notion, Figma).
- Resource Server: An API or backend that the client app calls on behalf of the user. It validates tokens.
- Authorization Server: Often the same as the IdP; issues access tokens, refresh tokens, and ID tokens.
A Brief History: From SAML to Modern Auth
SSO isn't new. Back in 2005, enterprises adopted SAML 2.0 — an XML-based protocol built for corporate intranets, desktop browsers, and IT-managed applications. It worked, but it was designed before mobile apps, REST APIs, or SPAs existed. SAML assertions are verbose XML documents, the flow is browser-only, and integrating it as a developer is notoriously painful.
In 2012, the IETF published OAuth 2.0 (RFC 6749) — lightweight, JSON-based, and built for the modern web. The OpenID Foundation then layered OpenID Connect (OIDC) on top to add identity. Today, every major platform — Google, Apple, Microsoft, GitHub — uses OIDC for consumer auth. SAML lives on only in legacy enterprise integrations. For anything new, OAuth 2.0 + OIDC is the standard.
The Protocol Stack: OAuth 2.0 vs. OpenID Connect vs. SAML
These three are not competitors — they form a layered stack. OAuth 2.0 answers "what can this app access?", OIDC adds "who is the user?", and SAML is the older XML-based approach that tried to do both.
graph LR
OAuth["OAuth 2.0\n(Authorization)"] -->|"adds identity"| OIDC["OpenID Connect\n(Authentication)"]
SAML["SAML 2.0\n(Legacy Enterprise)"] -..->|"older alternative"| OIDC
- OAuth 2.0 — Issues short-lived Access Tokens that grant scoped access to APIs. Not an auth protocol — it doesn't tell you who the user is.
- OpenID Connect (OIDC) — Sits on top of OAuth 2.0 and adds an ID Token (a
signed JWT with user claims) and a
/userinfoendpoint. "Sign in with Google" is OIDC. - SAML 2.0 — XML assertions over browser redirects. Still alive in enterprise (Workday, Salesforce), but use OIDC for anything new.
| Dimension | SAML 2.0 | OAuth 2.0 + OIDC |
|---|---|---|
| Format | XML | JSON / JWT |
| Mobile Support | Poor (browser-only) | First-class (PKCE) |
| API Authorization | Not designed for it | Core use case |
| Dev Experience | Complex, hard to debug | Simple, easy to inspect |
| Used Today | Legacy Enterprise SaaS | Consumer Apps, APIs, Modern SaaS |
The OIDC Authorization Code Flow — Step by Step
The Authorization Code Flow is the gold standard for server-side web applications. It is the flow Google uses for "Sign in with Google." Understanding this is understanding how SSO actually works.
sequenceDiagram
participant User as Resource Owner (User)
participant App as Relying Party (e.g., Notion)
participant Google as Google Auth Server (IdP)
participant GAPI as Google UserInfo API
User->>App: Clicks "Sign in with Google"
App->>Google: 302 Redirect: /authorize?client_id=...&response_type=code&scope=openid email&redirect_uri=...&state=xyz&code_challenge=...
Google->>User: Show Google Login Page
User->>Google: Enter Credentials / Select Account
Google->>App: 302 Redirect to redirect_uri?code=AUTH_CODE&state=xyz
App->>Google: POST /token (code=AUTH_CODE, client_secret, code_verifier)
Google->>App: {access_token, id_token (JWT), refresh_token, expires_in}
App->>GAPI: GET /userinfo (Authorization: Bearer access_token)
GAPI->>App: {sub, email, name, picture}
App->>User: User is now logged in!
Phase 1 — The Authorization Request
The app redirects the browser to Google's Authorization Endpoint with a few key parameters:
response_type=code: Tells Google to return a short-lived authorization code, not the token directly — keeps tokens out of the browser URL and history.scope=openid email profile: Declares what the app needs.openidtriggers OIDC and unlocks the ID Token;emailandprofilerequest user data.state: A random nonce generated by the app. Google echoes it back on redirect — the app verifies it to prevent CSRF attacks.
Phase 2 — The Token Exchange
After the user authenticates, Google sends a short-lived authorization code (~60 seconds TTL)
to the redirect_uri. The app's backend server then makes a server-to-server POST
request directly to Google's Token Endpoint — never via the browser. This is the critical
security boundary: the client_secret (which proves the app's identity) never touches the browser.
Google validates the code and returns three tokens:
- Access Token: A short-lived (typically 1 hour) credential to call Google APIs on the user's behalf.
- ID Token (JWT): A signed JSON Web Token containing user identity claims (
sub,email,aud,iss,exp). This is what the app uses to establish a user session. - Refresh Token: A long-lived token used to obtain new Access Tokens when the current one expires, without requiring the user to log in again.
The ID Token: A JWT
The ID Token is the heart of OIDC. It is a JSON Web Token (JWT), which is a self-contained, signed data structure that doesn't require a network round-trip to validate. A JWT is three Base64URL-encoded strings joined by dots:
flowchart LR
A["Header\n(Algorithm & Key ID)"] --- B["."]
B --- C["Payload\n(Claims / User Data)"]
C --- D["."]
D --- E["Signature\n(RSA-256 / EC-256)"]
The Validation Challenge
Once your app receives an ID Token, it faces a fundamental problem: how do you trust it? The token came over the network — it could be expired, tampered with, issued by a different identity provider entirely, or intended for a different app. Calling Google on every request to ask "is this token still valid?" would add a network round-trip to every single API call — a latency and availability nightmare at scale.
Validating an ID Token Without a Network Call
Because the ID Token is signed by Google using an RSA private key, your app can verify it locally using
Google's public keys, fetched once from Google's JWKS (JSON Web Key Set) endpoint:
https://www.googleapis.com/oauth2/v3/certs. Cache these keys (Google rotates them ~weekly). This
means token validation is a pure CPU operation — no network round-trip to Google required on every API call.
PKCE: Protecting the Authorization Code
PKCE (Proof Key for Code Exchange, pronounced "pixie") is a security extension that prevents a malicious app on the same device from intercepting the authorization code redirect. It is mandatory for SPAs and mobile apps.
sequenceDiagram
participant App
participant Google
App->>App: 1. Generate random code_verifier (43-128 chars)
App->>App: 2. code_challenge = BASE64URL(SHA256(code_verifier))
App->>Google: 3. /authorize?code_challenge=X9f2...&code_challenge_method=S256
Google->>App: 4. Returns auth code
App->>Google: 5. POST /token (code=..., code_verifier=original_secret)
Google->>Google: 6. Verify: SHA256(code_verifier) == stored code_challenge?
Google->>App: 7. Issues tokens only if match
A malicious app that intercepts the code in step 4 can't exchange it for tokens — it never saw the
code_verifier.
Session Architecture: What Happens After Token Exchange?
After OIDC completes, the app creates its own server-side session. Tokens are never stored in
the browser — localStorage is readable by any JavaScript and is an XSS vulnerability. Instead:
sequenceDiagram
participant Browser
participant Server
participant Redis
Browser->>Server: POST callback with auth code
Server->>Server: Validate ID Token sig, iss, aud, exp
Server->>Redis: Store session user_id, email, refresh_token
Server->>Browser: Set HttpOnly Secure SameSite cookie with session_id
Note over Browser: Only the opaque cookie is stored, no tokens
Browser->>Server: GET /dashboard with cookie
Server->>Redis: Lookup session_id, return user context
Server->>Browser: Return response
The cookie flags are critical: HttpOnly blocks JavaScript from reading it (stops XSS theft),
Secure ensures HTTPS-only transmission, and SameSite=Lax blocks the cookie on
cross-site requests (stops CSRF).
Token Refresh
Access Tokens expire in ~1 hour. The server silently refreshes them using the stored Refresh Token — invisible to the user:
sequenceDiagram
participant App
participant Google
App->>App: Access Token is expired
App->>Google: POST /token (grant_type=refresh_token, refresh_token=..., client_id, client_secret)
Google->>App: New access_token + new id_token (refresh_token may rotate)
App->>App: Update stored tokens, continue request
Google's Infrastructure: How It Scales
Google processes billions of SSO authentications per day across Gmail, YouTube, Google Workspace, and millions
of third-party apps. The architecture behind accounts.google.com is a masterclass in distributed
systems engineering.
flowchart TD
Client[Browser / Mobile] --> GLB[Global Load Balancer\nAnycast IP]
GLB --> FE[Frontend Auth Servers\nMultiple Regions]
FE --> AuthLogic[Authentication Logic\nMFA, Risk Engine, Session Lookup]
AuthLogic --> SpannerDB[(Google Spanner\nGlobal User DB)]
AuthLogic --> Bigtable[(Bigtable\nSession Store)]
AuthLogic --> KeyStore[(Hardware Security Modules\nPrivate Keys)]
AuthLogic --> RiskSignals[Risk Scoring Engine\nIP Reputation, Device Trust]
Spanner: The Global User Database
Google's user account database is built on Cloud Spanner, a globally distributed relational database offering external consistency (stronger than serializable) with automatic sharding. This means a user's account data is replicated across multiple continents with single-digit millisecond read latency globally. Credential validation happens within a single Spanner transaction.
Token Signing with Hardware Security Modules (HSMs)
Google's JWT signing private keys are stored in Hardware Security Modules (HSMs) — physical tamper-resistant cryptographic processors. The private key material never leaves the HSM. Instead, the signing operation (RSASSA-PKCS1-v1_5 or ES256) is performed inside the hardware. Even Google's own engineers cannot extract the raw key material. Keys are rotated automatically, and the corresponding public keys are published to the JWKS endpoint.
Risk-Based Authentication
Google doesn't just validate passwords. Every login attempt is scored by a risk engine that considers:
- IP address reputation and geolocation (new country login?)
- Device fingerprint (known device vs. new device)
- User behavioral signals (login time patterns)
- Velocity signals (too many logins in short time?)
- Password breach detection (Is this credential in a known leaked dataset?)
If the risk score exceeds a threshold, Google requires an additional factor — a 2FA prompt, a security key challenge, or an email verification — before issuing tokens.
Security Attack Vectors and Mitigations
1. Open Redirect Attack
Attack: A malicious site constructs a login URL like:
https://accounts.google.com/o/oauth2/auth?...&redirect_uri=https://evil.com, attempting to have
Google redirect the auth code to the attacker's server.
Mitigation: Google strictly validates that the redirect_uri in the request
exactly matches a pre-registered URI. Any mismatch causes an immediate hard error — no redirect is issued.
2. CSRF on the Redirect
Attack: An attacker initiates an OAuth flow with their own Google account and tricks a victim into completing the flow, linking the attacker's Google account to the victim's app account.
Mitigation: The state parameter. The app generates a random nonce before the
redirect, stores it in the session, and verifies it matches on callback. The attacker cannot forge a state they
don't control.
3. Authorization Code Interception
Attack: On mobile, a malicious app registered with the same custom URL scheme
(myapp://callback) intercepts the authorization code redirect.
Mitigation: PKCE. The code is useless without the code_verifier that only the
legitimate app holds.
4. JWT Algorithm Confusion Attack (alg=none)
Attack: An attacker crafts a JWT with alg: "none" and no signature, then presents
it as a valid ID Token.
Mitigation: Always explicitly specify expected algorithms (e.g., RS256, ES256) in your JWT
validation library. Never allow alg: "none". Use a well-maintained library (e.g.,
google-auth-library) rather than hand-rolling JWT validation.
5. Token Leakage via Referer Header
Attack: If an access token or code is included in a URL query parameter, it can leak via the HTTP Referer header to analytics or CDN logs.
Mitigation: The Authorization Code flow ensures tokens are never placed in the URL. The authorization code (which is in the URL) is short-lived (~60 seconds, single-use) and useless without the client_secret.
System Design: Building Your Own SSO System
What if you need to build internal SSO for a suite of microservices? Here is the reference architecture:
flowchart TD
Apps["Internal Apps\n(HR, Finance, CRM)"] --> APIGW[API Gateway]
APIGW --> AuthSvc[Auth Service\n/authorize /token /userinfo /introspect]
AuthSvc --> UserDB[(User Store\nPostgres)]
AuthSvc --> SessionStore[(Session Store\nRedis Cluster)]
AuthSvc --> KeyVault[(Key Vault\nHSM / AWS KMS)]
AuthSvc --> ExtIdP["External IdPs\nGoogle, GitHub (via OIDC)"]
APIGW --> TokenCheck{Token Validation}
TokenCheck -->|"JWT: Local verify\n(cached JWKS)"| FastPath[Fast Path ✅]
TokenCheck -->|"Opaque: Introspect"| AuthSvc
The Auth Service Core Responsibilities
- Session Issuance: After successful authentication (local or federated), issue a signed session token or opaque session ID stored in Redis with TTL.
- Token Minting: Issue JWTs signed with a private key stored in KMS. Include
jti(JWT ID) for revocation tracking. - JWKS Endpoint: Expose public keys at
/.well-known/jwks.jsonso downstream services can validate tokens locally without calling the auth service. - Introspection Endpoint: For opaque tokens or real-time revocation checks.
- Federation Gateway: Act as an OIDC Relying Party to external providers (Google, GitHub), normalizing their identity claims into your internal user model.
Caching Strategy for JWKS
Downstream services that validate JWTs locally need the signing public keys. Cache these aggressively with a
stale-while-revalidate strategy: serve from cache immediately but refresh in the background.
When you rotate keys, use a transitional period where both old and new keys are published. The JWT's
kid (Key ID) header field tells validators which specific key to use, allowing zero-downtime key
rotation.
Conclusion
Google-like SSO is a multi-layered system that combines the OAuth 2.0 delegation framework, the OpenID Connect identity layer, JWT cryptography, PKCE security extensions, hardware-backed key storage, risk-based authentication signals, and distributed session management. The elegance of the system lies in its security model: your credentials never touch third-party apps, tokens are cryptographically verifiable without network calls, and even if a short-lived access token leaks, the blast radius is strictly limited in time and scope. Understanding this architecture is foundational to building any modern, secure, user-facing platform at scale.
- To explore Google's globally distributed database engine, read Google Spanner: The Database That Redefined What's Possible.
- To learn about security boundaries and encryption, read Security Design.
- To explore REST and OIDC integration patterns, read Integration & API Design.