关于 sign-in-with-google-web
sign-in-with-google-web 是指导使用 Google Identity Services Web SDK 安全集成 Sign In With Google 的 Agent 技能。内容涵盖后端中介与纯浏览器架构、安全策略、会话处理及 Web 应用实现模式。
主要功能
- 01比较 BFF、令牌中介和纯浏览器模式
- 02说明 ID 令牌验证与会话处理
- 03列出 CSP、COOP 和 iframe 策略
- 04指导在 SPA 中加载 GIS 脚本
- 05涵盖登出和自动登录行为
适用场景
- 01为 Web 应用选择身份验证架构
- 02实现服务器端 ID 令牌验证
- 03配置 GIS 所需的浏览器安全策略
- 04为静态 SPA 或离线 PWA 构建登录流程
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ google/ skills --skill sign-in-with-google-web安装后在 Claude Code 中输入/sign-in-with-google-web确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/bff_fastapi_verification.mdin_memory_spa_nonce.mdintermediate_iframe.mdoffline_pwa_encryption.mdtmb_login_uri_redirect.md详细介绍
使用 Google 登录(SiwG)集成与安全架构
本技能提供规范性的架构指南、安全实现契约和故障预防协议,用于通过 Google Identity Services(GIS)Web SDK
(https://accounts.google.com/gsi/client)集成使用 Google 登录。
1. IETF 架构分类与层级(优先推荐 BFF / TMB)
所有使用 Google 登录的实现都 MUST 符合
IETF《基于浏览器的应用程序 OAuth 2.0》(draft-ietf-oauth-browser-based-apps)
中的分类法。
重要
首要建议: 只要应用有后端服务,就始终优先推荐后端中介模式(模式 A:前端后端(BFF)或模式 B:令牌中介后端 / 重定向模式),因为它们是最安全、最可靠且符合行业标准的架构。仅当受到无服务器静态托管或离线优先 PWA 需求的限制时,才退而采用仅客户端浏览器模式(模式 C 和 D)。
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ 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 │
└──────────────────────────┴──────────────────────────┴───────────────────────────────────┘
🏆 模式 A:前端后端(BFF - IETF § 6.1)——强烈推荐
-
为何这是黄金标准:页面重新加载期间,ID 令牌(JWT)和访问令牌绝不会暴露给浏览器 JavaScript。这使架构能够完全免受 XSS 令牌窃取攻击。
-
架构:浏览器前端加载 GIS SDK,通过 JavaScript 回调(
callback: handleCredentialResponse)捕获凭据,并立即通过fetch('/api/auth/google', { method: 'POST' })将 ID 令牌(JWT)转发到后端。 -
后端职责:后端依据 Google 的公共 JWK 验证 JWT 加密签名 (
google.oauth2.id_token.verify_oauth2_token),根据服务器会话状态检查aud、hd和nonce声明,创建服务器会话,并签发第一方HttpOnly; Secure; SameSite=Lax会话 Cookie。
🚀 模式 B:通过 login_uri 重定向的令牌中介后端(IETF § 6.2)——服务端渲染与表单 POST
-
为何安全性极高:通过指示 GIS 直接向后端的
login_uri执行 HTTP POST,完全绕过客户端 JavaScript 对凭据的处理。 -
架构:通过
google.accounts.id.initialize({ client_id, login_uri: "https://example.com/api/auth/callback", ux_mode: "redirect" })或 HTML 属性(data-login_uri="https://example.com/api/auth/callback"和data-ux_mode="redirect")进行配置。 -
后端职责:后端接收表单 POST 请求正文中的 ID 令牌(
credential参数),验证双重提交的g_csrf_tokenCookie 与 POST 请求正文中的g_csrf_token字段相符,在服务端以加密方式验证 ID 令牌,建立会话 Cookie,并返回标准 HTTP 302/303 重定向。 -
完整实现请参阅 references/tmb_login_uri_redirect.md。
模式 C:基于浏览器的 OAuth 客户端 — 临时内存存储(IETF § 6.3)— 静态 SPA 的回退方案
-
适用范围:仅在部署严格限定为无服务器/静态部署(例如 GitHub Pages、Firebase 静态托管),且不包含后端组件时使用。
-
存储原则:在当前标签页会话期间,ID 令牌严格保存在私有 JavaScript 内存/闭包中。切勿将原始令牌存储在
localStorage或sessionStorage中。 -
会话续期:使用 GIS One Tap / FedCM 自动选择(
auto_select: true),在页面重新加载时透明地重新获取新的 ID 令牌并存入内存,不使用持久化浏览器存储。 -
XSS 和重放保护:通过
window.crypto.getRandomValues()生成加密nonce,并将其直接传递给google.accounts.id.initialize({ client_id, nonce: clientNonce })。在信任内存中的声明之前,验证payload.nonce === clientNonce。
模式 D:基于浏览器的 OAuth 客户端 — 使用 WebCrypto 加密的 IndexedDB(IETF § 6.3)— 离线 PWA 的回退方案
-
适用范围:仅用于离线优先的渐进式 Web 应用(PWA),此类应用要求用户身份验证凭据在断网时仍能经受标签页刷新。
-
存储原则:切勿将未加密的原始 ID 令牌存储在
localStorage中。在将密文和 IV 写入IndexedDB之前,使用不可导出的会话密钥(extractable: false)通过 WebCryptoAES-GCM加密 ID 令牌载荷。
2. 强制安全指令和浏览器策略
为避免多轮修复循环和浏览器安全异常,请在初始交付内容中提供所有必需的 CSP、COOP 和用户激活指令:
A. 内容安全策略(CSP)指令
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/;
如果使用严格的基于 nonce 的 CSP,请将 nonce 附加到 GIS 脚本标签:
<script src="https://accounts.google.com/gsi/client" async defer nonce="{{NONCE}}"></script>。
B. 跨源打开器策略(COOP)
要允许 GIS 弹出对话框通过 postMessage 将凭据传回父窗口:
Cross-Origin-Opener-Policy: same-origin-allow-popups
(使用 same-origin 提供服务而不设置 allow-popups,会导致 GIS 弹出窗口失效并出现静默失败)。
C. 自动选择、FedCM 和登出生命周期(disableAutoSelect)
启用 Google One Tap 自动零点击返回登录时(GIS 默认启用 FedCM):
-
在
google.accounts.id.initialize({...})中设置auto_select: true(不要传入已弃用的use_fedcm_for_prompt参数)。 -
已弃用库警告:切勿将已弃用的
gapi.auth2(gapi.auth2.init、gapi.auth2.getAuthInstance().signOut())与 Google Identity Services(GIS)混用。gapi.auth2已彻底退役;请仅使用 GIS 方法。 -
登出协议:用户明确退出应用时,必须调用
google.accounts.id.disableAutoSelect():function handleUserLogout() { // 1. 禁用 One Tap / FedCM 自动重新验证 google.accounts.id.disableAutoSelect(); // 2. 清除应用会话状态 / 服务端 Cookie fetch('/api/auth/logout', { method: 'POST' }).then(() => { window.location.href = '/login'; }); } -
(如果未调用
disableAutoSelect(),用户主动登出后下次访问页面时会立即陷入自动重新登录循环)。
D. 可靠的动态脚本加载与框架生命周期(React、Next.js、SPA)
在单页应用(React、Next.js、Vue、Angular)中挂载 Google 登录时:
-
现代 SPA 会异步执行组件生命周期。在
<script src="https://accounts.google.com/gsi/client">加载完成前尝试访问window.google.accounts.id,会导致ReferenceError: google is not defined。 -
绝不要使用
gapi或轮询(setInterval):使用确定性的动态脚本加载,并通过onload/addEventListener('load')事件监听器处理。 -
动态脚本加载器模式:
import React, { useEffect, useState } from 'react'; export function useGoogleIdentityScript() { const [isLoaded, setIsLoaded] = useState(false); useEffect(() => { if (window.google?.accounts?.id) { setIsLoaded(true); return; } const existingScript = document.querySelector( 'script[src="https://accounts.google.com/gsi/client"]' ); if (existingScript) { existingScript.addEventListener('load', () => setIsLoaded(true)); return; } const script = document.createElement('script'); script.src = 'https://accounts.google.com/gsi/client'; script.async = true; script.defer = true; script.onload = () => setIsLoaded(true); document.head.appendChild(script); }, []); return isLoaded; } -
(始终使用确定性的
onload事件监听器或框架的<Script strategy="afterInteractive">,而不是脆弱的setInterval轮询。)
E. 嵌入式 Iframe 与权限策略(identity-credentials-get)
在跨源 <iframe> 元素或小组件集成中嵌入 Google 登录或 One Tap 时,现代浏览器安全模型和 FedCM 要求在容器框架上显式委派权限策略:
<iframe
src="https://example.com/embed"
allow="identity-credentials-get 'src' https://accounts.google.com">
</iframe>
(省略 allow="identity-credentials-get" 会阻止 FedCM / One Tap 在嵌入式或跨源环境中初始化。) 完整集成指南请参阅 references/intermediate_iframe.md。
3. 实现约定与反模式禁令
A. 严格禁止使用旧版 GAPI(gapi.auth2)
-
绝不要导入或引用
gapi.auth2、gapi.auth2.init、gapi.auth2.getAuthInstance()或gapi.signin2。始终提醒gapi.auth2已弃用并停止服务。 -
GIS(
google.accounts.id.*和google.accounts.oauth2.*)完全无状态,取代了所有旧版 GAPI 身份验证库。
4. 实现方案(渐进式披露)
生成实现代码时,请加载与目标架构匹配的特定参考文件:
-
方案 1 — 模式 A(使用 Python / FastAPI 的前端后端模式 [推荐]):加载 references/bff_fastapi_verification.md, 了解自定义 UI 按钮提示、
google.oauth2.id_token.verify_oauth2_token加密验证、nonce重放检查、Google Workspacehd域名强制执行,以及HttpOnly; Secure; SameSite=Lax会话 Cookie 的签发。 -
方案 2 — 模式 B(使用
login_uri表单 POST 重定向的令牌中介后端 [推荐]):加载 references/tmb_login_uri_redirect.md, 了解data-login_uri/ux_mode: 'redirect'前端设置,以及 FastAPI 双重提交g_csrf_tokenCookie 与表单正文验证。 -
方案 3 — 模式 C(基于浏览器的临时内存 SPA,使用原生 GIS Nonce [回退方案]):加载 references/in_memory_spa_nonce.md,了解 WebCrypto
nonce生成(google.accounts.id.initialize({ client_id, nonce: clientNonce }))、UTF-8 安全的 Base64URL JWT 解码,以及私有 闭包令牌管理。 -
方案 4 — 模式 D(基于浏览器的 WebCrypto 加密 IndexedDB,适用于 离线 PWA [回退方案]):加载 references/offline_pwa_encryption.md, 了解不可提取(
extractable: false)的AES-GCMCryptoKey生成 及加密的IndexedDB存储。 -
方案 5 — 嵌入式跨源上下文(
gsi/intermediate):加载 references/intermediate_iframe.md,了解<div id="g_id_intermediate_iframe">、allow="identity-credentials-get", 以及严格的window.postMessage来源验证。
5. 所有模式下的登出与账号撤销
export function handleSignOut(userEmail) {
// 1. Disable client-side One Tap auto-selection
google.accounts.id.disableAutoSelect();
// 2. Revoke OAuth grant if user disconnected account
if (userEmail) {
google.accounts.id.revoke(userEmail, () => {
console.log('User grant revoked');
});
}
// 3. Clear storage / session
AuthManager.clear(); // Pattern C
fetch('/api/auth/logout', { method: 'POST' }); // Pattern A
}
6. 参考资料与规范性标准
-
RFC 7519:JSON Web Token(JWT) — 标准声明(
iss、sub、aud、exp、iat、nonce)、格式及处理规则。 -
RFC 7515:JSON Web Signature(JWS) — 针对 Google 公共 JWK 证书 (
https://www.googleapis.com/oauth2/v3/certs)进行加密签名验证。 -
RFC 4648 § 5:Base64url 编码 — JWT 各段中使用的无填充 URL 安全 Base64 编码。
-
IETF OAuth 2.0 基于浏览器的应用 — 规范性架构标准(§ 6.1 BFF、§ 6.2 TMB、§ 6.3 客户端)。
-
Google Identity Services Web 参考 — 官方 GIS API 参考。
-
中间 iframe API 参考 — 跨源 iframe 嵌入指南。
-
在服务端验证 ID 令牌 — 服务端令牌验证协议。
-
W3C 联合凭据管理 API(FedCM) — 浏览器身份凭据中介标准。
-
W3C Web Cryptography API — 不可提取密钥生成(
extractable: false)和 AES-GCM 标准。