首頁 / Skills / 開發提效 / Google 登入(網頁)Sign In With Google
開發提效

Google 登入(網頁)Sign In With Google

GoogleApache-2.0Skill 語言: 英文
授權已核對不含腳本
演示真實執行紀錄 · claude-sonnet-5-5 · 2026年10月
I'm adding Sign in with Google to a Next.js app that has a Node/Express backend. What architecture should I use and what must the server verify? Keep it concise.
Skill: sign-in-with-google-web

Architecture: Backend-mediated (BFF), with Express owning the session

You have a backend, so don't keep tokens in the browser. Pick one of two flows.

Option A: Next.js calls Express (recommended for a custom UI)

  1. Load GIS (https://accounts.google.com/gsi/client) in Next.js with <Script strategy="afterInteractive"> or an onload loader. Don't poll.
  2. Generate a random nonce server-side. Store it in the session, or in a short-lived HttpOnly cookie, and pass it to google.accounts.id.initialize({ client_id, nonce, callback }).
  3. In the callback, POST the credential (the ID token) to /api/auth/google with credentials: 'include'.
  4. Express verifies the token, creates a server session, and sets an HttpOnly; Secure; SameSite=Lax cookie.
  5. The ID token is never stored in localStorage or sessionStorage. The browser holds only the session cookie.

Option B: login_uri redirect
GIS POSTs the credential straight to Express (ux_mode: "redirect"). Express must check the double-submit CSRF value: the g_csrf_token cookie has to equal the g_csrf_token body field. Then it verifies the token, sets the cookie, and returns a 302. No JavaScript ever touches the token.

If Next.js and Express are on different origins, put them under one site or proxy /api through Next.js. That keeps the cookie first-party and makes SameSite=Lax work. Otherwise you need CORS with credentials, which is more fragile.

What the server must verify

Use OAuth2Client.verifyIdToken({ idToken, audience: CLIENT_ID }) from google-auth-library. Don't hand-roll JWT parsing. It checks the signature against Google's JWKS (https://www.googleapis.com/oauth2/v3/certs) and the standard claims. Make sure these hold:

Check Requirement
Signature Valid against Google's current public keys (the library caches and rotates them).
iss https://accounts.google.com or accounts.google.com.
aud Exactly your OAuth client ID. This is the most commonly missed check.
exp Not expired.
nonce Matches the value you issued for this session. It's one-time use, so delete it after the check (this blocks replay).
email_verified true before you trust or link on email.
sub Use this as the stable user key. Never use email as the primary identifier, because emails can change.
hd If you restrict to a Workspace domain, check the hd claim server-side. Client-side hd hints aren't enforcement.

For the Option B flow, also check the g_csrf_token cookie against the body field, as described above.

Session and hardening

  • Regenerate the session ID on login, to prevent session fixation.
  • Use CSRF protection on your own state-changing endpoints. SameSite=Lax helps but isn't enough on its own.
  • Send Cross-Origin-Opener-Policy: same-origin-allow-popups. Plain same-origin breaks the GIS popup silently.
  • Set a CSP that allows script-src https://accounts.google.com/gsi/client, frame-src https://accounts.google.com/gsi/, and connect-src https://accounts.google.com/gsi/.
  • On logout, call google.accounts.id.disableAutoSelect() and destroy the server session. Otherwise One Tap signs the user straight back in.
  • Don't use the legacy gapi.auth2 or gapi.signin2. They're retired. Use GIS only.
  • Don't trust any user info the client sends alongside the token. Take identity only from the verified payload.

Express sketch

const { OAuth2Client } = require('google-auth-library');
const client = new OAuth2Client(CLIENT_ID);

app.post('/api/auth/google', async (req, res) => {
  const ticket = await client.verifyIdToken({
    idToken: req.body.credential,
    audience: CLIENT_ID,
  });
  const p = ticket.getPayload();

  if (!p.email_verified) return res.sendStatus(401);
  if (p.nonce !== req.session.nonce) return res.sendStatus(401);
  delete req.session.nonce;
  // if (p.hd !== 'yourdomain.com') return res.sendStatus(403);

  const user = await upsertUserBySub(p.sub, p.email);
  req.session.regenerate(() => {
    req.session.userId = user.id;
    res.sendStatus(204);
  });
});

This is a sketch. Wrap verifyIdToken in try/catch and return 401 on failure. Also make sure regenerate doesn't lose the nonce check order.

I can wire this into your project if you'd like. It would help to know whether Next.js and Express share an origin.

Pure-instruction skill, so the trial gave the model only the Skill and Read tools. The generated Express sketch was not run or tested; the model itself says to wrap verification in try/catch and check the nonce handling order. Review all authentication code before using it.

它做什麼
指引模型使用 Google Identity Services(GIS)在網頁上整合 Google 登入。它優先建議權杖不進入瀏覽器 JavaScript 的後端中介方案,並提醒不要再使用已停用的 gapi.auth2。

運作方式

  1. 選擇架構:後端代理前端(BFF,建議)、login_uri 表單 POST 轉址(建議),以及給純靜態網站與離線 PWA 的純前端備案。
  2. 列出必須設定的瀏覽器端項目:CSP、Cross-Origin-Opener-Policy: same-origin-allow-popups、FedCM 與 One Tap 的行為、登出時呼叫 disableAutoSelect(),以及 iframe 內的 identity-credentials-get 權限。
  3. 提供參考實作:FastAPI 驗證 ID 權杖(簽章、audience、nonce、Workspace 的 hd)、轉址模式的雙重提交 CSRF、React 記憶體儲存方案、加密 IndexedDB 方案與 intermediate iframe。

適合
為 React、Next.js、Angular 或純 HTML 網站加入 Google 登入,或檢視既有的整合程式碼。

限制
僅限網頁:不含舊版 Google Sign-In、服務帳戶與 Android/iOS 原生。伺服器端範例為 Python,使用 Node、Go、Java 時需自行改寫。

說明與風險

低風險:純指令與範例程式碼檔,來自 Google 官方 Skills 儲存庫(Apache-2.0)。沒有腳本,自身不連網、不寫入檔案。你需要自己的 Google OAuth 用戶端 ID,範例只示範它放在哪裡。範例只是起點,並非經過稽核的程式碼:FastAPI 範例中的 nonce 只在帶有 nonce cookie 時才驗證,請在自己的程式碼中改為必須驗證;所有驗證相關程式碼上線前都應自行審查。

SKILL.md 原文套件內共 7 個檔案