使用 OAuth 2.0 處理 JavaScript 網頁應用程式

本文說明如何實作 OAuth 2.0 授權,以便從 JavaScript 網路應用程式存取 YouTube Analytics API 或 YouTube Reporting API。OAuth 2.0 可讓使用者與應用程式共用特定資料,同時保有使用者名稱、密碼和其他資訊的隱私。舉例來說,應用程式可以使用 OAuth 2.0 取得權限,以便擷取頻道的 YouTube 數據分析資料。

這種 OAuth 2.0 流程稱為隱含授權流程。這項功能專為應用程式設計,這些應用程式只會在使用者使用應用程式時存取 API。這些應用程式無法儲存機密資訊。

在這個流程中,應用程式會開啟 Google 網址,該網址會使用查詢參數來識別應用程式,以及應用程式所需的 API 存取類型。您可以在目前的瀏覽器視窗或彈出式視窗中開啟網址。使用者可以透過 Google 驗證身分,並授予要求的權限。 接著,Google 會將使用者重新導向至您的應用程式。重新導向會包含存取權杖,應用程式會驗證該權杖,然後用於提出 API 要求。

Google API 用戶端程式庫和 Google 身分識別服務

如果您使用 JavaScript 適用的 Google API 用戶端程式庫向 Google 發出授權呼叫,則應使用 Google 身分識別服務 JavaScript 程式庫來處理 OAuth 2.0 流程。請參閱 Google 身分識別服務的權杖模型,該模型是根據 OAuth 2.0 隱含授權流程所設計。

必要條件

為專案啟用 API

任何會呼叫 Google API 的應用程式,都必須在 中啟用這些 API。

如要為專案啟用 API,請按照下列步驟操作:

  1. 在 中。
  2. 在「程式庫」頁面中,找出並啟用 YouTube Analytics API 和 YouTube Reporting API。許多擷取 YouTube 數據分析資料的應用程式也會與 YouTube Data API 介接。找出應用程式將使用的任何其他 API,並啟用這些 API。

建立授權憑證

任何使用 OAuth 2.0 存取 Google API 的應用程式,都必須具備授權憑證,才能向 Google 的 OAuth 2.0 伺服器識別應用程式。下列步驟說明如何為專案建立憑證。應用程式就能使用憑證存取您為該專案啟用的 API。

  1. 按一下 [Create credentials] (建立憑證) > [OAuth client ID] (OAuth 用戶端 ID)
  2. 選取「Web application」應用程式類型。
  3. 填妥表單。使用 JavaScript 提出已授權 Google API 要求的應用程式必須指定已授權的 JavaScript 來源。來源會標示應用程式可向 OAuth 2.0 伺服器傳送要求的網域。這些來源必須遵守 Google 的驗證規則

找出存取權範圍

範圍可讓應用程式僅要求存取其需要的資源,也能讓使用者控制對應用程式授予的存取量。因此,要求的範圍數量與取得使用者同意的可能性之間可能呈現反比關係。

開始實作 OAuth 2.0 授權之前,建議您找出應用程式需要權限存取的範圍。

YouTube Analytics API 會使用下列範圍:

範圍
https://www.googleapis.com/auth/youtube管理您的 YouTube 帳戶
https://www.googleapis.com/auth/youtube.readonly查看您的 YouTube 帳戶
https://www.googleapis.com/auth/youtubepartner查看及管理您在 YouTube 上的元素和相關內容
https://www.googleapis.com/auth/yt-analytics-monetary.readonly查看您 YouTube 內容的金額和非金額 YouTube 數據分析報表
https://www.googleapis.com/auth/yt-analytics.readonly查看您 YouTube 內容的 YouTube 數據分析報表

YouTube Reporting API 會使用下列範圍:

範圍
https://www.googleapis.com/auth/yt-analytics-monetary.readonly查看您 YouTube 內容的金額和非金額 YouTube 數據分析報表
https://www.googleapis.com/auth/yt-analytics.readonly查看您 YouTube 內容的 YouTube 數據分析報表

OAuth 2.0 API 範圍」文件包含您可能用於存取 Google API 的範圍完整清單。

取得 OAuth 2.0 存取權杖

以下步驟說明應用程式如何與 Google 的 OAuth 2.0 伺服器互動,取得使用者同意,代表使用者執行 API 要求。應用程式必須取得同意聲明,才能執行需要使用者授權的 Google API 要求。

步驟 1:重新導向至 Google 的 OAuth 2.0 伺服器

如要要求存取使用者資料的權限,請將使用者重新導向至 Google 的 OAuth 2.0 伺服器。

OAuth 2.0 端點

請在 https://accounts.google.com/o/oauth2/v2/auth 中產生網址,以便向 Google 的 OAuth 2.0 端點要求存取權。這個端點可透過 HTTPS 存取;純 HTTP 連線會遭到拒絕。

Google 授權伺服器支援下列查詢字串參數,適用於網路伺服器應用程式:

參數
client_id 必填

應用程式的用戶端 ID。您可以在 中找到這個值。

redirect_uri 必填

決定 API 伺服器在使用者完成授權流程後,將使用者重新導向至哪個位置。這個值必須與 OAuth 2.0 用戶端的授權重新導向 URI 完全相符,您可以在用戶端的 中設定這項值。如果這個值與提供的 client_id 授權重新導向 URI 不符,您會收到 redirect_uri_mismatch 錯誤。

請注意,httphttps 配置、大小寫和尾端斜線 ('/') 都必須相符。

response_type 必填

JavaScript 應用程式需要將參數值設為 token。這個值會指示 Google 授權伺服器,在使用者完成授權程序後,將存取權杖以 URI 片段 ID (#) 的 name=value 組合形式傳回,並將使用者重新導向至該 ID。

scope 必填

以空格分隔的範圍清單,用於識別應用程式可代表使用者存取的資源。這些值會提供 Google 向使用者顯示的同意畫面。

範圍可讓應用程式僅要求存取其需要的資源,也能讓使用者控制對應用程式授予的存取量。因此,要求的範圍數量與取得使用者同意的可能性之間存在反比關係。

YouTube Analytics API 會使用下列範圍:

範圍
https://www.googleapis.com/auth/youtube管理您的 YouTube 帳戶
https://www.googleapis.com/auth/youtube.readonly查看您的 YouTube 帳戶
https://www.googleapis.com/auth/youtubepartner查看及管理您在 YouTube 上的元素和相關內容
https://www.googleapis.com/auth/yt-analytics-monetary.readonly查看您 YouTube 內容的金額和非金額 YouTube 數據分析報表
https://www.googleapis.com/auth/yt-analytics.readonly查看您 YouTube 內容的 YouTube 數據分析報表

YouTube Reporting API 會使用下列範圍:

範圍
https://www.googleapis.com/auth/yt-analytics-monetary.readonly查看您 YouTube 內容的金額和非金額 YouTube 數據分析報表
https://www.googleapis.com/auth/yt-analytics.readonly查看您 YouTube 內容的 YouTube 數據分析報表

OAuth 2.0 API 範圍」文件提供您可能用於存取 Google API 的範圍完整清單。

建議您盡可能在情境中要求應用程式存取授權範圍。透過漸進式授權,在相關情境下要求存取使用者資料,有助於使用者更容易瞭解應用程式為何需要取得所要求的存取權。

state 建議

指定應用程式用於在授權要求和授權伺服器回應之間維持狀態的任何字串值。在使用者同意或拒絕應用程式的存取要求後,伺服器會傳回您在 redirect_uri 的網址片段 ID (#) 中,以 name=value 成對傳送的確切值。

您可以將這個參數用於多種用途,例如將使用者導向應用程式中的正確資源、傳送 Nonce,以及減輕跨網站請求偽造問題。由於 redirect_uri 可能會遭到猜測,因此使用 state 值可提高您對傳入連線的信心,確保該連線是驗證要求的結果。如果您產生隨機字串,或對擷取用戶端狀態的 Cookie 或其他值進行雜湊編碼,您可以驗證回應,進一步確保要求和回應來自同一個瀏覽器,以防範跨網站要求偽造等攻擊。如需建立及確認 state 權杖的範例,請參閱 OpenID Connect 說明文件。

include_granted_scopes 選填

讓應用程式使用漸進式授權,要求在情境中存取其他範圍的權限。如果您將這個參數的值設為 true,且授權要求已核准,則新的存取權存證也會涵蓋使用者先前授予應用程式存取權的任何範圍。如需範例,請參閱「逐步授權」一節。

login_hint 選填

如果應用程式知道哪位使用者嘗試進行驗證,可以使用這個參數向 Google 驗證伺服器提供提示。伺服器會使用提示簡化登入流程,方法是預先填入登入表單中的電子郵件欄位,或選取適當的多重登入工作階段。

將參數值設為電子郵件地址或 sub ID,這與使用者的 Google ID 相同。

prompt 選填

以空格分隔的提示清單,區分大小寫,供使用者查看。如果您未指定這個參數,使用者只會在專案第一次要求存取權時收到提示。詳情請參閱「 提示重新同意聲明」。

可能的值為:

none 請勿顯示任何驗證或同意畫面。不得與其他值一併指定。
consent 提示使用者同意。
select_account 提示使用者選取帳戶。

將重新導向作業重導至 Google 授權伺服器的範例

下方的範例網址會要求離線存取權 (access_type=offline),範圍為允許存取權以擷取使用者的 YouTube Analytics 報表。它會使用遞增式授權,確保新的存取權權杖涵蓋使用者先前授予應用程式存取權的任何範圍。網址也會為必要的 redirect_uriresponse_typeclient_id 參數以及 state 參數設定值。網址包含換行符號和空格,方便閱讀。

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyt-analytics.readonly&
 include_granted_scopes=true&
 state=state_parameter_passthrough_value&
 redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
 response_type=token&
 client_id=client_id

建立要求網址後,請將使用者重新導向至該網址。

JavaScript 範例程式碼

下列 JavaScript 程式碼片段顯示如何在 JavaScript 中啟動授權流程,而不需要使用 JavaScript 適用的 Google API 用戶端程式庫。由於這個 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/yt-analytics.readonly https://www.googleapis.com/auth/calendar.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();
}

步驟 2:Google 提示使用者同意

在這個步驟中,使用者會決定是否授予應用程式所要求的存取權。在此階段,Google 會顯示同意畫面,其中顯示應用程式名稱,以及應用程式要求使用者授權憑證存取 Google API 服務的權限,以及要授予的存取範圍摘要。使用者可以選擇同意授予存取應用程式要求的一或多個範圍,或是拒絕要求。

應用程式在此階段不需要採取任何行動,因為它會等待 Google OAuth 2.0 伺服器的回應,指出是否已授予存取權。下一個步驟會說明該回應。

錯誤

向 Google 的 OAuth 2.0 授權端點提出要求時,可能會顯示使用者介面的錯誤訊息,而不是預期的驗證和授權流程。以下列出常見的錯誤代碼和建議解決方案。

admin_policy_enforced

由於 Google Workspace 管理員的政策,Google 帳戶無法授權所要求的一或多個範圍。如要進一步瞭解管理員如何限制對所有範圍或機密和受限制範圍的存取權,直到明確授予 OAuth 客戶端 ID 存取權為止,請參閱 Google Workspace 管理員說明文章「 控管哪些第三方應用程式和內部應用程式可存取 Google Workspace 資料」。

disallowed_useragent

授權端點會顯示在 Google 的 OAuth 2.0 政策禁止使用的嵌入式使用者代理程式中。

Android

Android 開發人員在 android.webkit.WebView 中開啟授權要求時,可能會遇到這則錯誤訊息。開發人員應改用 Android 程式庫,例如 Google 登入 (Android 版) 或 OpenID Foundation 的 AppAuth for Android

如果 Android 應用程式在嵌入式使用者代理程式中開啟一般網頁連結,且使用者從您的網站前往 Google 的 OAuth 2.0 授權端點,網頁開發人員可能會遇到這個錯誤。開發人員應允許一般連結在作業系統的預設連結處理常式中開啟,包括 Android 應用程式連結處理常式或預設瀏覽器應用程式。Android 自訂分頁程式庫也是支援的選項。

iOS

iOS 和 macOS 開發人員在 WKWebView 中開啟授權要求時,可能會遇到這個錯誤。開發人員應改用 iOS 程式庫,例如 Google 登入 (適用於 iOS) 或 OpenID Foundation 的 AppAuth for iOS

如果 iOS 或 macOS 應用程式在嵌入式使用者代理程式中開啟一般網頁連結,且使用者從您的網站前往 Google 的 OAuth 2.0 授權端點,網頁開發人員可能會遇到這個錯誤。開發人員應允許一般連結在作業系統的預設連結處理常式中開啟,包括 通用連結處理常式或預設瀏覽器應用程式。SFSafariViewController 程式庫也是支援的選項。

org_internal

要求中的 OAuth 用戶端 ID 是專案的一部分,可限制特定 Google Cloud 組織中的 Google 帳戶存取權。如要進一步瞭解這項設定選項,請參閱「設定 OAuth 同意畫面」說明文章中的「使用者類型」一節。

invalid_client

提出要求的來源未經授權,無法存取此客戶。請參閱 origin_mismatch

invalid_grant

使用增量授權時,權杖可能已過期或已失效。 再次驗證使用者身分,並徵求使用者同意取得新的權杖。如果您持續看到這項錯誤,請確認應用程式已正確設定,且您在要求中使用正確的符記和參數。否則,使用者帳戶可能已遭到刪除或停用。

origin_mismatch

發出授權要求的 JavaScript 通訊協定、網域和/或連接埠,可能與為 OAuth 用戶端 ID 註冊的已授權 JavaScript 來源 URI 不符。在 中查看已授權的 JavaScript 來源。

redirect_uri_mismatch

授權要求中傳遞的 redirect_uri 與 OAuth 用戶端 ID 的授權重新導向 URI 不符。在 中查看已授權的重新導向 URI。

發出授權要求的 JavaScript 通訊協定、網域和/或連接埠,可能與為 OAuth 用戶端 ID 註冊的已授權 JavaScript 來源 URI 不符。查看 中的已授權 JavaScript 來源。

redirect_uri 參數可能會參照已淘汰且不再支援的 OAuth 額外管道 (OOB) 流程。請參閱遷移指南更新整合功能。

invalid_request

您提出的要求發生錯誤,這可能是由多種原因造成:

  • 要求格式不正確
  • 要求缺少必要參數
  • 要求使用 Google 不支援的授權方法。確認 OAuth 整合功能使用建議的整合方法

步驟 3:處理 OAuth 2.0 伺服器回應

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 伺服器回應範例

您可以按一下下列範例網址,要求只讀存取權,以便查看 Google 雲端硬碟中檔案的中繼資料,以及查看 Google 日曆活動的只讀存取權,藉此測試這個流程:

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyt-analytics.readonly&
 include_granted_scopes=true&
 state=state_parameter_passthrough_value&
 redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
 response_type=token&
 client_id=client_id

完成 OAuth 2.0 流程後,系統會將您重新導向至 http://localhost/oauth2callback。除非本機機器剛好提供該位址的檔案,否則該網址會產生 404 NOT FOUND 錯誤。在下一個步驟中,我們會進一步說明使用者重新導向至應用程式時,URI 中傳回的資訊。

步驟 4:檢查使用者授予的範圍

一次要求多個權限範圍時,使用者可能不會授予應用程式要求的所有權限範圍。 應用程式應一律檢查使用者授予的存取範圍,並透過停用相關功能來處理任何存取範圍拒絕的情況。詳情請參閱如何處理精細權限

OAuth 2.0 端點

如要檢查使用者是否已授予應用程式存取特定範圍的權限,請查看存取權存取金鑰回應中的 scope 欄位。由 access_token 授予的存取權範圍,以空格分隔的字串清單表示,並區分大小寫。

舉例來說,下列存取權存取權杖回應範例指出,使用者已授予應用程式存取權,可存取唯讀的 Google 雲端硬碟活動和日曆活動:

  {
    "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
    "expires_in": 3920,
    "token_type": "Bearer",
    "scope": "https://www.googleapis.com/auth/yt-analytics.readonly https://www.googleapis.com/auth/calendar.readonly",
    "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
  }

呼叫 Google API

OAuth 2.0 端點

應用程式取得存取權權杖後,如果已授予 API 所需的存取範圍,您就可以使用權杖代表特定使用者帳戶呼叫 Google API。如要這樣做,請在 API 要求中加入存取權杖,方法是加入 access_token 查詢參數或 Authorization HTTP 標頭 Bearer 值。盡可能使用 HTTP 標頭,因為查詢字串通常會顯示在伺服器記錄中。在大多數情況下,您可以使用用戶端程式庫設定 Google API 呼叫 (例如呼叫 YouTube Analytics API)。

請注意,YouTube Analytics API 不支援服務帳戶流程。YouTube Reporting API 僅支援服務帳戶,且僅適用於擁有及管理多個 YouTube 頻道的 YouTube 內容擁有者,例如唱片公司和電影工作室。

您可以在 OAuth 2.0 Playground 中試用所有 Google API,並查看其範圍。

HTTP GET 範例

使用 Authorization: Bearer HTTP 標頭呼叫 reports.query 端點 (YouTube Analytics API) 的呼叫可能如下所示。請注意,您需要指定自己的存取權杖:

GET /youtube/analytics/v1/reports?ids=channel%3D%3DMINE&start-date=2016-05-01&end-date=2016-06-30&metrics=views HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

以下是針對已驗證使用者,使用 access_token 查詢字串參數呼叫相同 API 的範例:

GET https://www.googleapis.com/youtube/analytics/v1/reports?access_token=access_token&ids=channel%3D%3DMINE&start-date=2016-05-01&end-date=2016-06-30&metrics=views

curl 範例

您可以使用 curl 指令列應用程式測試這些指令。以下是使用 HTTP 標頭選項的範例 (建議做法):

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/youtube/analytics/v1/reports?ids=channel%3D%3DMINE&start-date=2016-05-01&end-date=2016-06-30&metrics=views

或者,您也可以使用查詢字串參數選項:

curl https://www.googleapis.com/youtube/analytics/v1/reports?access_token=access_token&ids=channel%3D%3DMINE&start-date=2016-05-01&end-date=2016-06-30&metrics=views

JavaScript 範例程式碼

下列程式碼片段示範如何使用 CORS (跨來源資源共享) 向 Google API 傳送要求。本範例不會使用 JavaScript 適用的 Google API 用戶端程式庫。不過,即使您未使用用戶端程式庫,該程式庫說明文件中的 CORS 支援指南可能仍可協助您進一步瞭解這些要求。

在這個程式碼片段中,access_token 變數代表您取得的權杖,可代表已授權的使用者提出 API 要求。完整範例示範如何將該權杖儲存在瀏覽器的本機儲存空間中,並在提出 API 要求時擷取。

var xhr = new XMLHttpRequest();
xhr.open('GET',
    'https://www.googleapis.com/youtube/analytics/v1/reports?ids=channel%3D%3DMINE&start-date=2016-05-01&end-date=2016-06-30&metrics=views&' +
    'access_token=' + params['access_token']);
xhr.onreadystatechange = function (e) {
  console.log(xhr.response);
};
xhr.send(null);

完整範例

OAuth 2.0 端點

這個程式碼範例示範如何在 JavaScript 中完成 OAuth 2.0 流程,而不使用 JavaScript 適用的 Google API 用戶端程式庫。這個程式碼適用於 HTML 頁面,可顯示試用 API 要求的按鈕。當您點選按鈕時,程式碼會檢查網頁是否已在瀏覽器的本機儲存空間中儲存 API 存取權權杖。如果是,則會執行 API 要求。否則,系統會啟動 OAuth 2.0 流程。

針對 OAuth 2.0 流程,頁面會依照下列步驟操作:

  1. 這項服務會將使用者導向 Google 的 OAuth 2.0 伺服器,後者會要求存取 https://www.googleapis.com/auth/yt-analytics.readonlyhttps://www.googleapis.com/auth/calendar.readonly 範圍。
  2. 授予 (或拒絕) 一或多個要求範圍的存取權後,系統會將使用者重新導向至原始網頁,該網頁會從片段 ID 字串剖析存取權杖。
  3. 這個頁面會檢查使用者授予應用程式存取權的範圍。
  4. 如果使用者已授予所要求的範圍 (scope()) 存取權,網頁就會使用存取權杖發出範例 API 要求。

    這項 API 要求會呼叫 YouTube Analytics API 的 reports.query 方法,擷取已授權使用者 YouTube 頻道的觀看次數。

  5. 如果要求執行成功,API 回應會記錄在瀏覽器的偵錯主控台中。

您可以透過 Google 帳戶的「權限」頁面撤銷應用程式的存取權。應用程式會列為 Google API 說明文件的 OAuth 2.0 示範

如要在本機執行此程式碼,您必須為 YOUR_CLIENT_IDYOUR_REDIRECT_URI 變數設定值,以對應授權憑證YOUR_REDIRECT_URI 變數應設為網頁放送的網址。這個值必須與您在 中設定的 OAuth 2.0 用戶端授權重新導向 URI 完全相符。如果這個值與授權 URI 不符,您會收到 redirect_uri_mismatch 錯誤。您的專案也必須為此要求啟用適當的 API

<html><head></head><body>
<script>
  var YOUR_CLIENT_ID = 'REPLACE_THIS_VALUE';
  var YOUR_REDIRECT_URI = 'REPLACE_THIS_VALUE';

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

      trySampleRequest();
    } else {
      console.log('State mismatch. Possible CSRF attack');
    }
  }

  // Function to generate a random state value
  function generateCryptoRandomState() {
    const randomValues = new Uint32Array(2);
    window.crypto.getRandomValues(randomValues);

    // Encode as UTF-8
    const utf8Encoder = new TextEncoder();
    const utf8Array = utf8Encoder.encode(
      String.fromCharCode.apply(null, randomValues)
    );

    // Base64 encode the UTF-8 data
    return btoa(String.fromCharCode.apply(null, utf8Array))
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=+$/, '');
  }

  // 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']) { 
      // User authorized the request. Now, check which scopes were granted.
      if (params['scope'].includes('https://www.googleapis.com/auth/drive.metadata.readonly')) {
        // User authorized read-only Drive activity permission.
        // Calling the APIs, etc.
        var xhr = new XMLHttpRequest();
        xhr.open('GET',
          'https://www.googleapis.com/youtube/analytics/v1/reports?ids=channel%3D%3DMINE&start-date=2016-05-01&end-date=2016-06-30&metrics=views&' +
          '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 {
        // User didn't authorize read-only Drive activity permission.
        // Update UX and application accordingly
        console.log('User did not authorize read-only Drive activity permission.');
      }

      // Check if user authorized Calendar read permission.
      if (params['scope'].includes('https://www.googleapis.com/auth/calendar.readonly')) {
        // User authorized Calendar read permission.
        // Calling the APIs, etc.
        console.log('User authorized Calendar read permission.');
      }
      else {
        // User didn't authorize Calendar read permission.
        // Update UX and application accordingly
        console.log('User did not authorize Calendar read permission.');
      } 
    } else {
      oauth2SignIn();
    }
  }

  /*
   * Create form to request access token from Google's OAuth 2.0 server.
   */
  function oauth2SignIn() {
    // create random state value and store in local storage
    var state = generateCryptoRandomState();
    localStorage.setItem('state', state);

    // 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/yt-analytics.readonly https://www.googleapis.com/auth/calendar.readonly',
                  'state': state,
                  '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。本規則不適用於 localhost URI (包括 localhost IP 位址 URI)。

主機

主機不得是原始 IP 位址。本規則不適用於本機 IP 位址。

網域
  • 代管頂層網域 (頂層網域) 必須屬於公開字尾清單
  • 主機網域不得為 “googleusercontent.com”
  • 除非應用程式擁有網域,否則 JavaScript 來源不得包含縮短網址網域 (例如 goo.gl)。
  • Userinfo

    JavaScript 來源不得包含 userinfo 子元件。

    路徑

    JavaScript 來源不得包含路徑元件。

    查詢

    JavaScript 來源不得包含查詢元件。

    Fragment

    JavaScript 來源不得包含片段元件。

    字元 JavaScript 來源不得包含特定字元,包括:
    • 萬用字元 ('*')
    • 非可列印的 ASCII 字元
    • 無效的百分號編碼 (任何不符合網址編碼格式 (百分號後接兩個十六進位數字) 的百分號編碼)
    • 零值字元 (已編碼的 NULL 字元,例如 %00%C0%80)

    增量授權

    在 OAuth 2.0 通訊協定中,應用程式會要求存取資源的授權,這些資源會透過範圍識別。在需要資源時要求授權,是提供最佳使用者體驗的最佳做法。為實現這項做法,Google 的授權伺服器支援增量授權。這項功能可讓您視需要要求範圍,如果使用者授予新範圍的權限,系統會傳回授權碼,您可以將該授權碼換成包含使用者授予專案的所有範圍的權杖。

    舉例來說,假設應用程式擷取 YouTube 數據分析報表,其中部分是需要額外存取權限的收益報表,而其他報表則不需要。在這種情況下,應用程式在登入時可能只會要求存取 https://www.googleapis.com/auth/yt-analytics.readonly 範圍。不過,如果使用者嘗試擷取金錢報表,應用程式也可以要求存取 https://www.googleapis.com/auth/yt-analytics-monetary.readonly 範圍。

    從增量授權取得的存取權杖適用下列規則:

    • 權杖可用於存取與新綜合授權中納入的任何範圍相對應的資源。
    • 當您使用組合授權的重新整理權杖來取得存取權杖時,存取權杖會代表組合授權,並可用於回應中包含的任何 scope 值。
    • 即使使用者是透過不同用戶端要求授權,合併授權仍會包含使用者授予 API 專案的所有權限範圍。舉例來說,如果使用者使用應用程式的電腦版用戶端授予一個範圍的存取權,然後透過行動版用戶端將另一個範圍的存取權授予相同的應用程式,則合併授權會包含這兩個範圍。
    • 如果您撤銷代表組合授權的權杖,系統會同時撤銷代表相關聯使用者的所有授權範圍存取權。

    以下程式碼範例說明如何在現有的存取權杖中新增權限範圍。這種做法可讓應用程式避免管理多個存取權權杖。

    OAuth 2.0 端點

    在這個範例中,除了使用者已授予應用程式的其他存取權之外,呼叫應用程式還要求存取權,以便擷取使用者的 YouTube Analytics 資料。

    如要將範圍新增至現有的存取權杖,請在向 Google OAuth 2.0 伺服器提出要求時,加入 include_granted_scopes 參數。

    以下程式碼片段說明如何執行這項操作。程式碼片段假設您已在瀏覽器的本機儲存空間中,儲存存取權權杖有效範圍的權限。(完整範例程式碼會在瀏覽器的本機儲存空間中設定 oauth2-test-params.scope 屬性,藉此儲存存取權杖有效範圍的清單)。

    程式碼片段會比較存取權存證有效的範圍,以及您要用於特定查詢的範圍。如果存取權杖不涵蓋該範圍,系統就會啟動 OAuth 2.0 流程。這裡的 oauth2SignIn 函式與步驟 2 中提供的函式相同 (後續的完整範例中也會提供這項函式)。

    var SCOPE = 'https://www.googleapis.com/auth/yt-analytics.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 要求,以確保先前授予應用程式的權限已移除。

    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}

    憑證可以是存取權杖或更新權杖。如果符記是存取權杖,且具有對應的更新憑證,則更新憑證也會遭到撤銷。

    如果撤銷作業順利完成,回應的 HTTP 狀態碼會是 200。在錯誤情況下,系統會傳回 HTTP 狀態碼 400 和錯誤代碼。

    下列 JavaScript 程式碼片段說明如何在 JavaScript 中撤銷權杖,而不需要使用 JavaScript 適用的 Google API 用戶端程式庫。由於 Google 用於撤銷權杖的 OAuth 2.0 端點不支援跨來源資源共用 (CORS),因此程式碼會建立表單並將表單提交至端點,而不是使用 XMLHttpRequest() 方法發布要求。

    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();
    }

    導入跨帳戶防護

    您還可以透過 Google 的跨帳戶保護服務,實施跨帳戶保護機制,進一步保護使用者的帳戶。這項服務可讓您訂閱安全性事件通知,為應用程式提供有關使用者帳戶重大變更的資訊。接著,您可以根據決定如何回應事件,使用這些資訊採取行動。

    Google 跨帳戶保護服務傳送至應用程式的事件類型示例包括:

    • https://schemas.openid.net/secevent/risc/event-type/sessions-revoked
    • https://schemas.openid.net/secevent/oauth/event-type/token-revoked
    • https://schemas.openid.net/secevent/risc/event-type/account-disabled

    如要進一步瞭解如何實作跨帳戶保護功能,以及查看可用的完整事件清單,請參閱「 透過跨帳戶保護功能保護使用者帳戶 」一文。