用于客户端 Web 应用程序的 OAuth 2.0

本文档介绍了如何实施 OAuth 2.0 授权以从 JavaScript Web 应用程序访问 Google API。 OAuth 2.0 允许用户与应用程序共享特定数据,同时将他们的用户名、密码和其他信息保密。例如,应用程序可以使用 OAuth 2.0 获得用户的许可,以便将文件存储在其 Google 云端硬盘中。

这OAuth 2.0用户流量被称为隐性补助流。它专为仅当用户在应用程序中时访问 API 的应用程序而设计。这些应用程序无法存储机密信息。

在此流程中,您的应用程序打开一个 Google URL,该 URL 使用查询参数来标识您的应用程序和应用程序所需的 API 访问类型。您可以在当前浏览器窗口或弹出窗口中打开 URL。用户可以通过 Google 进行身份验证并授予请求的权限。然后 Google 会将用户重定向回您的应用。重定向包括一个访问令牌,您的应用程序会验证该令牌,然后使用它来发出 API 请求。

先决条件

为您的项目启用 API

调用谷歌API的应用程序需要能够在这些API API Console。

要为您的项目启用 API:

  1. Open the API Library 在 Google API Console。
  2. If prompted, select a project, or create a new one.
  3. 在 API Library 列出了所有可用的API,按产品系列和普及分组。如果要启用API不在列表中可见,用搜索找到它,或者点击查看全部的产品系列属于。
  4. 选择您要启用的API,然后点击启用按钮。
  5. If prompted, enable billing.
  6. If prompted, read and accept the API's Terms of Service.

创建授权凭证

任何使用 OAuth 2.0 访问 Google API 的应用程序都必须具有向 Google 的 OAuth 2.0 服务器标识应用程序的授权凭据。以下步骤说明了如何为您的项目创建凭据。然后,您的应用程序可以使用凭据访问您为该项目启用的 API。

  1. Go to the Credentials page.
  2. 单击创建证书> OAuth用户端ID。
  3. 选择Web应用程序的应用程序类型。
  4. 完成表格。使用JavaScript的应用程序,使谷歌授权的API请求都必须指定授权的JavaScript源。源标识您的应用程序可以向 OAuth 2.0 服务器发送请求的域。这些来源必须符合谷歌的验证规则

确定访问范围

范围使您的应用程序能够仅请求访问它需要的资源,同时还使用户能够控制他们授予您的应用程序的访问权限。因此,请求的范围数量与获得用户同意的可能性之间可能存在反比关系。

在开始实施 OAuth 2.0 授权之前,我们建议您确定您的应用需要访问权限的范围。

的OAuth 2.0 API范围文档包含范围,您可以使用访问谷歌的API的完整列表。

获取 OAuth 2.0 访问令牌

以下步骤展示了您的应用程序如何与 Google 的 OAuth 2.0 服务器交互以获取用户的同意以代表用户执行 API 请求。您的应用程序必须先获得该同意,然后才能执行需要用户授权的 Google API 请求。

第一步:配置客户端对象

如果您使用的是谷歌的API客户端JavaScript库来处理OAuth 2.0流程,第一步是配置gapi.auth2gapi.client对象。这些对象使您的应用程序能够获得用户授权并发出授权的 API 请求。

客户端对象标识您的应用程序请求访问权限的范围。这些值通知 Google 向用户显示的同意屏幕。

JS 客户端库

JavaScript 客户端库简化了授权过程的许多方面:

  1. 它为 Google 的授权服务器创建重定向 URL,并提供一种将用户定向到该 URL 的方法。
  2. 它处理从该服务器重定向回您的应用程序。
  3. 它验证授权服务器返回的访问令牌。
  4. 它存储授权服务器发送给您的应用程序的访问令牌,并在您的应用程序随后进行授权 API 调用时检索它。

下面的代码段是从一个摘录完整的例子稍后在本文档中示出。此代码初始化gapi.client对象,你的应用程序将在以后使用来进行API调用。当创建对象时, gapi.auth2对象,你的应用程序使用,检查和监控用户的授权状态,也被初始化。

要将呼叫gapi.client.init指定以下字段:

  • apiKeyclientId值指定应用程序的授权证书。正如所讨论的创建授权凭证部分,可以在能够获得这些值 API Console。请注意, clientId ,如果你的应用程序进行授权的API请求是必需的。仅发出未经授权的请求的应用程序只能指定 API 密钥。
  • scope字段指定的空格分隔列表访问作用域相对应的资源,你的应用程序可以代表用户的访问。这些值通知 Google 向用户显示的同意屏幕。

    我们建议您的应用程序尽可能请求访问上下文中的授权范围。通过请求访问用户数据的情况下,通过增量授权,你帮助用户更容易理解为什么您的应用程序需要被请求的访问。

  • discoveryDocs字段标识列表API发现的文件,你的应用程序使用。 Discovery 文档描述了 API 的表面,包括其资源模式,JavaScript 客户端库使用该信息来生成应用程序可以使用的方法。在此示例中,代码检索 Google Drive API 版本 3 的发现文档。

gapi.client.init调用完成,该代码将GoogleAuth变量来标识谷歌验证对象。最后,代码设置了一个监听器,当用户的登录状态发生变化时调用函数。 (该函数未在代码段中定义。)

var GoogleAuth; // Google Auth object.
function initClient() {
  gapi.client.init({
      'apiKey': 'YOUR_API_KEY',
      'clientId': 'YOUR_CLIENT_ID',
      'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
      'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest']
  }).then(function () {
      GoogleAuth = gapi.auth2.getAuthInstance();

      // Listen for sign-in state changes.
      GoogleAuth.isSignedIn.listen(updateSigninStatus);
  });
}

OAuth 2.0 端点

如果您直接访问 OAuth 2.0 端点,则可以继续下一步。

第 2 步:重定向到 Google 的 OAuth 2.0 服务器

要请求访问用户数据的权限,请将用户重定向到 Google 的 OAuth 2.0 服务器。

JS 客户端库

调用GoogleAuth.signIn()方法来引导用户到谷歌的授权服务器。

GoogleAuth.signIn();

在实践中,你的应用程序可能会设置一个布尔值,以确定是否调用signIn()试图进行API调用之前的方法。

下面的代码片段演示了如何启动用户授权流程。请注意有关代码段的以下几点:

  • GoogleAuth在代码引用的对象是相同的,在代码段中定义的全局变量的步骤1

  • updateSigninStatus功能是侦听更改用户的授权状态的监听器。其作为监听的作用,在代码段也被定义在第1步:
    GoogleAuth.isSignedIn.listen(updateSigninStatus);
  • 该代码段定义了两个额外的全局变量:

    • isAuthorized是一个布尔变量,指示用户是否已经登录,该值可以设置应用程序加载时和更新,如果在应用程序或从用户的迹象。

      在该片段中, sendAuthorizedApiRequest函数检查变量的值,以确定该应用程序是否应尝试需要授权的或提示用户授权的应用程序的API请求。

    • currentApiRequest是一个对象,关于最后的API请求的详细信息存储在用户尝试。当应用程序调用该对象的值设置sendAuthorizedApiRequest功能。

      如果用户已授权该应用程序,则会立即执行该请求。否则,该功能将用户重定向到登录。在用户登录后, updateSignInStatus函数调用sendAuthorizedApiRequest ,通过在授权流程开始之前尝试了同样的要求。

var isAuthorized;
var currentApiRequest;

/**
 * Store the request details. Then check to determine whether the user
 * has authorized the application.
 *   - If the user has granted access, make the API request.
 *   - If the user has not granted access, initiate the sign-in flow.
 */
function sendAuthorizedApiRequest(requestDetails) {
  currentApiRequest = requestDetails;
  if (isAuthorized) {
    // Make API request
    // gapi.client.request(requestDetails)

    // Reset currentApiRequest variable.
    currentApiRequest = {};
  } else {
    GoogleAuth.signIn();
  }
}

/**
 * Listener called when user completes auth flow. If the currentApiRequest
 * variable is set, then the user was prompted to authorize the application
 * before the request executed. In that case, proceed with that API request.
 */
function updateSigninStatus(isSignedIn) {
  if (isSignedIn) {
    isAuthorized = true;
    if (currentApiRequest) {
      sendAuthorizedApiRequest(currentApiRequest);
    }
  } else {
    isAuthorized = false;
  }
}

OAuth 2.0 端点

生成一个URL,从谷歌的OAuth 2.0端点在请求访问https://accounts.google.com/o/oauth2/v2/auth 。此端点可通过 HTTPS 访问;拒绝普通的 HTTP 连接。

Google 授权服务器支持 Web 服务器应用程序的以下查询字符串参数:

参数
client_id必需的

您的应用程序的客户端 ID。你可以找到在这个值 API ConsoleCredentials page

redirect_uri必需的

确定用户完成授权流程后 API 服务器将用户重定向到何处。该值必须完全匹配授权的重定向的URI的OAuth 2.0用户端,您可以在您的客户机的配置的一个 API ConsoleCredentials page。如果此值不为所提供的匹配授权的重新导向URI client_id你会得到一个redirect_uri_mismatch错误。

注意, httphttps方案,案例,和斜线(“ / ”)必须全部匹配。

response_type必需的

JavaScript应用程序需要的参数的值设置为token 。此值指示谷歌授权服务器返回令牌作为所述接入name=value在URI(的片段标识符对# )该用户正在完成授权过程之后重新定向。

scope必需的

一个以空格分隔的范围列表,用于标识您的应用程序可以代表用户访问的资源。这些值通知 Google 向用户显示的同意屏幕。

范围使您的应用程序能够仅请求访问它需要的资源,同时还使用户能够控制他们授予您的应用程序的访问权限。因此,请求的范围数量与获得用户同意的可能性之间存在反比关系。

我们建议您的应用程序尽可能请求访问上下文中的授权范围。通过请求访问用户数据的情况下,通过增量授权,你帮助用户更容易理解为什么您的应用程序需要被请求的访问。

state受到推崇的

指定您的应用程序用于维护授权请求和授权服务器响应之间的状态的任何字符串值。服务器返回您发送的精确值name=value对的URL片段标识符( #中的) redirect_uri用户同意后,或拒绝您的应用程序的访问请求。

您可以将此参数用于多种用途,例如将用户定向到应用程序中的正确资源、发送随机数以及减少跨站点请求伪造。由于您的redirect_uri可以猜到,使用state值可以增加你保证传入连接的认证请求的结果。如果您生成随机字符串或对 cookie 的哈希值或捕获客户端状态的其他值进行编码,则可以验证响应以额外确保请求和响应源自同一浏览器,从而防止跨站点等攻击请求伪造。见ID连接文档中如何创建并确认一个例子state令牌。

include_granted_scopes可选的

使应用程序能够使用增量授权来请求访问上下文中的其他范围。如果这个参数的值设置为true和授权请求被批准,那么新的访问令牌也将覆盖该用户以前授予的应用程序访问的任何范围。见增量授权的示例部分。

login_hint可选的

如果您的应用程序知道哪个用户正在尝试进行身份验证,它可以使用此参数向 Google 身份验证服务器提供提示。服务器通过在登录表单中预填电子邮件字段或选择适当的多登录会话,使用提示来简化登录流程。

设置参数值到电子邮件地址或sub标识符,这相当于用户的谷歌ID。

prompt可选的

以空格分隔、区分大小写的提示列表,以呈现给用户。如果您不指定此参数,则仅在您的项目第一次请求访问时才会提示用户。见提示重新同意,以获取更多信息。

可能的值为:

none不显示任何身份验证或同意屏幕。不得与其他值一起指定。
consent提示用户同意。
select_account提示用户选择一个帐户。

重定向到 Google 授权服务器的示例

下面显示了一个示例 URL,其中包含换行符和空格以提高可读性。

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly&
 include_granted_scopes=true&
 response_type=token&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//oauth2.example.com/code&
 client_id=client_id

创建请求 URL 后,将用户重定向到它。

JavaScript 示例代码

以下 JavaScript 代码段展示了如何在不使用 JavaScript 的 Google API 客户端库的情况下在 JavaScript 中启动授权流程。由于此 OAuth 2.0 端点不支持跨域资源共享 (CORS),因此该代码段创建了一个表单,用于打开对该端点的请求。

/*
 * Create form to request access token from Google's OAuth 2.0 server.
 */
function oauthSignIn() {
  // Google's OAuth 2.0 endpoint for requesting an access token
  var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';

  // Create <form> element to submit parameters to OAuth 2.0 endpoint.
  var form = document.createElement('form');
  form.setAttribute('method', 'GET'); // Send as a GET request.
  form.setAttribute('action', oauth2Endpoint);

  // Parameters to pass to OAuth 2.0 endpoint.
  var params = {'client_id': 'YOUR_CLIENT_ID',
                'redirect_uri': 'YOUR_REDIRECT_URI',
                'response_type': 'token',
                'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
                'include_granted_scopes': 'true',
                'state': 'pass-through value'};

  // Add form parameters as hidden input values.
  for (var p in params) {
    var input = document.createElement('input');
    input.setAttribute('type', 'hidden');
    input.setAttribute('name', p);
    input.setAttribute('value', params[p]);
    form.appendChild(input);
  }

  // Add form to page and submit it to open the OAuth 2.0 endpoint.
  document.body.appendChild(form);
  form.submit();
}

第 3 步:Google 提示用户同意

在此步骤中,用户决定是否授予您的应用程序请求的访问权限。在此阶段,Google 会显示一个同意窗口,其中显示您的应用程序的名称以及它请求使用用户授权凭据访问的 Google API 服务以及要授予的访问权限范围的摘要。然后,用户可以同意授予对您的应用程序请求的一个或多个范围的访问权限或拒绝该请求。

您的应用程序在此阶段无需执行任何操作,因为它会等待来自 Google 的 OAuth 2.0 服务器的响应,指示是否授予任何访问权限。该响应将在以下步骤中进行解释。

错误

对 Google 的 OAuth 2.0 授权端点的请求可能会显示面向用户的错误消息,而不是预期的身份验证和授权流程。下面列出了常见的错误代码和建议的解决方法。

admin_policy_enforced

由于 Google Workspace 管理员的政策,Google 帐户无法授权一个或多个请求的范围。看到谷歌工作区管理员说明文章控制哪些第三方和内部应用程序访问谷歌工作区数据有关,直到访问被明确授予您的OAuth用户端ID管理员可以如何限制对所有范围或敏感和受限制的作用域的详细信息。

disallowed_useragent

授权端点是由谷歌的不允许的嵌入式用户代理内部显示的OAuth 2.0政策

安卓

在打开的授权请求时,Android开发可能会遇到此错误消息android.webkit.WebView 。开发者应该使用的Android库,如谷歌登录Android版或OpenID基金会的AppAuth未为Android

当 Android 应用程序在嵌入式用户代理中打开通用 Web 链接并且用户从您的站点导航到 Google 的 OAuth 2.0 授权端点时,Web 开发人员可能会遇到此错误。开发者应该允许一般链接到操作系统,其中包括默认链路处理器打开Android应用程序链接的处理程序或默认浏览器应用程序。在Android的自定义选项卡库也是支持的选项。

IOS

在打开的授权请求时,iOS和MacOS的开发商可能会遇到这个错误WKWebView 。开发者应该使用的iOS库,如谷歌登录在为iOS或OpenID基金会的AppAuth未适用于iOS

当 iOS 或 macOS 应用程序在嵌入式用户代理中打开通用 Web 链接并且用户从您的站点导航到 Google 的 OAuth 2.0 授权端点时,Web 开发人员可能会遇到此错误。开发者应该允许一般链接到操作系统,其中包括默认链路处理器打开通用链接处理程序或默认浏览器应用程序。该SFSafariViewController库也是支持的选项。

org_internal

在请求的OAuth用户端ID是项目限制在一个特定的访问谷歌帐户的一部分,谷歌云组织。有关此配置选项的详细信息,请参阅用户类型的设置您的OAuth同意画面帮助文章节。

origin_mismatch

发起授权请求的 JavaScript 的方案、域和/或端口可能与为 OAuth 客户端 ID 注册的授权 JavaScript 源 URI 不匹配。审查授权的JavaScript源 Google API ConsoleCredentials page

redirect_uri_mismatch

redirect_uri在授权请求传递不匹配的OAuth用户端ID被授权的重定向URI。审查授权的重定向URI的 Google API Console Credentials page

发起授权请求的 JavaScript 的方案、域和/或端口可能与为 OAuth 客户端 ID 注册的授权 JavaScript 源 URI 不匹配。审查授权的JavaScript源 Google API Console Credentials page

步骤 4:处理 OAuth 2.0 服务器响应

JS 客户端库

JavaScript 客户端库处理来自 Google 授权服务器的响应。如果您设置侦听器来监视当前用户登录状态的更改,则在用户授予对应用程序的请求访问权限时调用该函数。

OAuth 2.0 端点

所述的OAuth 2.0服务器发送到一个响应redirect_uri在接入令牌请求中指定。

如果用户批准请求,则响应包含访问令牌。如果用户不批准请求,则响应包含错误消息。在重定向 URI 的哈希片段上返回访问令牌或错误消息,如下所示:

  • 访问令牌响应:

    https://oauth2.example.com/callback#access_token=4/P7q7W91&token_type=Bearer&expires_in=3600

    access_token参数,所述片段串还包含token_type参数,它始终设置为Bearer ,并且expires_in参数,其指定所述令牌的寿命,单位为秒。如果state在令牌请求中的访问被指定的参数,其值也被包括在响应中。

  • 错误回答:
    https://oauth2.example.com/callback#error=access_denied

OAuth 2.0 服务器响应示例

您可以通过单击以下示例 URL 来测试此流程,该示例 URL 请求只读访问权限以查看 Google Drive 中文件的元数据:

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly&
 include_granted_scopes=true&
 response_type=token&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//oauth2.example.com/code&
 client_id=client_id

在完成的OAuth 2.0流程后,您将被重定向到http://localhost/oauth2callback 。该URL将产生一个404 NOT FOUND除非你的本地计算机碰巧成为一个文件在该地址错误。下一步提供有关当用户重定向回您的应用程序时 URI 中返回的信息的更多详细信息。

调用 Google API

JS 客户端库

在您的应用程序获得访问令牌后,您可以使用 JavaScript 客户端库代表用户发出 API 请求。客户端库为您管理访问令牌,您无需执行任何特殊操作即可在请求中发送它。

客户端库支持两种调用 API 方法的方式。如果您加载了发现文档,API 将为您定义特定于方法的函数。您也可以使用gapi.client.request函数来调用的API方法。下面的两个片段演示这些选项的驱动器API的about.get方法。

// Example 1: Use method-specific function
var request = gapi.client.drive.about.get({'fields': 'user'});

// Execute the API request.
request.execute(function(response) {
  console.log(response);
});


// Example 2: Use gapi.client.request(args) function
var request = gapi.client.request({
  'method': 'GET',
  'path': '/drive/v3/about',
  'params': {'fields': 'user'}
});
// Execute the API request.
request.execute(function(response) {
  console.log(response);
});

OAuth 2.0 端点

在您的应用程序获得访问令牌后,如果已授予 API 所需的访问范围,您可以使用该令牌代表给定的用户帐户调用 Google API。要做到这一点,包括通过包括一个在请求令牌给API访问access_token查询参数或Authorization HTTP标头Bearer值。如果可能,最好使用 HTTP 标头,因为查询字符串往往在服务器日志中可见。在大多数情况下,你可以使用客户端库建立到谷歌的API您的来电(例如,当调用驱动器文件API )。

你可以尝试所有的谷歌API和查看他们的范围在的OAuth 2.0游乐场

HTTP GET 示例

在调用drive.files使用端点(驱动文件API) Authorization: Bearer HTTP标头看起来像下面这样。请注意,您需要指定自己的访问令牌:

GET /drive/v2/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

下面是使用经过验证的用户相同的API调用access_token查询字符串参数:

GET https://www.googleapis.com/drive/v2/files?access_token=access_token

curl的例子

您可以测试与这些命令curl命令行应用程序。下面是一个使用 HTTP 标头选项(首选)的示例:

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files

或者,查询字符串参数选项:

curl https://www.googleapis.com/drive/v2/files?access_token=access_token

JavaScript 示例代码

下面的代码片段演示了如何使用 CORS(跨源资源共享)向 Google API 发送请求。此示例不使用适用于 JavaScript 的 Google API 客户端库。但是,即使你不使用的客户端库中, CORS支持该库的文档中的指南将帮助可能你更好地理解这些要求。

在此代码片段中, access_token变量表示令牌您获得使代表授权用户的API请求。在完整的例子演示了如何才能将令牌存储在浏览器的本地存储和检索它使一个API请求时。

var xhr = new XMLHttpRequest();
xhr.open('GET',
    'https://www.googleapis.com/drive/v3/about?fields=user&' +
    'access_token=' + params['access_token']);
xhr.onreadystatechange = function (e) {
  console.log(xhr.response);
};
xhr.send(null);

完整示例

JS 客户端库

示例代码演示

本节包含以下代码示例的工作演示,以演示代码在实际应用程序中的行为。您授权的应用程序后,将在其中列出连接到您的谷歌帐户的应用程序。这款应用程序名为OAuth 2.0用户演示了谷歌API文档。同样,如果您撤销访问权限并刷新该页面,则该应用程序将不再列出。

请注意,这个应用程序请求的访问https://www.googleapis.com/auth/drive.metadata.readonly范围。请求访问权限仅用于演示如何在 JavaScript 应用程序中启动 OAuth 2.0 流程。此应用程序不会发出任何 API 请求。

JavaScript 示例代码

如上所示,此代码示例适用于加载适用于 JavaScript 的 Google API 客户端库并启动 OAuth 2.0 流程的页面(应用程序)。该页面显示:

  • 一个让用户登录应用程序的按钮。如果用户之前未授权应用程序,则应用程序将启动 OAuth 2.0 流程。
  • 两个按钮,允许用户退出应用程序或撤销先前授予应用程序的访问权限。如果您退出应用程序,则您并未撤销授予该应用程序的访问权限。您需要重新登录,然后该应用才能代表您发出其他授权请求,但您下次使用该应用时无需再次授予访问权限。但是,如果您撤消访问权限,则确实需要再次授予访问权限。

您也可以撤销通过访问应用程序的权限为您的谷歌帐户页面。该应用程序被列为OAuth 2.0用户演示了谷歌API文档

<script>
  var GoogleAuth;
  var SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly';
  function handleClientLoad() {
    // Load the API's client and auth2 modules.
    // Call the initClient function after the modules load.
    gapi.load('client:auth2', initClient);
  }

  function initClient() {
    // In practice, your app can retrieve one or more discovery documents.
    var discoveryUrl = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';

    // Initialize the gapi.client object, which app uses to make API requests.
    // Get API key and client ID from API Console.
    // 'scope' field specifies space-delimited list of access scopes.
    gapi.client.init({
        'apiKey': 'YOUR_API_KEY',
        'clientId': 'YOUR_CLIENT_ID',
        'discoveryDocs': [discoveryUrl],
        'scope': SCOPE
    }).then(function () {
      GoogleAuth = gapi.auth2.getAuthInstance();

      // Listen for sign-in state changes.
      GoogleAuth.isSignedIn.listen(updateSigninStatus);

      // Handle initial sign-in state. (Determine if user is already signed in.)
      var user = GoogleAuth.currentUser.get();
      setSigninStatus();

      // Call handleAuthClick function when user clicks on
      //      "Sign In/Authorize" button.
      $('#sign-in-or-out-button').click(function() {
        handleAuthClick();
      });
      $('#revoke-access-button').click(function() {
        revokeAccess();
      });
    });
  }

  function handleAuthClick() {
    if (GoogleAuth.isSignedIn.get()) {
      // User is authorized and has clicked "Sign out" button.
      GoogleAuth.signOut();
    } else {
      // User is not signed in. Start Google auth flow.
      GoogleAuth.signIn();
    }
  }

  function revokeAccess() {
    GoogleAuth.disconnect();
  }

  function setSigninStatus() {
    var user = GoogleAuth.currentUser.get();
    var isAuthorized = user.hasGrantedScopes(SCOPE);
    if (isAuthorized) {
      $('#sign-in-or-out-button').html('Sign out');
      $('#revoke-access-button').css('display', 'inline-block');
      $('#auth-status').html('You are currently signed in and have granted ' +
          'access to this app.');
    } else {
      $('#sign-in-or-out-button').html('Sign In/Authorize');
      $('#revoke-access-button').css('display', 'none');
      $('#auth-status').html('You have not authorized this app or you are ' +
          'signed out.');
    }
  }

  function updateSigninStatus() {
    setSigninStatus();
  }
</script>

<button id="sign-in-or-out-button"
        style="margin-left: 25px">Sign In/Authorize</button>
<button id="revoke-access-button"
        style="display: none; margin-left: 25px">Revoke access</button>

<div id="auth-status" style="display: inline; padding-left: 25px"></div><hr>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script async defer src="https://apis.google.com/js/api.js"
        onload="this.onload=function(){};handleClientLoad()"
        onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>

OAuth 2.0 端点

此代码示例演示了如何在不使用适用于 JavaScript 的 Google API 客户端库的情况下在 JavaScript 中完成 OAuth 2.0 流程。该代码用于显示用于尝试 API 请求的按钮的 HTML 页面。如果单击该按钮,代码会检查页面是否在浏览器的本地存储中存储了 API 访问令牌。如果是,它会执行 API 请求。否则,它会启动 OAuth 2.0 流程。

对于 OAuth 2.0 流程,该页面遵循以下步骤:

  1. 这将用户定向到谷歌的OAuth 2.0服务器,请求访问https://www.googleapis.com/auth/drive.metadata.readonly范围。
  2. 在授予(或拒绝)对一个或多个请求范围的访问权限后,用户将被重定向到原始页面,该页面从片段标识符字符串中解析访问令牌。
  3. 该页面使用访问令牌发出示例 API 请求。

    API请求调用驱动API的about.get方法来检索有关授权用户的谷歌云端硬盘帐户信息。

  4. 如果请求成功执行,API 响应将记录在浏览器的调试控制台中。

您可以撤消通过访问应用程序的权限为您的谷歌帐户页面。该应用程序将被列为OAuth 2.0用户演示了谷歌API文档

要在本地运行此代码,您需要设置值的YOUR_CLIENT_IDYOUR_REDIRECT_URI变量对应于你的授权凭证。该YOUR_REDIRECT_URI变量应设置到页面被服务的同一URL。该值必须完全匹配授权的重定向的URI的OAuth 2.0用户端,你的配置的一个 API Console Credentials page。如果此值不匹配授权的URI,你会得到一个redirect_uri_mismatch错误。您的项目还必须启用相应的API此请求。

<html><head></head><body>
<script>
  var YOUR_CLIENT_ID = 'REPLACE_THIS_VALUE';
  var YOUR_REDIRECT_URI = 'REPLACE_THIS_VALUE';
  var fragmentString = location.hash.substring(1);

  // Parse query string to see if page request is coming from OAuth 2.0 server.
  var params = {};
  var regex = /([^&=]+)=([^&]*)/g, m;
  while (m = regex.exec(fragmentString)) {
    params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
  }
  if (Object.keys(params).length > 0) {
    localStorage.setItem('oauth2-test-params', JSON.stringify(params) );
    if (params['state'] && params['state'] == 'try_sample_request') {
      trySampleRequest();
    }
  }

  // If there's an access token, try an API request.
  // Otherwise, start OAuth 2.0 flow.
  function trySampleRequest() {
    var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
    if (params && params['access_token']) {
      var xhr = new XMLHttpRequest();
      xhr.open('GET',
          'https://www.googleapis.com/drive/v3/about?fields=user&' +
          'access_token=' + params['access_token']);
      xhr.onreadystatechange = function (e) {
        if (xhr.readyState === 4 && xhr.status === 200) {
          console.log(xhr.response);
        } else if (xhr.readyState === 4 && xhr.status === 401) {
          // Token invalid, so prompt for user permission.
          oauth2SignIn();
        }
      };
      xhr.send(null);
    } else {
      oauth2SignIn();
    }
  }

  /*
   * Create form to request access token from Google's OAuth 2.0 server.
   */
  function oauth2SignIn() {
    // Google's OAuth 2.0 endpoint for requesting an access token
    var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';

    // Create element to open OAuth 2.0 endpoint in new window.
    var form = document.createElement('form');
    form.setAttribute('method', 'GET'); // Send as a GET request.
    form.setAttribute('action', oauth2Endpoint);

    // Parameters to pass to OAuth 2.0 endpoint.
    var params = {'client_id': YOUR_CLIENT_ID,
                  'redirect_uri': YOUR_REDIRECT_URI,
                  'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
                  'state': 'try_sample_request',
                  'include_granted_scopes': 'true',
                  'response_type': 'token'};

    // Add form parameters as hidden input values.
    for (var p in params) {
      var input = document.createElement('input');
      input.setAttribute('type', 'hidden');
      input.setAttribute('name', p);
      input.setAttribute('value', params[p]);
      form.appendChild(input);
    }

    // Add form to page and submit it to open the OAuth 2.0 endpoint.
    document.body.appendChild(form);
    form.submit();
  }
</script>

<button onclick="trySampleRequest();">Try sample request</button>
</body></html>

JavaScript 源验证规则

Google 将以下验证规则应用于 JavaScript 源,以帮助开发人员确保其应用程序的安全。您的 JavaScript 来源必须遵守这些规则。见RFC 3986第3节的域,主机和方案的定义,如下所述。

验证规则
方案

JavaScript 源必须使用 HTTPS 方案,而不是普通的 HTTP。本地主机 URI(包括本地主机 IP 地址 URI)不受此规则约束。

主持人

主机不能是原始 IP 地址。本地主机 IP 地址不受此规则的约束。

领域
  • 主机的顶级域名(顶级域名)必须属于公共后缀列表
  • 主机域名无法“googleusercontent.com”
  • JavaScript来源不能包含网址缩短服务领域(如goo.gl ),除非应用程序拥有该域名。
  • 用户信息

    JavaScript 源不能包含 userinfo 子组件。

    小路

    JavaScript 源不能包含路径组件。

    询问

    JavaScript 源不能包含查询组件。

    分段

    JavaScript 源不能包含片段组件。

    人物JavaScript 来源不能包含某些字符,包括:
    • 通配符( '*'
    • 不可打印的 ASCII 字符
    • 无效的百分比编码(任何不遵循 URL 编码形式的百分比符号后跟两个十六进制数字的百分比编码)
    • 空字符(编码的NULL字符,例如, %00%C0%80

    增量授权

    在 OAuth 2.0 协议中,您的应用程序请求访问资源的授权,资源由范围标识。在您需要资源时请求授权被认为是最佳的用户体验做法。为了实现这种做法,Google 的授权服务器支持增量授权。此功能允许您根据需要请求范围,如果用户授予新范围的权限,则返回一个授权代码,该代码可以交换为包含用户授予项目的所有范围的令牌。

    例如,一个让人们采样音乐曲目和创建混音的应用程序在登录时可能只需要很少的资源,可能只需要登录人的姓名。但是,保存完整的混音需要访问他们的 Google Drive .大多数人会发现,如果他们只在应用程序实际需要时才被要求访问他们的 Google 云端硬盘,那么这很自然。

    在这种情况下,在登录时的应用程序可能要求openidprofile范围在登录执行基本的,再后来要求https://www.googleapis.com/auth/drive.file在时间范围第一个保存混音的请求。

    以下规则适用于从增量授权中获取的访问令牌:

    • 令牌可用于访问与滚动到新的组合授权中的任何范围相对应的资源。
    • 当使用令牌为组合的授权,以获得访问令牌,令牌表示组合的授权和可用于任何的访问的刷新scope包括在该响应的值。
    • 组合授权包括用户授予 API 项目的所有范围,即使授权是从不同客户端请求的。例如,如果用户使用应用程序的桌面客户端授予对一个作用域的访问权限,然后通过移动客户端向同一应用程序授予另一个作用域,则组合授权将包括两个作用域。
    • 如果您撤销代表组合授权的令牌,则同时撤销代表关联用户对所有该授权范围的访问。

    下面的代码示例展示了如何向现有访问令牌添加范围。这种方法使您的应用程序无需管理多个访问令牌。

    JS 客户端库

    为了范围添加到现有的访问令牌,调用GoogleUser.grant(options)方法。该options对象标识将要授予访问权限的其他范围。

    // Space-separated list of additional scope(s) you are requesting access to.
    // This code adds read-only access to the user's calendars via the Calendar API.
    var NEW_SCOPES = 'https://www.googleapis.com/auth/calendar.readonly';
    
    // Retrieve the GoogleUser object for the current user.
    var GoogleUser = GoogleAuth.currentUser.get();
    GoogleUser.grant({'scope': NEW_SCOPES});

    OAuth 2.0 端点

    为了范围添加到现有的访问令牌,包括include_granted_scopes在参数请求,谷歌的OAuth 2.0服务器

    下面的代码片段演示了如何做到这一点。该代码段假定您已将访问令牌有效的范围存储在浏览器的本地存储中。 (该完整的示例代码存储示波器用于其访问令牌是有效的通过设置列表oauth2-test-params.scope在浏览器的本地存储属性。)

    该代码段将访问令牌有效的范围与您要用于特定查询的范围进行比较。如果访问令牌未涵盖该范围,则 OAuth 2.0 流程将启动。这里, oauth2SignIn功能是相同于提供的一个步骤2 (和在后面提供完整的例子)。

    var SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly';
    var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
    
    var current_scope_granted = false;
    if (params.hasOwnProperty('scope')) {
      var scopes = params['scope'].split(' ');
      for (var s = 0; s < scopes.length; s++) {
        if (SCOPE == scopes[s]) {
          current_scope_granted = true;
        }
      }
    }
    
    if (!current_scope_granted) {
      oauth2SignIn(); // This function is defined elsewhere in this document.
    } else {
      // Since you already have access, you can proceed with the API request.
    }

    撤销令牌

    在某些情况下,用户可能希望撤销对应用程序的访问权限。用户可以通过撤销访问接入帐户设置。查看该第三方网站和应用程序的删除网站或应用程序访问部分访问您的帐户支持文档获取更多信息。

    应用程序也可以以编程方式撤销授予它的访问权限。在用户取消订阅、删除应用程序或应用程序所需的 API 资源发生显着变化的情况下,程序撤销非常重要。换句话说,删除过程的一部分可以包括一个 API 请求,以确保先前授予应用程序的权限被删除。

    JS 客户端库

    要以编程方式撤销令牌,呼叫GoogleAuth.disconnect()

    GoogleAuth.disconnect();

    OAuth 2.0 端点

    要以编程方式撤销令牌,你的应用程序发出请求https://oauth2.googleapis.com/revoke和包括令牌作为参数:

    curl -d -X -POST --header "Content-type:application/x-www-form-urlencoded" \
            https://oauth2.googleapis.com/revoke?token={token}

    The token can be an access token or a refresh token. If the token is an access token and it has a corresponding refresh token, the refresh token will also be revoked.

    If the revocation is successfully processed, then the HTTP status code of the response is 200 . For error conditions, an HTTP status code 400 is returned along with an error code.

    The following JavaScript snippet shows how to revoke a token in JavaScript without using the Google APIs Client Library for JavaScript. Since the Google's OAuth 2.0 endpoint for revoking tokens does not support Cross-origin Resource Sharing (CORS), the code creates a form and submits the form to the endpoint rather than using the XMLHttpRequest() method to post the request.

    function revokeAccess(accessToken) {
      // Google's OAuth 2.0 endpoint for revoking access tokens.
      var revokeTokenEndpoint = 'https://oauth2.googleapis.com/revoke';
    
      // Create <form> element to use to POST data to the OAuth 2.0 endpoint.
      var form = document.createElement('form');
      form.setAttribute('method', 'post');
      form.setAttribute('action', revokeTokenEndpoint);
    
      // Add access token to the form so it is set as value of 'token' parameter.
      // This corresponds to the sample curl request, where the URL is:
      //      https://oauth2.googleapis.com/revoke?token={token}
      var tokenField = document.createElement('input');
      tokenField.setAttribute('type', 'hidden');
      tokenField.setAttribute('name', 'token');
      tokenField.setAttribute('value', accessToken);
      form.appendChild(tokenField);
    
      // Add form to page and submit it to actually revoke the token.
      document.body.appendChild(form);
      form.submit();
    }