使用代码模型

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

借助 Google Identity Services 库,用户可以使用基于浏览器的弹出式窗口或重定向用户体验流程从 Google 请求授权代码。这将启动安全的 OAuth 2.0 流程,并生成用于代表用户调用 Google API 的访问令牌。

OAuth 2.0 授权代码流摘要:

  • 在浏览器中,利用按钮点击等手势,Google 帐号所有者向 Google 请求授权代码。
  • Google 会做出响应,将唯一的授权代码发送到在用户浏览器中运行的 JavaScript Web 应用中的回调,或者使用浏览器重定向直接调用授权代码端点。
  • 后端平台托管授权代码端点并接收代码。验证后,此代码会通过向 Google 的令牌端点发出的请求,交换每位用户的访问权限并刷新令牌。
  • Google 会验证授权代码,确认来自安全平台的请求,颁发访问令牌和刷新令牌,并通过调用平台托管的登录端点返回令牌。
  • 您的登录端点会接收访问令牌和刷新令牌,从而安全地存储刷新令牌以备后用。

初始化代码客户端

google.accounts.oauth2.initCodeClient() 方法用于初始化代码客户端。

您可以选择使用重定向弹出式窗口模式的用户流共享身份验证代码。使用重定向模式时,您的服务器上会托管 OAuth2 授权端点,Google 会将用户代理重定向到此端点,并以网址参数的形式共享授权代码。对于弹出模式,您需要定义 JavaScript 回调处理程序,用于将授权代码发送到您的服务器。弹出式窗口模式可用于提供顺畅的用户体验,无需访问者离开您的网站。

如需为以下各项初始化客户端,请执行以下操作:

  • 重定向用户体验流程,将 ux_mode 设置为 redirect,并将 redirect_uri 的值设置为您平台的授权代码端点。该值必须与您在 API 控制台中配置的 OAuth 2.0 客户端的某个已获授权的重定向 URI 完全匹配。它还必须遵守我们的重定向 URI 验证规则

  • 弹出用户体验流程,将 ux_mode 设置为 popup,并将 callback 的值设为用于向平台发送授权代码的函数的名称。

防范 CSRF 攻击

为了防止跨网站请求伪造 (CSRF) 攻击,我们在重定向和弹出模式用户体验流程中采用了略有不同的技术。对于重定向模式,系统会使用 OAuth 2.0 state 参数。如需详细了解如何生成和验证 state 参数,请参阅 RFC6749 第 10.12 节:跨站请求伪造。使用弹出式窗口模式时,您可以为请求添加自定义 HTTP 标头,然后在服务器上确认它与预期值和来源匹配。

选择用户体验模式,查看显示身份验证代码和 CSRF 处理方式的代码段:

重定向模式

初始化一个客户端,其中 Google 将用户的浏览器重定向到您的身份验证端点,将身份验证代码作为网址参数共享。

const client = google.accounts.oauth2.initCodeClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
  ux_mode: 'redirect',
  redirect_uri: "https://your.domain/code_callback_endpoint",
  state: "YOUR_BINDING_VALUE"
});

初始化一个客户端,其中用户的浏览器从 Google 接收身份验证代码并将其发送到您的服务器。

const client = google.accounts.oauth2.initCodeClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
  ux_mode: 'popup',
  callback: (response) => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', code_receiver_uri, true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    // Set custom header for CRSF
    xhr.setRequestHeader('X-Requested-With', 'XmlHttpRequest');
    xhr.onload = function() {
      console.log('Auth code response: ' + xhr.responseText);
    };
    xhr.send('code=' + response.code);
  },
});

触发 OAuth 2.0 代码流

调用代码客户端的 requestCode() 方法以触发用户流:

<button onclick="client.requestCode();">Authorize with Google</button>

这将要求用户登录 Google 帐号,并同意在将授权代码返回到重定向端点或回调处理程序之前共享各个范围。

Auth 代码处理

Google 会为您生成一个唯一的用户授权代码,您在后端服务器上接收并验证这些代码。

对于弹出模式,用户浏览器中运行的 callback 指定的处理程序将授权代码中继到平台托管的端点。

对于重定向模式,系统会将 GET 请求发送到 redirect_url 指定的端点,并在网址 code 参数中共享授权代码。如需接收授权代码,请执行以下操作:

  • 如果您还没有实现,请创建新的授权端点,或者

  • 更新您的现有端点以接受 GET 请求和网址参数。 以前,我们在载荷中使用了包含授权代码值的 PUT 请求。

授权端点

您的授权代码端点必须使用以下网址查询字符串参数处理 GET 请求:

名称
身份验证用户 请求用户登录身份验证
代码 Google 生成的 OAuth2 授权代码
高清 用户帐号的托管网域
提示符 用户意见征求对话框
范围 要授权的一个或多个 OAuth2 范围用空格分隔
state CRSF 状态变量

包含网址参数的 GET 请求示例,端点为 auth-code,由 example.com 托管:

Request URL: https://www.example.com/auth-code?state=42a7bd822fe32cc56&code=4/0AX4XfWiAvnXLqxlckFUVao8j0zvZUJ06AMgr-n0vSPotHWcn9p-zHCjqwr47KHS_vDvu8w&scope=email%20profile%20https://www.googleapis.com/auth/calendar.readonly%20https://www.googleapis.com/auth/photoslibrary.readonly%20https://www.googleapis.com/auth/contacts.readonly%20openid%20https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile&authuser=0&hd=example.com&prompt=consent

如果授权代码流由早期的 JavaScript 库启动,或直接调用 Google OAuth 2.0 端点,系统会使用 POST 请求。

在 HTTP 请求正文中包含授权代码作为载荷的 POST 请求示例:

  Request URL: https://www.example.com/auth-code
  Request Payload:
  4/0AX4XfWhll-BMV82wi4YwbrSaTPaRpUGpKqJ4zBxQldU\_70cnIdh-GJOBZlyHU3MNcz4qaw

验证请求

在您的服务器上,执行以下操作以帮助避免 CSRF 攻击。

针对重定向模式,检查 state 的值。

确认已为标头模式设置了 X-Requested-With: XmlHttpRequest 标头。

只有先成功验证了授权代码请求后,您才能从 Google 获取刷新令牌和访问令牌。

获取访问令牌和刷新令牌

在后端平台收到 Google 的授权代码并验证请求后,请使用授权代码从 Google 获取访问权限并刷新令牌以进行 API 调用。

按照使用 OAuth 2.0 针对网络服务器应用指南的第 5 步:交换授权代码和刷新访问令牌中的相关说明操作。

管理令牌

您的平台会安全地存储刷新令牌。您可以在移除用户帐号时删除存储的刷新令牌,也可以通过 google.accounts.oauth2.revoke 或直接从 https://myaccount.google.com/permissions 撤消用户意见征求。

或者,您可以考虑使用 RISC 通过跨帐号保护功能保护用户帐号

通常,您的后端平台会使用访问令牌调用 Google API。如果您的 Web 应用还会直接从用户的浏览器调用 Google API,您必须实现一种与 Web 应用共享访问令牌的方法,但这不在本指南的讨论范围内。如果使用此方法以及使用适用于 JavaScript 的 Google API 客户端库,请使用 gapi.client.SetToken() 将访问令牌临时存储在浏览器内存中,并启用以下库来调用 Google API。