為網路應用程式應用程式使用 OAuth 2.0

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

本文件說明網路伺服器應用程式如何使用 Google API 用戶端程式庫或 Google OAuth 2.0 端點,以實作 OAuth 2.0 授權以存取 Google API。

OAuth 2.0 可讓使用者與應用程式共用特定資料,同時保有使用者名稱、密碼和其他資訊的私密性。 舉例來說,應用程式可以使用 OAuth 2.0,取得使用者 您可以在 Google 雲端硬碟中儲存檔案的權限。

這個 OAuth 2.0 流程僅供使用者授權之用。專門用於儲存機密資訊及維持狀態的應用程式。合適的授權網路伺服器應用程式可在使用者與應用程式互動或使用者離開應用程式後存取 API。

網路伺服器應用程式 另外也會使用服務帳戶來授權 API 要求,尤其是在呼叫 Cloud API 存取專案型資料時,而非個別使用者的資料。網路伺服器應用程式可以搭配使用者授權使用服務帳戶。

用戶端程式庫

本頁的語言專屬範例使用 Google API 用戶端程式庫實作 OAuth 2.0 授權。如要執行程式碼範例,您必須先安裝自己語言的用戶端程式庫。

使用 Google API 用戶端程式庫處理應用程式的 OAuth 2.0 流程時,用戶端程式庫會執行許多應用程式需要自行處理的操作。例如,應用程式可決定何時使用或重新整理已儲存的存取權杖,以及應用程式必須重新取得同意的時間。用戶端程式庫也會產生正確的重新導向網址,並實作交換存取權杖授權碼的重新導向處理常式。

伺服器端應用程式適用的 Google API 用戶端程式庫支援下列語言:

必要條件

為您的專案啟用 API

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

如要為專案啟用 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 不在清單中,請使用搜尋功能尋找該 API,或按一下所屬的產品系列中的「查看全部」
  4. 選取要啟用的 API,然後按一下「Enable」(啟用) 按鈕。
  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. 按一下 [Create credentials] (建立憑證) > [OAuth client ID] (OAuth 用戶端 ID)
  3. 選取「Web application」應用程式類型。
  4. 填寫表單,然後按一下 [建立]。如果應用程式使用 PHP、Java、Python、Ruby 和 .NET 等語言和架構,就必須指定已獲授權的重新導向 URI。重新導向 URI 是 OAuth 2.0 伺服器可以將回應傳送至哪個端點。這些端點必須遵守 Google 的驗證規則

    如要進行測試,您可以指定參照本機電腦的 URI,例如 http://localhost:8080。不過請注意,本文中的所有範例均使用 http://localhost:8080 做為重新導向 URI。

    建議您設計應用程式的驗證端點,以便應用程式不要向頁面上的其他資源公開授權碼。

建立憑證後,請從 API Console下載 client_secret.json 檔案。將檔案安全地存放在僅限應用程式存取的位置。

識別存取權範圍

範圍可讓您的應用程式只要求存取所需資源的存取權,同時,使用者也能控管應用程式授予的存取權數量。因此,使用者要求的範圍數以及取得使用者同意聲明的可能性之間可能有反向關係。

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

此外,我們也建議您讓應用程式透過漸進式授權程序要求存取授權範圍,該程序會在情境中要求存取使用者資料。這個最佳做法可讓使用者更容易瞭解應用程式需要哪些存取權。

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

語言相關規定

如要執行本文件中的任何程式碼範例,你必須擁有 Google 帳戶、網際網路和網路瀏覽器。如果您使用的是其中一個 API 用戶端程式庫,請一併參閱下列語言的專屬規定。

PHP

如要執行本文件中的 PHP 程式碼範例,您必須:

  • 已安裝指令列介面 (CLI) 和 JSON 擴充功能的 PHP 5.6 以上版本。
  • Composer 依附元件管理工具。
  • 適用於 PHP 的 Google API 用戶端程式庫:

    composer require google/apiclient:^2.10

Python

如要執行本文件中的 Python 程式碼範例,您必須:

  • Python 2.6 以上版本
  • pip 套件管理工具。
  • 適用於 Python 的 Google API 用戶端程式庫:
    pip install --upgrade google-api-python-client
  • 使用者授權的 google-authgoogle-auth-oauthlibgoogle-auth-httplib2
    pip install --upgrade google-auth google-auth-oauthlib google-auth-httplib2
  • Flask Python 網頁應用程式架構。
    pip install --upgrade flask
  • requests HTTP 程式庫。
    pip install --upgrade requests

Ruby

如要執行本文件中的 Ruby 程式碼範例,您必須:

  • Ruby 2.2.2 以上版本
  • Ruby 適用的 Google API 用戶端程式庫:

    gem install google-api-client
  • Sinatra Ruby 網頁應用程式架構。

    gem install sinatra

Node.js

如要執行這份文件中的 Node.js 程式碼範例,您必須:

  • 維護 LTS、使用中的 LTS 或 Node.js 目前版本。
  • Google API Node.js 用戶端:

    npm install googleapis

HTTP/REST

您不需要安裝任何程式庫,就能直接呼叫 OAuth 2.0 端點。

取得 OAuth 2.0 存取憑證

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

以下清單概述了這些步驟:

  1. 您的應用程式會識別所需的權限。
  2. 您的應用程式會將使用者重新導向要求的權限和 Google 要求的權限清單。
  3. 使用者決定是否授予應用程式權限。
  4. 您的應用程式會判斷使用者做出的決定。
  5. 如果使用者已授予要求的權限,應用程式就會擷取使用者發出的 API 要求所需的權杖。

步驟 1:設定授權參數

第一步是建立授權要求。該要求會設定可識別應用程式的參數,並定義要要求使用者授予應用程式的權限。

  • 如果您使用 Google 用戶端程式庫進行 OAuth 2.0 驗證和授權,就必須建立及設定用於定義這些參數的物件。
  • 如果您直接呼叫 Google OAuth 2.0 端點,系統會產生網址,並在該網址設定參數。

下方分頁定義網路伺服器應用程式支援的授權參數。語言相關範例也會示範如何使用用戶端程式庫或授權程式庫,設定用來設定這些參數的物件。

PHP

下方的程式碼片段會建立 Google\Client() 物件,該物件定義授權要求中的參數。

這個物件會使用來自 client_secret.json 檔案的資訊來識別應用程式。(如要進一步瞭解該檔案,請參閱建立授權憑證)。這個物件也會指出應用程式要求存取的權限範圍,以及應用程式驗證端點的網址,該網址會處理 Google 的 OAuth 2.0 伺服器回應。最後,程式碼會設定選用的 access_typeinclude_granted_scopes 參數。

例如,下列程式碼要求使用者的 Google 雲端硬碟唯讀存取權:

$client = new Google\Client();
$client->setAuthConfig('client_secret.json');
$client->addScope(Google\Service\Drive::DRIVE_METADATA_READONLY);
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');
// offline access will give you both an access and refresh token so that
// your app can refresh the access token without user interaction.
$client->setAccessType('offline');
// Using "consent" ensures that your application always receives a refresh token.
// If you are not using offline access, you can omit this.
$client->setApprovalPrompt('consent');
$client->setIncludeGrantedScopes(true);   // incremental auth

要求會指定下列資訊:

參數
client_id 必要

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

在 PHP 中,呼叫 setAuthConfig 函式以從 client_secret.json 檔案載入授權憑證。

$client = new Google\Client();
$client->setAuthConfig('client_secret.json');
redirect_uri 必要

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

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

如要在 PHP 中設定這個值,請呼叫 setRedirectUri 函式。請注意,您必須為指定的 client_id 指定有效的重新導向 URI。

$client->setRedirectUri('https://oauth2.example.com/code');
scope 必要

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

範圍可讓您的應用程式只要求存取所需資源的存取權,同時還能讓使用者控管應用程式授予的存取權數量。因此,要求的範圍數和取得使用者同意聲明的可能性之間存在反向關係。

如要在 PHP 中設定這個值,請呼叫 addScope 函式:

$client->addScope(Google\Service\Drive::DRIVE_METADATA_READONLY);

建議您讓應用程式盡可能在相關情況下要求授權範圍。請在相關使用情境中,透過漸進式授權要求存取權,幫助使用者進一步瞭解應用程式需要其存取權的原因。

access_type 建議使用

指出應用程式是否在使用者瀏覽器中重新整理時,可以重新整理存取權杖。有效的參數值為 online,也就是預設值和 offline

如果您的應用程式並未在使用者瀏覽器中重新整理權杖,請將值設為 offline。這是重新整理本文件稍後說明存取權杖的方法。這個值會指示 Google 授權伺服器在應用程式首次交換權杖的驗證權杖時傳回更新權杖「和」存取權杖。

如要在 PHP 中設定這個值,請呼叫 setAccessType 函式:

$client->setAccessType('offline');
state 建議使用

指定應用程式用來維持授權要求和授權伺服器回應狀態的任何字串值。使用者同意或拒絕應用程式存取要求後,伺服器會傳回 redirect_uri 網址查詢元件 (?) 中做為 name=value 組合傳送的確切值。

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

如要在 PHP 中設定這個值,請呼叫 setState 函式:

$client->setState($sample_passthrough_value);
include_granted_scopes 選填

允許應用程式使用漸進式授權功能要求結構定義中的其他範圍。如果將這項參數值設為 true 並授權要求,新的存取權杖也會涵蓋使用者先前已授予應用程式存取權的所有範圍。如需範例,請參閱逐步授權一節。

如要在 PHP 中設定這個值,請呼叫 setIncludeGrantedScopes 函式:

$client->setIncludeGrantedScopes(true);
login_hint 選填

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

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

如要在 PHP 中設定這個值,請呼叫 setLoginHint 函式:

$client->setLoginHint('None');
prompt 選填

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

如要在 PHP 中設定這個值,請呼叫 setApprovalPrompt 函式:

$client->setApprovalPrompt('consent');

可能的值為:

none 不要顯示任何驗證或同意畫面。不得指定其他值。
consent 提示使用者提供同意聲明。
select_account 提示使用者選取帳戶。

Python

下列程式碼片段使用 google-auth-oauthlib.flow 模組建構授權要求。

程式碼會建構 Flow 物件,以使用建立授權憑證後下載的 client_secret.json 檔案識別應用程式。這個物件也會指出應用程式要求存取的權限範圍,以及應用程式驗證端點的網址,該網址會處理 Google 的 OAuth 2.0 伺服器回應。最後,程式碼會設定選用的 access_typeinclude_granted_scopes 參數。

例如,下列程式碼要求使用者的 Google 雲端硬碟唯讀存取權:

import google.oauth2.credentials
import google_auth_oauthlib.flow

# Use the client_secret.json file to identify the application requesting
# authorization. The client ID (from that file) and access scopes are required.
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=['https://www.googleapis.com/auth/drive.metadata.readonly'])

# Indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required. The value must exactly
# match one of the authorized redirect URIs for the OAuth 2.0 client, which you
# configured in the API Console. If this value doesn't match an authorized URI,
# you will get a 'redirect_uri_mismatch' error.
flow.redirect_uri = 'https://www.example.com/oauth2callback'

# Generate URL for request to Google's OAuth 2.0 server.
# Use kwargs to set optional request parameters.
authorization_url, state = flow.authorization_url(
    # Enable offline access so that you can refresh an access token without
    # re-prompting the user for permission. Recommended for web server apps.
    access_type='offline',
    # Enable incremental authorization. Recommended as a best practice.
    include_granted_scopes='true')

要求會指定下列資訊:

參數
client_id 必要

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

在 Python 中,呼叫 from_client_secrets_file 方法以從 client_secret.json 檔案擷取用戶端 ID。您也可以使用 from_client_config 方法,該方法會將用戶端設定原本顯示在用戶端密鑰檔案中,但不會存取檔案本身。

flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=['https://www.googleapis.com/auth/drive.metadata.readonly'])
redirect_uri 必要

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

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

如要在 Python 中設定這個值,請設定 flow 物件 redirect_uri 屬性:

flow.redirect_uri = 'https://oauth2.example.com/code'
scope 必要

範圍清單,指出應用程式可代表的使用者存取的資源。這些值會告知 Google 向使用者顯示的同意畫面。

範圍可讓您的應用程式只要求存取所需資源的存取權,同時還能讓使用者控管應用程式授予的存取權數量。因此,要求的範圍數和取得使用者同意聲明的可能性之間存在反向關係。

在 Python 中,請使用相同的方法來設定 client_id 來指定範圍清單。

flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=['https://www.googleapis.com/auth/drive.metadata.readonly'])

建議您讓應用程式盡可能在相關情況下要求授權範圍。請在相關使用情境中,透過漸進式授權要求存取權,幫助使用者進一步瞭解應用程式需要其存取權的原因。

access_type 建議使用

指出應用程式是否在使用者瀏覽器中重新整理時,可以重新整理存取權杖。有效的參數值為 online,也就是預設值和 offline

如果您的應用程式並未在使用者瀏覽器中重新整理權杖,請將值設為 offline。這是重新整理本文件稍後說明存取權杖的方法。這個值會指示 Google 授權伺服器在應用程式首次交換權杖的驗證權杖時傳回更新權杖「和」存取權杖。

在 Python 中,在呼叫 flow.authorization_url 方法時,指定 access_type 做為關鍵字引數,即可設定 access_type 參數:

authorization_url, state = flow.authorization_url(
    access_type='offline',
    include_granted_scopes='true')
state 建議使用

指定應用程式用來維持授權要求和授權伺服器回應狀態的任何字串值。使用者同意或拒絕應用程式存取要求後,伺服器會傳回 redirect_uri 網址查詢元件 (?) 中做為 name=value 組合傳送的確切值。

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

在 Python 中,請在呼叫 flow.authorization_url 方法時指定 state 做為關鍵字引數,以設定 state 參數:

authorization_url, state = flow.authorization_url(
    access_type='offline',
    state=sample_passthrough_value,
    include_granted_scopes='true')
include_granted_scopes 選填

允許應用程式使用漸進式授權功能要求結構定義中的其他範圍。如果將這項參數值設為 true 並授權要求,新的存取權杖也會涵蓋使用者先前已授予應用程式存取權的所有範圍。如需範例,請參閱逐步授權一節。

在 Python 中,在呼叫 flow.authorization_url 方法時,指定 include_granted_scopes 做為關鍵字引數,即可設定 include_granted_scopes 參數:

authorization_url, state = flow.authorization_url(
    access_type='offline',
    include_granted_scopes='true')
login_hint 選填

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

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

在 Python 中,在呼叫 flow.authorization_url 方法時,指定 login_hint 做為關鍵字引數,即可設定 login_hint 參數:

authorization_url, state = flow.authorization_url(
    access_type='offline',
    login_hint='None',
    include_granted_scopes='true')
prompt 選填

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

在 Python 中,請在呼叫 flow.authorization_url 方法時指定 prompt 做為關鍵字引數,以設定 prompt 參數:

authorization_url, state = flow.authorization_url(
      access_type='offline',
      prompt='consent',
      include_granted_scopes='true')

可能的值為:

none 不要顯示任何驗證或同意畫面。不得指定其他值。
consent 提示使用者提供同意聲明。
select_account 提示使用者選取帳戶。

Ruby

使用您建立的 Client_secrets.json 檔案來設定應用程式中的用戶端物件。設定用戶端物件時,您必須指定應用程式需要存取的範圍,以及應用程式驗證端點的網址,該網址會處理 OAuth 2.0 伺服器的回應。

例如,下列程式碼要求使用者的 Google 雲端硬碟唯讀存取權:

require 'google/apis/drive_v2'
require 'google/api_client/client_secrets'

client_secrets = Google::APIClient::ClientSecrets.load
auth_client = client_secrets.to_authorization
auth_client.update!(
  :scope => 'https://www.googleapis.com/auth/drive.metadata.readonly',
  :redirect_uri => 'http://www.example.com/oauth2callback',
  :additional_parameters => {
    "access_type" => "offline",         # offline access
    "include_granted_scopes" => "true"  # incremental auth
  }
)

您的應用程式會使用用戶端物件執行 OAuth 2.0 作業,例如產生授權要求網址,以及將存取權杖套用至 HTTP 要求。

Node.js

下方的程式碼片段會建立 google.auth.OAuth2 物件,該物件定義授權要求中的參數。

這個物件會使用來自 client_secret.json 檔案的資訊來辨識您的應用程式。 如要要求使用者擷取存取權杖,請將使用者重新導向至同意聲明頁面。 如何建立同意聲明頁面網址:

const {google} = require('googleapis');

/**
 * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI
 * from the client_secret.json file. To get these credentials for your application, visit
 * https://console.cloud.google.com/apis/credentials.
 */
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Access scopes for read-only Drive activity.
const scopes = [
  'https://www.googleapis.com/auth/drive.metadata.readonly'
];

// Generate a url that asks permissions for the Drive activity scope
const authorizationUrl = oauth2Client.generateAuthUrl({
  // 'online' (default) or 'offline' (gets refresh_token)
  access_type: 'offline',
  /** Pass in the scopes array defined above.
    * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
  scope: scopes,
  // Enable incremental authorization. Recommended as a best practice.
  include_granted_scopes: true
});

重要注意事項 - 系統只會在第一次授權時傳回 refresh_token。詳情請參閱這裡的說明

HTTP/REST

Google 的 OAuth 2.0 端點位於 https://accounts.google.com/o/oauth2/v2/auth。此端點只能透過 HTTPS 存取。系統會拒絕採用純 HTTP 連線。

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

參數
client_id 必要

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

redirect_uri 必要

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

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

response_type 必要

決定 Google OAuth 2.0 端點是否傳回授權碼。

將網路伺服器應用程式的參數值設為 code

scope 必要

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

範圍可讓您的應用程式只要求存取所需資源的存取權,同時還能讓使用者控管應用程式授予的存取權數量。因此,要求的範圍數和取得使用者同意聲明的可能性之間存在反向關係。

建議您讓應用程式盡可能在相關情況下要求授權範圍。請在相關使用情境中,透過漸進式授權要求存取權,幫助使用者進一步瞭解應用程式需要其存取權的原因。

access_type 建議使用

指出應用程式是否在使用者瀏覽器中重新整理時,可以重新整理存取權杖。有效的參數值為 online,也就是預設值和 offline

如果您的應用程式並未在使用者瀏覽器中重新整理權杖,請將值設為 offline。這是重新整理本文件稍後說明存取權杖的方法。這個值會指示 Google 授權伺服器在應用程式首次交換權杖的驗證權杖時傳回更新權杖「和」存取權杖。

state 建議使用

指定應用程式用來維持授權要求和授權伺服器回應狀態的任何字串值。使用者同意或拒絕應用程式存取要求後,伺服器會傳回 redirect_uri 網址查詢元件 (?) 中做為 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 提示使用者選取帳戶。

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

將使用者重新導向 Google 的 OAuth 2.0 伺服器,以啟動驗證和授權程序。此情況通常會發生在您的應用程式首次需要存取使用者的資料時。如果是漸進式授權,當應用程式首次需要存取尚未獲得存取權的其他資源時,也會發生這個步驟。

PHP

  1. 產生網址,向 Google 的 OAuth 2.0 伺服器要求存取權:
    $auth_url = $client->createAuthUrl();
  2. 將使用者重新導向 $auth_url
    header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));

Python

以下範例說明如何使用 Flask 網頁應用程式架構,將使用者重新導向至授權網址:

return flask.redirect(authorization_url)

Ruby

  1. 產生網址,向 Google 的 OAuth 2.0 伺服器要求存取權:
    auth_uri = auth_client.authorization_uri.to_s
  2. 將使用者重新導向 auth_uri

Node.js

  1. 使用步驟 1 generateAuthUrl 方法產生的網址 authorizationUrl,向 Google 的 OAuth 2.0 伺服器要求存取權。
  2. 將使用者重新導向 authorizationUrl
    res.writeHead(301, { "Location": authorizationUrl });

HTTP/REST

Sample redirect to Google's authorization server

An example URL is shown below, with line breaks and spaces for readability.

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

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

Google 的 OAuth 2.0 伺服器會驗證使用者,並取得使用者的同意,讓應用程式存取要求的範圍。系統會使用您指定的重新導向網址,將回應傳回應用程式。

步驟 3:Google 提示使用者提供同意聲明

在這個步驟中,使用者可以決定是否要授予應用程式要求的存取權。在這個階段,Google 會顯示同意視窗,當中會顯示您的應用程式名稱,以及應用程式要求存取使用者的授權憑證的 Google API 服務,並提供即將授予的存取權範圍摘要。接著,使用者便可同意授予應用程式一或多個一或多個範圍的存取權。

應用程式會在等待 Google 的 OAuth 2.0 伺服器的回應時,指出應用程式是否已授予任何存取權,因此您不必在這個階段採取任何行動。我們將在下列步驟中說明該回應。

錯誤

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

admin_policy_enforced

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

disallowed_useragent

授權端點會顯示於 Google #OAuth 2.0 政策不允許的嵌入式使用者代理程式中。

Android

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

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

iOS

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

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

org_internal

要求中的 OAuth 用戶端 ID 屬於某項專案,會限制在特定 Google Cloud 機構中存取 Google 帳戶。如要進一步瞭解這項設定,請參閱「設定 OAuth 同意畫面」說明文章中的使用者類型一節。

redirect_uri_mismatch

授權要求中傳遞的 redirect_uri 與 OAuth 用戶端 ID 的授權重新導向 URI 不符。查看 Google API Console Credentials page中的授權重新導向 URI。

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

OAuth 2.0 伺服器會使用要求中指定的網址,回應應用程式的存取要求。

如果使用者核准存取要求,回應就會包含授權碼。如果使用者未核准要求,回應會包含錯誤訊息。傳回到網路伺服器的授權碼或錯誤訊息會顯示在查詢字串中,如下所示:

錯誤回應:

https://oauth2.example.com/auth?error=access_denied

授權碼回應:

https://oauth2.example.com/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7

OAuth 2.0 伺服器回應範例

您可以點選下列範例網址來要求 Google 雲端硬碟檔案中的唯讀存取權,藉此測試這項程序:

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

完成 OAuth 2.0 流程後,系統應會將您重新導向至 http://localhost/oauth2callback,如果本機電腦在該檔案提供服務,可能會產生 404 NOT FOUND 錯誤。下一步是在使用者將使用者重新導向至應用程式時,傳回 URI 中傳回的資訊。

步驟 5:交換更新權杖和存取權杖的授權碼

網路伺服器收到授權碼後,就能交換授權碼給存取權杖。

PHP

如要交換存取權杖的授權碼,請使用 authenticate 方法:

$client->authenticate($_GET['code']);

您可以使用 getAccessToken 方法擷取存取權杖:

$access_token = $client->getAccessToken();

Python

在回呼頁面中,使用 google-auth 程式庫來驗證授權伺服器回應。接著,請使用 flow.fetch_token 方法交換回應中有關存取權杖的授權碼:

state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=['https://www.googleapis.com/auth/drive.metadata.readonly'],
    state=state)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)

authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)

# Store the credentials in the session.
# ACTION ITEM for developers:
#     Store user's access and refresh tokens in your data store if
#     incorporating this code into your real app.
credentials = flow.credentials
flask.session['credentials'] = {
    'token': credentials.token,
    'refresh_token': credentials.refresh_token,
    'token_uri': credentials.token_uri,
    'client_id': credentials.client_id,
    'client_secret': credentials.client_secret,
    'scopes': credentials.scopes}

Ruby

如要交換存取權杖的授權碼,請使用 fetch_access_token! 方法:

auth_client.code = auth_code
auth_client.fetch_access_token!

Node.js

如要交換存取權杖的授權碼,請使用 getToken 方法:

const url = require('url');

// Receive the callback from Google's OAuth 2.0 server.
if (req.url.startsWith('/oauth2callback')) {
  // Handle the OAuth 2.0 server response
  let q = url.parse(req.url, true).query;

  // Get access and refresh tokens (if access_type is offline)
  let { tokens } = await oauth2Client.getToken(q.code);
  oauth2Client.setCredentials(tokens);
}

HTTP/REST

如要交換存取權杖的授權碼,請呼叫 https://oauth2.googleapis.com/token 端點並設定下列參數:

欄位
client_id 從 API ConsoleCredentials page取得的用戶端 ID。
client_secret 從 API ConsoleCredentials page取得的用戶端密鑰。
code 初始要求傳回的授權碼。
grant_type 如 OAuth 2.0 規格所定義,這個欄位的值必須設為 authorization_code
redirect_uri 針對您的專案,在 API ConsoleCredentials page 中為指定的 client_id 列出的重新導向 URI。

下列程式碼片段是要求範例:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code

Google 回應存取要求的方式,是傳回含有短期存取權杖和更新權杖的 JSON 物件。請注意,只有當您的應用程式在向 Google 的授權伺服器發出初始要求時,將 access_type 參數設為 offline 時,才會傳回重新整理權杖。

回應中包含下列欄位:

欄位
access_token 應用程式為了授權 Google API 要求而傳送的權杖。
expires_in 存取權杖的剩餘生命週期 (以秒為單位)。
refresh_token 可用來取得新存取權杖的權杖。更新權杖的效力取決於使用者撤銷存取權。再次提醒您,如果您將初始參數的 access_type 參數設為 offline,才能讓這個欄位在 Google 授權伺服器中使用。
scope access_token 授予的存取權範圍,以空格分隔且區分大小寫的字串清單表示。
token_type 傳回的權杖類型。目前,這個欄位的值會一律設為 Bearer

下列程式碼片段是回應範例:

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

呼叫 Google API

PHP

如要使用存取權杖呼叫 Google API,請完成下列步驟:

  1. 如果您需要將存取權杖套用至新的 Google\Client 物件 (例如,將存取權杖儲存在使用者工作階段中),請使用 setAccessToken 方法:
    $client->setAccessToken($access_token);
  2. 為要呼叫的 API 建構服務物件。您可以為服務呼叫的 API 提供授權的 Google\Client 物件,藉此建構服務物件。例如,呼叫 Drive API:
    $drive = new Google\Service\Drive($client);
  3. 使用服務物件提供的介面向 API 服務發出要求。舉例來說,如要列出已驗證使用者的 Google 雲端硬碟中檔案,請按照下列步驟操作:
    $files = $drive->files->listFiles(array())->getItems();

Python

取得存取權杖後,您的應用程式就能使用該權杖,以代表特定使用者帳戶或服務帳戶來授權 API 要求。透過使用者專屬的授權憑證,針對您要呼叫的 API 建構服務物件,然後使用該物件提出已授權的 API 要求。

  1. 為要呼叫的 API 建構服務物件。呼叫 googleapiclient.discovery 程式庫的 build 方法,並提供 API 名稱和使用者憑證,藉此建構服務物件: 例如,呼叫 Drive API 版本 2:
    from googleapiclient.discovery import build
    
    drive = build('drive', 'v2', credentials=credentials)
  2. 使用服務物件提供的介面向 API 服務發出要求。舉例來說,如要列出已驗證使用者的 Google 雲端硬碟中檔案,請按照下列步驟操作:
    files = drive.files().list().execute()

Ruby

完成下列步驟,使用 auth_client 物件呼叫 Google API:

  1. 為要呼叫的 API 建構服務物件。例如,呼叫 Drive API 第 2 版:
    drive = Google::Apis::DriveV2::DriveService.new
  2. 在服務中設定憑證:
    drive.authorization = auth_client
  3. 使用服務物件提供的介面向 API 服務發出要求。舉例來說,如要列出已驗證使用者的 Google 雲端硬碟中檔案,請按照下列步驟操作:
    files = drive.list_files

或者,您也可以為方法提供 options 參數,為個別方法提供授權:

files = drive.list_files(options: { authorization: auth_client })

Node.js

取得存取權杖並將其設為 OAuth2 物件後,請使用該物件呼叫 Google API。您的應用程式可利用該權杖,代表指定使用者帳戶或服務帳戶來授權 API 要求。為要呼叫的 API 建構服務物件。

const { google } = require('googleapis');

// Example of using Google Drive API to list filenames in user's Drive.
const drive = google.drive('v3');
drive.files.list({
  auth: oauth2Client,
  pageSize: 10,
  fields: 'nextPageToken, files(id, name)',
}, (err1, res1) => {
  if (err1) return console.log('The API returned an error: ' + err1);
  const files = res1.data.files;
  if (files.length) {
    console.log('Files:');
    files.map((file) => {
      console.log(`${file.name} (${file.id})`);
    });
  } else {
    console.log('No files found.');
  }
});

HTTP/REST

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

您可以前往 OAuth 2.0 Playground 試用所有 Google API 並查看其範圍。

HTTP GET 範例

使用 Authorization: Bearer HTTP 標頭呼叫 drive.files 端點 (Drive Files API) 時,如下所示。請注意,您必須指定自己的存取權杖:

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

下面是針對已驗證的使用者,使用 access_token 查詢字串參數呼叫同一個 API:

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

完整範例

以下範例會在使用者驗證資料後,列印出使用者 Google 雲端硬碟中的 JSON 格式檔案清單,並允許應用程式存取使用者的雲端硬碟中繼資料。

PHP

如何執行這個範例:

  1. 在 API Console中,將本機電腦的網址新增至重新導向網址清單。例如,新增 http://localhost:8080
  2. 建立新目錄並進行變更。例如:
    mkdir ~/php-oauth2-example
    cd ~/php-oauth2-example
  3. 使用 Composer 安裝 PHP 適用的 Google API 用戶端程式庫
    composer require google/apiclient:^2.10
  4. 使用下列內容建立 index.phpoauth2callback.php 檔案。
  5. 請使用設定為提供 PHP 的網路伺服器執行範例。如果您使用的是 PHP 5.6 以上版本,可以使用 PHP' 的內建測試網路伺服器:
    php -S localhost:8080 ~/php-oauth2-example

index.php

<?php
require_once __DIR__.'/vendor/autoload.php';

session_start();

$client = new Google\Client();
$client->setAuthConfig('client_secrets.json');
$client->addScope(Google\Service\Drive::DRIVE_METADATA_READONLY);

if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
  $client->setAccessToken($_SESSION['access_token']);
  $drive = new Google\Service\Drive($client);
  $files = $drive->files->listFiles(array())->getItems();
  echo json_encode($files);
} else {
  $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php';
  header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}

OAuth2callback.php

<?php
require_once __DIR__.'/vendor/autoload.php';

session_start();

$client = new Google\Client();
$client->setAuthConfigFile('client_secrets.json');
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');
$client->addScope(Google\Service\Drive::DRIVE_METADATA_READONLY);

if (! isset($_GET['code'])) {
  $auth_url = $client->createAuthUrl();
  header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
  $client->authenticate($_GET['code']);
  $_SESSION['access_token'] = $client->getAccessToken();
  $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/';
  header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}

Python

這個範例使用 Flask 架構。這個程式碼會在 http://localhost:8080 執行網頁應用程式,方便您測試 OAuth 2.0 流程。如果前往該網址,您應該會看到四個連結:

  • 測試 API 要求:這個連結會指向嘗試執行範例 API 要求的網頁,如有需要,系統會啟動授權流程。如果成功,頁面會顯示 API 回應。
  • 直接測試驗證流程:這個連結會指向嘗試透過授權流程傳送使用者的網頁。應用程式要求取得權限,以代表使用者提交已獲授權的 API 要求。
  • 撤銷目前的憑證:這個連結會指向使用者撤銷已授予應用程式的權限頁面。
  • 清除 Flask 工作階段憑證:這個連結會清除儲存在 Flask 工作階段的授權憑證。這可讓您瞭解已授予應用程式權限的使用者,在新工作階段執行 API 要求時會發生什麼情況。此外,如果應用程式已撤銷使用者授予應用程式的權限,您的應用程式也會取得 API 回應,而應用程式仍會嘗試授權已撤銷的存取權杖。
# -*- coding: utf-8 -*-

import os
import flask
import requests

import google.oauth2.credentials
import google_auth_oauthlib.flow
import googleapiclient.discovery

# This variable specifies the name of a file that contains the OAuth 2.0
# information for this application, including its client_id and client_secret.
CLIENT_SECRETS_FILE = "client_secret.json"

# This OAuth 2.0 access scope allows for full read/write access to the
# authenticated user's account and requires requests to use an SSL connection.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
API_SERVICE_NAME = 'drive'
API_VERSION = 'v2'

app = flask.Flask(__name__)
# Note: A secret key is included in the sample so that it works.
# If you use this code in your application, replace this with a truly secret
# key. See https://flask.palletsprojects.com/quickstart/#sessions.
app.secret_key = 'REPLACE ME - this value is here as a placeholder.'


@app.route('/')
def index():
  return print_index_table()


@app.route('/test')
def test_api_request():
  if 'credentials' not in flask.session:
    return flask.redirect('authorize')

  # Load credentials from the session.
  credentials = google.oauth2.credentials.Credentials(
      **flask.session['credentials'])

  drive = googleapiclient.discovery.build(
      API_SERVICE_NAME, API_VERSION, credentials=credentials)

  files = drive.files().list().execute()

  # Save credentials back to session in case access token was refreshed.
  # ACTION ITEM: In a production app, you likely want to save these
  #              credentials in a persistent database instead.
  flask.session['credentials'] = credentials_to_dict(credentials)

  return flask.jsonify(**files)


@app.route('/authorize')
def authorize():
  # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps.
  flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
      CLIENT_SECRETS_FILE, scopes=SCOPES)

  # The URI created here must exactly match one of the authorized redirect URIs
  # for the OAuth 2.0 client, which you configured in the API Console. If this
  # value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch'
  # error.
  flow.redirect_uri = flask.url_for('oauth2callback', _external=True)

  authorization_url, state = flow.authorization_url(
      # Enable offline access so that you can refresh an access token without
      # re-prompting the user for permission. Recommended for web server apps.
      access_type='offline',
      # Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes='true')

  # Store the state so the callback can verify the auth server response.
  flask.session['state'] = state

  return flask.redirect(authorization_url)


@app.route('/oauth2callback')
def oauth2callback():
  # Specify the state when creating the flow in the callback so that it can
  # verified in the authorization server response.
  state = flask.session['state']

  flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
      CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
  flow.redirect_uri = flask.url_for('oauth2callback', _external=True)

  # Use the authorization server's response to fetch the OAuth 2.0 tokens.
  authorization_response = flask.request.url
  flow.fetch_token(authorization_response=authorization_response)

  # Store credentials in the session.
  # ACTION ITEM: In a production app, you likely want to save these
  #              credentials in a persistent database instead.
  credentials = flow.credentials
  flask.session['credentials'] = credentials_to_dict(credentials)

  return flask.redirect(flask.url_for('test_api_request'))


@app.route('/revoke')
def revoke():
  if 'credentials' not in flask.session:
    return ('You need to <a href="/authorize">authorize</a> before ' +
            'testing the code to revoke credentials.')

  credentials = google.oauth2.credentials.Credentials(
    **flask.session['credentials'])

  revoke = requests.post('https://oauth2.googleapis.com/revoke',
      params={'token': credentials.token},
      headers = {'content-type': 'application/x-www-form-urlencoded'})

  status_code = getattr(revoke, 'status_code')
  if status_code == 200:
    return('Credentials successfully revoked.' + print_index_table())
  else:
    return('An error occurred.' + print_index_table())


@app.route('/clear')
def clear_credentials():
  if 'credentials' in flask.session:
    del flask.session['credentials']
  return ('Credentials have been cleared.<br><br>' +
          print_index_table())


def credentials_to_dict(credentials):
  return {'token': credentials.token,
          'refresh_token': credentials.refresh_token,
          'token_uri': credentials.token_uri,
          'client_id': credentials.client_id,
          'client_secret': credentials.client_secret,
          'scopes': credentials.scopes}

def print_index_table():
  return ('<table>' +
          '<tr><td><a href="/test">Test an API request</a></td>' +
          '<td>Submit an API request and see a formatted JSON response. ' +
          '    Go through the authorization flow if there are no stored ' +
          '    credentials for the user.</td></tr>' +
          '<tr><td><a href="/authorize">Test the auth flow directly</a></td>' +
          '<td>Go directly to the authorization flow. If there are stored ' +
          '    credentials, you still might not be prompted to reauthorize ' +
          '    the application.</td></tr>' +
          '<tr><td><a href="/revoke">Revoke current credentials</a></td>' +
          '<td>Revoke the access token associated with the current user ' +
          '    session. After revoking credentials, if you go to the test ' +
          '    page, you should see an <code>invalid_grant</code> error.' +
          '</td></tr>' +
          '<tr><td><a href="/clear">Clear Flask session credentials</a></td>' +
          '<td>Clear the access token currently stored in the user session. ' +
          '    After clearing the token, if you <a href="/test">test the ' +
          '    API request</a> again, you should go back to the auth flow.' +
          '</td></tr></table>')


if __name__ == '__main__':
  # When running locally, disable OAuthlib's HTTPs verification.
  # ACTION ITEM for developers:
  #     When running in production *do not* leave this option enabled.
  os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'

  # Specify a hostname and port that are set as a valid redirect URI
  # for your API project in the Google API Console.
  app.run('localhost', 8080, debug=True)

Ruby

這個範例使用 Sinatra 架構。

require 'google/apis/drive_v2'
require 'google/api_client/client_secrets'
require 'json'
require 'sinatra'

enable :sessions
set :session_secret, 'setme'

get '/' do
  unless session.has_key?(:credentials)
    redirect to('/oauth2callback')
  end
  client_opts = JSON.parse(session[:credentials])
  auth_client = Signet::OAuth2::Client.new(client_opts)
  drive = Google::Apis::DriveV2::DriveService.new
  files = drive.list_files(options: { authorization: auth_client })
  "<pre>#{JSON.pretty_generate(files.to_h)}</pre>"
end

get '/oauth2callback' do
  client_secrets = Google::APIClient::ClientSecrets.load
  auth_client = client_secrets.to_authorization
  auth_client.update!(
    :scope => 'https://www.googleapis.com/auth/drive.metadata.readonly',
    :redirect_uri => url('/oauth2callback'))
  if request['code'] == nil
    auth_uri = auth_client.authorization_uri.to_s
    redirect to(auth_uri)
  else
    auth_client.code = request['code']
    auth_client.fetch_access_token!
    auth_client.client_secret = nil
    session[:credentials] = auth_client.to_json
    redirect to('/')
  end
end

Node.js

如何執行這個範例:

  1. 在 API Console中,將本機電腦的網址新增至重新導向網址清單。例如,加入 http://localhost
  2. 確認您已安裝維護 LTS、使用中的 LTS 或現行的 Node.js 版本。
  3. 建立新目錄並進行變更。例如:
    mkdir ~/nodejs-oauth2-example
    cd ~/nodejs-oauth2-example
  4. Install the Google API Client Library for Node.js using npm:
    npm install googleapis
  5. 使用下列內容建立 main.js 檔案。
  6. 執行範例:
    node .\main.js

main.js

const http = require('http');
const https = require('https');
const url = require('url');
const { google } = require('googleapis');

/**
 * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI.
 * To get these credentials for your application, visit
 * https://console.cloud.google.com/apis/credentials.
 */
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Access scopes for read-only Drive activity.
const scopes = [
  'https://www.googleapis.com/auth/drive.metadata.readonly'
];

// Generate a url that asks permissions for the Drive activity scope
const authorizationUrl = oauth2Client.generateAuthUrl({
  // 'online' (default) or 'offline' (gets refresh_token)
  access_type: 'offline',
  /** Pass in the scopes array defined above.
    * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
  scope: scopes,
  // Enable incremental authorization. Recommended as a best practice.
  include_granted_scopes: true
});

/* Global variable that stores user credential in this code example.
 * ACTION ITEM for developers:
 *   Store user's refresh token in your data store if
 *   incorporating this code into your real app.
 *   For more information on handling refresh tokens,
 *   see https://github.com/googleapis/google-api-nodejs-client#handling-refresh-tokens
 */
let userCredential = null;

async function main() {
  const server = http.createServer(async function (req, res) {
    // Example on redirecting user to Google's OAuth 2.0 server.
    if (req.url == '/') {
      res.writeHead(301, { "Location": authorizationUrl });
    }

    // Receive the callback from Google's OAuth 2.0 server.
    if (req.url.startsWith('/oauth2callback')) {
      // Handle the OAuth 2.0 server response
      let q = url.parse(req.url, true).query;

      if (q.error) { // An error response e.g. error=access_denied
        console.log('Error:' + q.error);
      } else { // Get access and refresh tokens (if access_type is offline)
        let { tokens } = await oauth2Client.getToken(q.code);
        oauth2Client.setCredentials(tokens);

        /** Save credential to the global variable in case access token was refreshed.
          * ACTION ITEM: In a production app, you likely want to save the refresh token
          *              in a secure persistent database instead. */
        userCredential = tokens;

        // Example of using Google Drive API to list filenames in user's Drive.
        const drive = google.drive('v3');
        drive.files.list({
          auth: oauth2Client,
          pageSize: 10,
          fields: 'nextPageToken, files(id, name)',
        }, (err1, res1) => {
          if (err1) return console.log('The API returned an error: ' + err1);
          const files = res1.data.files;
          if (files.length) {
            console.log('Files:');
            files.map((file) => {
              console.log(`${file.name} (${file.id})`);
            });
          } else {
            console.log('No files found.');
          }
        });
      }
    }

    // Example on revoking a token
    if (req.url == '/revoke') {
      // Build the string for the POST request
      let postData = "token=" + userCredential.access_token;

      // Options for POST request to Google's OAuth 2.0 server to revoke a token
      let postOptions = {
        host: 'oauth2.googleapis.com',
        port: '443',
        path: '/revoke',
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          'Content-Length': Buffer.byteLength(postData)
        }
      };

      // Set up the request
      const postReq = https.request(postOptions, function (res) {
        res.setEncoding('utf8');
        res.on('data', d => {
          console.log('Response: ' + d);
        });
      });

      postReq.on('error', error => {
        console.log(error)
      });

      // Post the request with data
      postReq.write(postData);
      postReq.end();
    }
    res.end();
  }).listen(80);
}
main().catch(console.error);

HTTP/REST

這個 Python 範例使用 Flask 架構和 Requests 程式庫來示範 OAuth 2.0 網路流程。針對這個流程,建議您使用 Python 適用的 Google API 用戶端程式庫。(Python 中的範例確實使用用戶端程式庫)。

import json

import flask
import requests


app = flask.Flask(__name__)

CLIENT_ID = '123456789.apps.googleusercontent.com'
CLIENT_SECRET = 'abc123'  # Read from a file or environmental variable in a real app
SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly'
REDIRECT_URI = 'http://example.com/oauth2callback'


@app.route('/')
def index():
  if 'credentials' not in flask.session:
    return flask.redirect(flask.url_for('oauth2callback'))
  credentials = json.loads(flask.session['credentials'])
  if credentials['expires_in'] <= 0:
    return flask.redirect(flask.url_for('oauth2callback'))
  else:
    headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])}
    req_uri = 'https://www.googleapis.com/drive/v2/files'
    r = requests.get(req_uri, headers=headers)
    return r.text


@app.route('/oauth2callback')
def oauth2callback():
  if 'code' not in flask.request.args:
    auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code'
                '&client_id={}&redirect_uri={}&scope={}').format(CLIENT_ID, REDIRECT_URI, SCOPE)
    return flask.redirect(auth_uri)
  else:
    auth_code = flask.request.args.get('code')
    data = {'code': auth_code,
            'client_id': CLIENT_ID,
            'client_secret': CLIENT_SECRET,
            'redirect_uri': REDIRECT_URI,
            'grant_type': 'authorization_code'}
    r = requests.post('https://oauth2.googleapis.com/token', data=data)
    flask.session['credentials'] = r.text
    return flask.redirect(flask.url_for('index'))


if __name__ == '__main__':
  import uuid
  app.secret_key = str(uuid.uuid4())
  app.debug = False
  app.run()

重新導向 URI 驗證規則

Google 將下列驗證規則重新導向 URI,以協助開發人員確保應用程式安全無虞。您的重新導向 URI 必須遵守這些規則。如需網域、主機、路徑、查詢、配置和使用者資訊的定義,請參閱 RFC 3986 第 3 節

驗證規則
配置

重新導向 URI 必須使用 HTTPS 配置,而非純 HTTP。本機主機 URI (包括 localhost IP 位址 URI) 不受此規則影響。

主機

主機不可以是原始 IP 位址。本機主機 IP 位址排除在這項規則的適用範圍之外。

網域
  • 主機 TLD (頂層網域) 必須屬於公開尾碼清單
  • 主機網域不能為 “googleusercontent.com”
  • 除非應用程式擁有網域,否則重新導向 URI 不得包含網址縮短網域 (例如 goo.gl)。此外,如果擁有較短網域的應用程式選擇重新導向至該網域,則該重新導向 URI 的路徑中必須包含 “/google-callback/”,或是以 “/google-callback” 結尾。
  • 使用者資訊

    重新導向 URI 不得包含 userinfo 子元件。

    路徑

    重新導向 URI 不得包含路徑週遊類型 (也稱為目錄反向追蹤),由 “/..”“\..” 或其網址編碼表示。

    查詢

    重新導向 URI 不得包含開放式重新導向

    片段

    重新導向 URI 不得包含片段元件。

    字元 重新導向 URI 不得包含下列字元:
    • 萬用字元 ('*')
    • 不可列印的 ASCII 字元
    • 百分比編碼無效 (任何百分比均不遵循百分比編碼符號,後面接著兩個十六進位數字)
    • 空值字元 (例如已編碼的 NULL 字元,例如%00%C0%80)

    漸進式授權

    在 OAuth 2.0 通訊協定中,應用程式會要求存取資源 (系統會透過範圍來辨識資源)。如需在需要時要求資源授權,最佳做法是提供最佳使用者體驗。為了實踐這個做法,Google 的授權伺服器支援漸進式授權。這項功能可讓您視需求要求範圍,如果使用者授予新範圍的權限,則可能會傳回授權碼,以做為使用者授予專案的所有範圍的權杖。

    舉例來說,允許使用者取樣的音樂曲目並建立混音作品的應用程式,需要在登入時使用的資源就不多,甚至比登入者的名稱來得少。但請注意,儲存已完成的組合需要存取 Google 雲端硬碟。假如使用者在應用程式實際需要存取 Google 雲端硬碟時要求提供 Google 雲端硬碟的存取權,多數人會覺得很自然。

    在這種情況下,應用程式可在登入時要求 openidprofile 範圍執行基本登入,然後在第一次要求時儲存 https://www.googleapis.com/auth/drive.file 範圍以儲存組合。

    如要實作漸進式授權,您必須完成一般要求存取權杖,但請確認授權要求含有先前授予的範圍。這個方法可讓應用程式不必管理多個存取權杖。

    下列規則適用於透過漸進式授權取得的存取權杖:

    • 權杖可用於存取與新組合授權結合的任何範圍對應的資源。
    • 使用合併權杖進行合併授權以取得存取權杖時,存取權杖代表合併的授權,並可用於回應中的 scope 值。
    • 組合授權包括使用者授予 API 專案的所有範圍,即使要求來自不同用戶端也一樣。舉例來說,如果使用者透過應用程式用戶端用戶端授予某個範圍的存取權,然後透過行動用戶端將同一個範圍授予同一個應用程式,則合併授權會同時包含這兩個範圍。
    • 如果您撤銷代表合併授權的權杖,則代表關聯使用者的所有授權範圍存取權都會同時撤銷。

    步驟 1:設定授權參數」中的語言專屬程式碼範例,以及步驟 2:重新導向至 Google 的 OAuth 2.0 伺服器中的 HTTP/REST 重新導向網址範例,均使用漸進式授權。下列程式碼範例也會顯示使用漸進式授權所需的程式碼。

    PHP

    $client->setIncludeGrantedScopes(true);

    Python

    在 Python 中,將 include_granted_scopes 關鍵字引數設為 true,確保授權要求包含先前授予的範圍。include_granted_scopes 很有可能不是您「唯一」設定的關鍵字引數,如下方範例所示。

    authorization_url, state = flow.authorization_url(
        # Enable offline access so that you can refresh an access token without
        # re-prompting the user for permission. Recommended for web server apps.
        access_type='offline',
        # Enable incremental authorization. Recommended as a best practice.
        include_granted_scopes='true')

    Ruby

    auth_client.update!(
      :additional_parameters => {"include_granted_scopes" => "true"}
    )

    Node.js

    const authorizationUrl = oauth2Client.generateAuthUrl({
      // 'online' (default) or 'offline' (gets refresh_token)
      access_type: 'offline',
      /** Pass in the scopes array defined above.
        * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
      scope: scopes,
      // Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes: true
    });
    

    HTTP/REST

    GET https://accounts.google.com/o/oauth2/v2/auth?
      client_id=your_client_id&
      response_type=code&
      state=state_parameter_passthrough_value&
      scope=https%3A//www.googleapis.com/auth/drive.file&
      redirect_uri=https%3A//oauth2.example.com/code&
      prompt=consent&
      include_granted_scopes=true

    重新整理存取權杖 (離線存取)

    存取權杖會定期失效,且會成為相關 API 要求的無效憑證。如果您要求取得與權杖相關的範圍的離線存取權,可以在不提示使用者 (包括使用者不存在) 的情況下重新整理存取權杖。

    • 如果您使用 Google API 用戶端程式庫,用戶端物件在您設定該物件的離線存取權時,請視需要重新整理存取權杖。
    • 如果您不使用用戶端程式庫,就必須將使用者重新導向至 Google #OAuth 2.0 伺服器時,將 access_type HTTP 查詢參數設為 offline。在這種情況下,Google 的授權伺服器會在交換存取權杖時傳回重新整理權杖。接著,如果存取權杖過期 (或任何其他時間),您就能使用更新權杖來取得新的存取權杖。

    在使用者不存在 Google API 的情況下,凡是需要存取 Google API 的應用程式,都必須要求離線存取功能。例如,如果應用程式在執行備份服務或預先指定時間執行備份服務,則需要在使用者不存在的情況下重新整理資料。預設存取權樣式稱為 online

    伺服器端網頁應用程式、已安裝的應用程式和裝置在授權過程中都會取得更新權杖。重新整理權杖通常不用於用戶端 (JavaScript) 網頁應用程式。

    PHP

    如果應用程式需要離線存取 Google API,請將 API 用戶端的存取權類型設為 offline

    $client->setAccessType("offline");

    在使用者授予對指定範圍的離線存取權後,您可以繼續透過 API 用戶端,在使用者離線時代表 API 存取 Google API。用戶端物件會視需要重新整理存取權杖。

    Python

    在 Python 中,將 access_type 關鍵字引數設為 offline,確保您能夠重新整理使用者權杖,而無須再次提示使用者授予權限。access_type 很有可能不是您「唯一」設定的關鍵字引數,如下方範例所示。

    authorization_url, state = flow.authorization_url(
        # Enable offline access so that you can refresh an access token without
        # re-prompting the user for permission. Recommended for web server apps.
        access_type='offline',
        # Enable incremental authorization. Recommended as a best practice.
        include_granted_scopes='true')

    在使用者授予對指定範圍的離線存取權後,您可以繼續透過 API 用戶端,在使用者離線時代表 API 存取 Google API。用戶端物件會視需要重新整理存取權杖。

    Ruby

    如果應用程式需要離線存取 Google API,請將 API 用戶端的存取權類型設為 offline

    auth_client.update!(
      :additional_parameters => {"access_type" => "offline"}
    )

    在使用者授予對指定範圍的離線存取權後,您可以繼續透過 API 用戶端,在使用者離線時代表 API 存取 Google API。用戶端物件會視需要重新整理存取權杖。

    Node.js

    如果應用程式需要離線存取 Google API,請將 API 用戶端的存取權類型設為 offline

    const authorizationUrl = oauth2Client.generateAuthUrl({
      // 'online' (default) or 'offline' (gets refresh_token)
      access_type: 'offline',
      /** Pass in the scopes array defined above.
        * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
      scope: scopes,
      // Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes: true
    });
    

    在使用者授予對指定範圍的離線存取權後,您可以繼續透過 API 用戶端,在使用者離線時代表 API 存取 Google API。用戶端物件會視需要重新整理存取權杖。

    存取憑證會過期。如果這個程式庫即將到期,這個程式庫會自動使用重新整理權杖來取得新的存取權杖。如要確保一律會儲存最新的權杖,最簡單的方法是使用權杖事件:

    oauth2Client.on('tokens', (tokens) => {
      if (tokens.refresh_token) {
        // store the refresh_token in your secure persistent database
        console.log(tokens.refresh_token);
      }
      console.log(tokens.access_token);
    });

    這個權杖事件只會在第一個授權中發生,您必須在呼叫 generateAuthUrl 方法時將 access_type 設為 offline,才能接收更新權杖。如果您已向應用程式授予所需權限,但未設定適當的接收接收權杖限制,就必須重新授權應用程式以取得新的更新權杖。

    如要稍後再設定 refresh_token,請使用 setCredentials 方法:

    oauth2Client.setCredentials({
      refresh_token: `STORED_REFRESH_TOKEN`
    });
    

    當用戶端有更新權杖之後,系統就會自動取得存取權杖,並在下次呼叫 API 時重新整理。

    HTTP/REST

    如要重新整理存取權杖,您的應用程式會將 HTTPS POST 要求傳送至 Google 的授權伺服器 (https://oauth2.googleapis.com/token),其中包含下列參數:

    欄位
    client_id API Console取得的用戶端 ID。
    client_secret API Console取得的用戶端密鑰。
    grant_type OAuth 2.0 規格定義,這個欄位的值必須設為 refresh_token
    refresh_token 從授權碼交換所傳回的更新權杖。

    下列程式碼片段是要求範例:

    POST /token HTTP/1.1
    Host: oauth2.googleapis.com
    Content-Type: application/x-www-form-urlencoded
    
    client_id=your_client_id&
    client_secret=your_client_secret&
    refresh_token=refresh_token&
    grant_type=refresh_token

    只要使用者尚未撤銷已授予應用程式的存取權,權杖伺服器就會傳回包含新存取權杖的 JSON 物件。下列程式碼片段顯示回應範例:

    {
      "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
      "expires_in": 3920,
      "scope": "https://www.googleapis.com/auth/drive.metadata.readonly",
      "token_type": "Bearer"
    }

    請注意,要核發的重新整理權杖數量設有上限,每個用戶端/使用者組合都有一個限制,且所有用戶端的每位使用者都有一個限制。請將更新權杖儲存在長期儲存空間中,只要權杖持續有效,就能繼續使用。如果您的應用程式要求更新的重新整理權杖數量過多,就有可能達到這些限制,這時舊的更新權杖將會停止運作。

    撤銷憑證

    在某些情況下,使用者可能會想撤銷先前授予應用程式的存取權。使用者可以透過前往帳戶設定來撤銷存取權。詳情請參閱「第三方網站」中的網站或應用程式存取權部分和支援文件的應用程式。

    您也可以讓應用程式以程式輔助的方式撤銷應用程式的存取權。如果使用者取消訂閱、移除應用程式或應用程式所需的 API 資源有大幅變動,程式輔助撤銷機制就相當重要。換句話說,在移除程序中的一部分可包含 API 要求,確保已移除先前授予應用程式的權限。

    PHP

    如要透過程式輔助撤銷權杖,請呼叫 revokeToken()

    $client->revokeToken();

    Python

    如要透過程式撤銷權杖,請向 https://oauth2.googleapis.com/revoke 發出要求,其中包含權杖做為參數,並設定 Content-Type 標頭:

    requests.post('https://oauth2.googleapis.com/revoke',
        params={'token': credentials.token},
        headers = {'content-type': 'application/x-www-form-urlencoded'})

    Ruby

    如要透過程式撤銷權杖,請向 oauth2.revoke 端點發出 HTTP 要求:

    uri = URI('https://oauth2.googleapis.com/revoke')
    response = Net::HTTP.post_form(uri, 'token' => auth_client.access_token)
    

    權杖可以是存取權杖或更新權杖。如果權杖是存取權杖,且具有對應的重新整理權杖,則系統會一併撤銷更新權杖。

    如果撤銷作業成功,回應的狀態碼為 200。如果是錯誤條件,系統會傳回狀態碼 400 和錯誤代碼。

    Node.js

    如要透過程式撤銷權杖,請向 /revoke 端點發出 HTTPS POST 要求:

    const https = require('https');
    
    // Build the string for the POST request
    let postData = "token=" + userCredential.access_token;
    
    // Options for POST request to Google's OAuth 2.0 server to revoke a token
    let postOptions = {
      host: 'oauth2.googleapis.com',
      port: '443',
      path: '/revoke',
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(postData)
      }
    };
    
    // Set up the request
    const postReq = https.request(postOptions, function (res) {
      res.setEncoding('utf8');
      res.on('data', d => {
        console.log('Response: ' + d);
      });
    });
    
    postReq.on('error', error => {
      console.log(error)
    });
    
    // Post the request with data
    postReq.write(postData);
    postReq.end();
    

    權杖參數可以是存取權杖或更新權杖。如果權杖是存取權杖,且具有對應的重新整理權杖,則系統會一併撤銷更新權杖。

    如果撤銷作業成功,回應的狀態碼為 200。如果是錯誤條件,系統會傳回狀態碼 400 和錯誤代碼。

    HTTP/REST

    如要透過程式撤銷權杖,應用程式會向 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 以及錯誤代碼。