--- name: sign-in-with-google-web metadata: category: Identity version: "1.0.0" description: >- Implement, configure, and secure Sign In With Google (SiwG) using Google Identity Services (GIS / `https://accounts.google.com/gsi/client`) across web architectures. Use when creating Google sign-in buttons, implementing Google One Tap with FedCM, integrating GIS in React/Next.js/Angular/HTML, verifying ID tokens on backend runtimes (Python/Node.js/Go/Java), enforcing Google Workspace domain restrictions (`hd`), securing client-side ID tokens via WebCrypto nonces or encrypted IndexedDB, embedding in cross-origin iframes via the Intermediate Iframe API (`https://accounts.google.com/gsi/intermediate`), configuring Content Security Policy (CSP), COOP, and Permissions Policy (`identity-credentials-get`) headers, or handling sign-out and revocation. Don't use for legacy Google Sign-In (`gapi.auth2`), Google Cloud IAM service accounts, or Android/iOS native Credential Manager. --- # Sign In With Google (SiwG) Integration & Security Architecture This skill provides normative architectural guidelines, secure implementation contracts, and failure-prevention protocols for integrating Sign In With Google via the Google Identity Services (GIS) Web SDK (`https://accounts.google.com/gsi/client`). ## 1. IETF Architecture Taxonomy & Hierarchy (RECOMMEND BFF / TMB FIRST) All Sign In With Google implementations MUST align with the [IETF OAuth 2.0 for Browser-Based Applications (`draft-ietf-oauth-browser-based-apps`)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#name-application-architecture-pa) taxonomy. > [!IMPORTANT] **Primary Recommendation:** Always recommend backend-mediated > patterns (**Pattern A: Backend For Frontend** or **Pattern B: Token-Mediating > Backend / Redirect Mode**) as the most secure, robust, and industry-standard > architectures whenever an application has a backend server. Fall back to > client-only browser patterns (Patterns C & D) only when constrained by > serverless static hosting or offline-first PWA requirements. ``` ┌─────────────────────────────────────────────────────────────────────────────────────────┐ │ IETF ARCHITECTURE HIERARCHY & SELECTION │ ├──────────────────────────┬──────────────────────────┬───────────────────────────────────┤ │ Pattern A (RECOMMENDED): │ Pattern B (RECOMMENDED): │ Patterns C & D (Fallbacks): │ │ Backend For Frontend │ Token-Mediating Backend │ Browser-Based OAuth Client │ │ (BFF - IETF § 6.1) │ (TMB / Redirect § 6.2) │ (JavaScript-Only - IETF § 6.3) │ ├──────────────────────────┼──────────────────────────┼───────────────────────────────────┤ │ 🏆 GOLD STANDARD │ 🚀 NATIVE FORM REDIRECT │ ⚡ STATIC SPA / 💾 OFFLINE PWA │ │ • Full-stack SPA + API │ • Server-rendered & apps │ • Pure client-side or offline PWAs│ │ • Client JS fetch to API │ • GIS HTTP POST login_uri│ • C: Ephemeral in-memory closure │ │ • HttpOnly session cookie│ • Direct server redirect │ • D: WebCrypto Encrypted IndexedDB│ │ • Tokens never in browser│ • Tokens never in JS │ • Strict WebCrypto nonces / keys │ └──────────────────────────┴──────────────────────────┴───────────────────────────────────┘ ``` ### 🏆 Pattern A: Backend For Frontend (BFF - IETF § 6.1) — STRONGLY RECOMMENDED * **Why it is the Gold Standard**: ID tokens (JWTs) and access tokens are never exposed to browser JavaScript across page reloads. This provides complete architectural immunity against XSS token harvesting. * **Architecture**: The browser frontend loads the GIS SDK, captures the credential in JavaScript callback (`callback: handleCredentialResponse`), and immediately forwards the ID token (JWT) via `fetch('/api/auth/google', { method: 'POST' })` to the backend. * **Backend Responsibilities**: The backend validates the JWT cryptographic signature against Google's public JWKs (`google.oauth2.id_token.verify_oauth2_token`), checks the `aud`, `hd`, and `nonce` claims against server session state, creates a server session, and issues a first-party `HttpOnly; Secure; SameSite=Lax` session cookie. * See full implementation in [references/bff_fastapi_verification.md](references/bff_fastapi_verification.md). ### 🚀 Pattern B: Token-Mediating Backend via `login_uri` Redirect (IETF § 6.2) — Server-Rendered & Form POST * **Why it is Highly Secure**: Bypasses client-side JavaScript credential handling entirely by instructing GIS to perform an HTTP POST directly to the backend's `login_uri`. * **Architecture**: Configured via `google.accounts.id.initialize({ client_id, login_uri: "https://example.com/api/auth/callback", ux_mode: "redirect" })` or HTML attributes (`data-login_uri="https://example.com/api/auth/callback"` and `data-ux_mode="redirect"`). * **Backend Responsibilities**: The backend receives the ID token as a form POST body (`credential` parameter), validates the double-submit `g_csrf_token` cookie against the `g_csrf_token` POST body field, cryptographically verifies the ID token server-side, establishes a session cookie, and returns a standard HTTP 302/303 redirect. * See full implementation in [references/tmb_login_uri_redirect.md](references/tmb_login_uri_redirect.md). ### Pattern C: Browser-Based OAuth Client — Ephemeral In-Memory (IETF § 6.3) — Fallback for Static SPAs * **Scope**: Use ONLY when deployment is strictly serverless/static (e.g., GitHub Pages, Firebase static hosting) with no backend component. * **Storage Invariant**: The ID token is held strictly in private JavaScript memory/closures during the active tab session. **Never store raw tokens in `localStorage` or `sessionStorage`**. * **Session Renewal**: Uses GIS One Tap / FedCM auto-select (`auto_select: true`) to transparently re-acquire fresh ID tokens into memory on page reload without persistent browser storage. * **XSS & Replay Protection**: Generate a cryptographic `nonce` via `window.crypto.getRandomValues()` and pass it directly to `google.accounts.id.initialize({ client_id, nonce: clientNonce })`. Validate that `payload.nonce === clientNonce` before trusting claims in memory. * See full implementation in [references/in_memory_spa_nonce.md](references/in_memory_spa_nonce.md). ### Pattern D: Browser-Based OAuth Client — WebCrypto Encrypted IndexedDB (IETF § 6.3) — Fallback for Offline PWAs * **Scope**: Use ONLY for offline-first Progressive Web Apps (PWAs) where user authentication proof must survive tab refreshes when disconnected from the network. * **Storage Invariant**: **NEVER store raw plaintext ID tokens in `localStorage`**. Encrypt the ID token payload using WebCrypto `AES-GCM` with a non-extractable session key (`extractable: false`) before writing ciphertext and IV to `IndexedDB`. * See full implementation in [references/offline_pwa_encryption.md](references/offline_pwa_encryption.md). -------------------------------------------------------------------------------- ## 2. Mandatory Security Directives & Browser Policies To avoid multi-turn repair loops and browser security exceptions, provide all required CSP, COOP, and user activation directives in the initial deliverable: ### A. Content Security Policy (CSP) Directives ```http Content-Security-Policy: script-src 'self' https://accounts.google.com/gsi/client; frame-src https://accounts.google.com/gsi/; connect-src https://accounts.google.com/gsi/; ``` If using strict nonce-based CSP, attach the nonce to the GIS script tag: ``. ### B. Cross-Origin Opener Policy (COOP) To allow GIS popup dialogs to communicate credentials back to the parent window via `postMessage`: ```http Cross-Origin-Opener-Policy: same-origin-allow-popups ``` *(Serving `same-origin` without `allow-popups` breaks GIS popups and results in silent failures).* ### C. Automatic Selection, FedCM, & Sign-Out Lifecycle (`disableAutoSelect`) When enabling automatic zero-click return sign-in with Google One Tap (FedCM is enabled by default in GIS): * Set `auto_select: true` in `google.accounts.id.initialize({...})` (do not pass the deprecated `use_fedcm_for_prompt` parameter). * **Deprecated Library Warning**: NEVER mix deprecated `gapi.auth2` (`gapi.auth2.init`, `gapi.auth2.getAuthInstance().signOut()`) with Google Identity Services (GIS). `gapi.auth2` is completely retired; use GIS methods exclusively. * **Sign-Out Protocol**: When the user explicitly logs out of your application, you **MUST** call `google.accounts.id.disableAutoSelect()`: ```javascript function handleUserLogout() { // 1. Disable automatic One Tap / FedCM re-authentication google.accounts.id.disableAutoSelect(); // 2. Clear application session state / server cookie fetch('/api/auth/logout', { method: 'POST' }).then(() => { window.location.href = '/login'; }); } ``` * *(Failing to call `disableAutoSelect()` causes an immediate automatic re-login loop on the next page visit after intentional user logout).* ### D. Reliable Dynamic Script Loading & Framework Lifecycle (React, Next.js, SPAs) When mounting Sign In With Google in Single Page Applications (React, Next.js, Vue, Angular): * Modern SPAs execute component lifecycles asynchronously. Attempting to access `window.google.accounts.id` before `