將 OAuth 2.0 用於網路伺服器應用程式

本文件說明網路伺服器應用程式如何使用 Google API 用戶端程式庫 用來執行 OAuth 2.0 授權的 OAuth 2.0 端點 Google API

OAuth 2.0 可讓使用者與應用程式共用特定資料,同時維持 使用者名稱、密碼以及其他資訊隱私 舉例來說,應用程式可以使用 OAuth 2.0 取得 讓使用者在 Google 雲端硬碟中儲存檔案。

這個 OAuth 2.0 流程僅適用於使用者授權。專為應用程式而設計 可以儲存機密資訊及維護狀態經過適當授權的網路伺服器 當使用者與應用程式互動時,或在使用者之後與應用程式互動時,應用程式都能存取 API 已離開應用程式。

網路伺服器應用程式經常使用 服務帳戶授權 API 要求,特別是在呼叫 Cloud API 來存取要求時 以專案為基礎的資料,而非使用者專屬資料網路伺服器應用程式可以使用 Service 結合使用者授權

用戶端程式庫

本頁所列語言範例 要導入的 Google API 用戶端程式庫 OAuth 2.0 授權。如要執行程式碼範例,您必須先安裝 用戶端程式庫。

使用 Google API 用戶端程式庫處理應用程式的 OAuth 2.0 流程時,用戶端 程式庫會執行許多應用程式,必須自行處理這些動作。適用對象 例如決定應用程式何時可以使用或更新儲存的存取權杖 應用程式必須取得同意聲明。用戶端程式庫也會產生正確的重新導向 網址並協助實作重新導向處理常式,以交換存取權杖的授權碼。

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

,瞭解如何調查及移除這項存取權。

必要條件

為專案啟用 API

凡是呼叫 Google 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,然後按一下「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. 選取「網頁應用程式」應用程式類型。
  4. 填寫表單,然後按一下「建立」。使用語言與架構的應用程式 例如 PHP、Java、Python、Ruby 和 .NET ,您必須指定授權的重新導向 URI。 重新導向 URI 是 OAuth 2.0 伺服器可傳送回應的端點。這些 端點必須遵循 Google 的驗證規則

    如要進行測試,可以指定參照本機電腦的 URI,例如 http://localhost:8080。在此提醒您,所有 本文件的範例會使用 http://localhost:8080 做為重新導向 URI。

    建議您設計應用程式的驗證端點, 您的應用程式並未將授權碼公開給 頁面。

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

識別存取權範圍

限定範圍可讓應用程式只要求存取其所需資源, 讓使用者控制他們授予應用程式的存取權限量。因此, 可能是要求的範圍數量與 徵得使用者同意

建議您先確定執行 OAuth 2.0 授權的範圍後,再開始執行 OAuth 2.0 授權 才能存取您的應用程式

此外,建議您讓應用程式透過 漸進式授權程序, 會在相關情境下要求存取使用者資料。這項最佳做法有助於使用者 為何應用程式需要其要求的存取權。

OAuth 2.0 API 範圍文件提供了 列出可能用於存取 Google API 的範圍清單。

特定語言規定

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

PHP

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

  • PHP 5.6 以上版本,並且已安裝指令列介面 (CLI) 和 JSON 擴充功能。
  • 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 2.6 以上版本
  • Ruby 適用的 Google 驗證程式庫:

    gem install googleauth
  • Sinatra Ruby 網頁應用程式架構。

    gem install sinatra

Node.js

如要執行本文件中的 Node.js 程式碼範例,您需要:

  • Node.js 的維護 LTS、運作中的 LTS 或現行版本。
  • Google API Node.js 用戶端:

    npm install googleapis crypto express express-session

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

// Required, call the setAuthConfig function to load authorization credentials from
// client_secret.json file.
$client->setAuthConfig('client_secret.json');

// Required, to set the scope value, call the addScope function
$client->addScope(Google\Service\Drive::DRIVE_METADATA_READONLY);

// Required, call the setRedirectUri function to specify a valid redirect URI for the
// provided client_id
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');

// Recommended, 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');

// Recommended, call the setState function. Using a state value can increase your assurance that
// an incoming connection is the result of an authentication request.
$client->setState($sample_passthrough_value);

// Optional, if your application knows which user is trying to authenticate, it can use this
// parameter to provide a hint to the Google Authentication Server.
$client->setLoginHint('hint@example.com');

// Optional, call the setPrompt function to set "consent" will prompt the user for consent
$client->setPrompt('consent');

// Optional, call the setIncludeGrantedScopes function with true to enable incremental
// authorization
$client->setIncludeGrantedScopes(true);

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

# Required, call the from_client_secrets_file method to retrieve the client ID from a
# client_secret.json file. The client ID (from that file) and access scopes are required. (You can
# also use the from_client_config method, which passes the client configuration as it originally
# appeared in a client secrets file but doesn't access the file itself.)
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=['https://www.googleapis.com/auth/drive.metadata.readonly'])

# Required, 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(
    # Recommended, 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',
    # Optional, enable incremental authorization. Recommended as a best practice.
    include_granted_scopes='true',
    # Optional, if your application knows which user is trying to authenticate, it can use this
    # parameter to provide a hint to the Google Authentication Server.
    login_hint='hint@example.com',
    # Optional, set prompt to 'consent' will prompt the user for consent
    prompt='consent')

小茹

使用您建立的 client_secrets.json 檔案來設定 應用程式。設定用戶端物件時,您會指定應用程式需要的範圍 存取,以及應用程式驗證端點的網址,這是處理回應 進行存取。

舉例來說,這個程式碼要求使用者的唯讀存取權, Google 雲端硬碟:

require 'google/apis/drive_v3'
require "googleauth"
require 'googleauth/stores/redis_token_store'

client_id = Google::Auth::ClientId.from_file('/path/to/client_secret.json')
scope = 'https://www.googleapis.com/auth/drive.metadata.readonly'
token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)
authorizer = Google::Auth::WebUserAuthorizer.new(client_id, scope, token_store, '/oauth2callback')

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

Node.js

下列程式碼片段會建立 google.auth.OAuth2 物件,並定義 參數。

這個物件會使用 client_secret.json 檔案中的資訊來識別您的應用程式。目的地: 向使用者要求權限以擷取存取權杖,您將他們重新導向至同意頁面。 建立同意頁面網址的方法如下:

const {google} = require('googleapis');
const crypto = require('crypto');
const express = require('express');
const session = require('express-session');

/**
 * 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 secure random state value.
const state = crypto.randomBytes(32).toString('hex');

// Store state in the session
req.session.state = state;

// 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,
  // Include the state parameter to reduce the risk of CSRF attacks.
  state: state
});

重要注意事項 - refresh_token只會傳回第一個 或授權。更多詳細資料 此處

HTTP/REST

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

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

參數
client_id 必填

應用程式的用戶端 ID。您可以在 API Console Credentials page

redirect_uri 必填

決定 API 伺服器在使用者完成提示後,將他們重新導向何處 授權流程。這個值必須與其中一個已授權的重新導向 URI 完全相符 OAuth 2.0 用戶端,也就是您在用戶端的 API Console Credentials page。如果這個值與 提供的 client_id 授權重新導向 URI redirect_uri_mismatch 個錯誤。

請注意,httphttps 配置、大小寫和結尾斜線 (「/」) 必須全部相符。

response_type 必填

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

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

scope 必填

A 罩杯 以空格分隔 這些範圍清單會指出應用程式可在 代表使用者這些值決定 Google 向 內容。

限定範圍可讓應用程式只要求存取其所需資源 並讓使用者控制授予 應用程式。因此,要求的範圍數量之間存在反向關係 以及取得使用者同意聲明的可能性

建議您讓應用程式要求授權範圍的存取權 。透過以下方式,要求存取使用者資料: 漸進式授權,可讓使用者更輕鬆地 瞭解應用程式為何需要其要求的存取權。

access_type 建議事項

指出在沒有使用者的情況下,您的應用程式是否可以重新整理存取權杖 。有效的參數值為 online (預設值) 值和 offline

如果您的應用程式需要更新存取權杖,請將值設為 offline 當使用者不在瀏覽器上時。這是重新整理存取權的方法 符記這個值代表 Google 授權 初次存取更新權杖,「以及」存取更新權杖時 應用程式會將授權碼交換權杖

state 建議事項

指定應用程式用來維持 授權要求和授權伺服器的回應。 伺服器會傳回您以 name=value 組合形式傳送的確切值, 網址查詢元件 (?) redirect_uri使用者同意或拒絕應用程式的 存取要求。

這個參數可用於多種用途,例如將使用者導向 在應用程式中使用正確的資源、傳送 Nonce 以及降低跨網站要求 偽造。由於您可以猜測 redirect_uri,因此使用 state 值可讓您確保傳入連線的結果是 驗證要求如果您產生隨機字串或將 Cookie 雜湊編碼 另一個會擷取用戶端狀態的值,您可以驗證 也要確保請求和回應都來自相同的瀏覽器。 提供網路攻擊防護功能 跨網站要求 偽造。詳情請參閱 OpenID Connect 範例,說明如何建立並確認 state 權杖。

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)

小茹

  1. 產生網址以向 Google 的 OAuth 2.0 伺服器要求存取權:
    auth_uri = authorizer.get_authorization_url(login_hint: user_id, request: request)
  2. 將使用者重新導向至 auth_uri

Node.js

  1. 使用步驟 1 中產生的網址 authorizationUrl generateAuthUrl 方法,向 Google 的 OAuth 2.0 伺服器要求存取權。
  2. 將使用者重新導向至 authorizationUrl
    res.redirect(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 帳戶無法授權一或多個要求的範圍 Google Workspace 管理員參閱 Google Workspace 管理員說明文章 控管哪些第三方和內部應用程式存取 Google Workspace 資料 進一步瞭解管理員可能如何限制所有範圍或敏感資料的存取權,以及 受到限制,直到已明確授予 OAuth 用戶端 ID 存取權為止。

disallowed_useragent

授權端點會顯示在 Google 的 OAuth 2.0 政策

Android

Android 開發人員在 android.webkit.WebView。 開發人員應改用 Android 程式庫,例如 Android 版 Google 登入或 OpenID Foundation 的 AppAuth for Android

當 Android 應用程式在 內嵌使用者代理程式,而使用者前往 Google 的 OAuth 2.0 授權端點 你的網站開發人員應允許使用預設連結處理常式開啟一般連結, 其中包括 Android 應用程式連結 處理常式或預設的瀏覽器應用程式中。 Android 自訂分頁 另一個支援選項

iOS

iOS 和 macOS 開發人員在 WKWebView。 開發人員應改用 iOS 程式庫,例如 iOS 版 Google 登入或 OpenID Foundation 的 AppAuth for iOS

當 iOS 或 macOS 應用程式在 內嵌使用者代理程式,然後使用者透過 你的網站開發人員應允許使用預設連結處理常式開啟一般連結, 其中包括 通用連結 處理常式或預設的瀏覽器應用程式中。 SFSafariViewController 另一個支援選項

org_internal

請求中的 OAuth 用戶端 ID 所屬的專案,限制了 Google 帳戶存取 特定 Google Cloud 機構。 如要進一步瞭解這個設定選項,請參閱 使用者類型 部分。

invalid_client

OAuth 用戶端密鑰不正確。詳閱 OAuth 用戶端 設定,包括用於這項要求的用戶端 ID 和密鑰。

invalid_grant

更新存取權杖或使用 漸進式授權,權杖可能已過期或 已失效。 再次驗證使用者,並要求使用者同意取得新的權杖。如果您選擇繼續 看到這則錯誤訊息,請確認您的應用程式設定正確, 要求使用正確的符記和參數否則,使用者帳戶 可能已遭刪除或停用

redirect_uri_mismatch

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

redirect_uri 參數可能是指 OAuth 的架構外 (OOB) 流程, 已淘汰,不再受到支援。詳情請參閱 遷移指南,以更新 擷取及準備資料、針對特定領域進行預先訓練 調整指示、離線評估和整合

invalid_request

您提出的要求發生問題。可能原因如下:

  • 要求的格式不正確
  • 要求中缺少必要參數
  • 這項要求使用了 Google 不支援的授權方法。驗證 OAuth 整合採用建議的整合方式

步驟 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 錯誤,除非本機機器在該位址提供檔案。 下一步則是詳述使用者 重新導向回您的應用程式。

步驟 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}

小茹

在回呼頁面上,使用 googleauth 程式庫驗證授權伺服器 回應。使用 authorizer.handle_auth_callback_deferred 方法儲存 授權碼,並重新導向回原先要求授權的網址。這個 暫時在使用者的工作階段中儲存結果,藉此延後程式碼交換作業。

  target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)
  redirect target_url

Node.js

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

const url = require('url');

// Receive the callback from Google's OAuth 2.0 server.
app.get('/oauth2callback', async (req, res) => {
  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 if (q.state !== req.session.state) { //check state value
    console.log('State mismatch. Possible CSRF attack');
    res.end('State mismatch. Possible CSRF attack');
  } else { // 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 Console取得的用戶端 ID Credentials page
client_secret 從 API Console取得的用戶端密鑰 Credentials page
code 初始要求傳回的授權碼。
grant_type 根據 OAuth 2.0 中的定義 規格,這個欄位的值必須設為 authorization_code
redirect_uri 在 API Console Credentials page 已提供 client_id

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

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 物件,藉此回應這項要求 權杖和重新整理權杖 請注意,只有在應用程式設定了 access_type 的情況下,系統才會傳回更新權杖 加到 offlineGoogle 授權伺服器

回應中會包含以下欄位:

欄位
access_token 您的應用程式為授權 Google API 要求而傳送的憑證。
expires_in 存取權杖的剩餘生命週期 (以秒為單位)。
refresh_token 可用來取得新的存取權杖的憑證。在 使用者撤銷存取權。 再次提醒您,如果您設定 access_type,則這個欄位只會顯示在回應中 參數加到 Google 授權伺服器的第一個要求中 offline
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"
}

錯誤

交換存取權杖的授權碼時,您可能會看到下列內容: 錯誤,而非預期的回應。常見的錯誤代碼和建議解決方法如下: 。

invalid_grant

提供的授權碼無效或格式錯誤。索取新驗證碼的方式如下: 重新啟動 OAuth 程序,以提示使用者徵求同意 可以選取「重新建立」,再次生成新的提示

呼叫 Google API

PHP

完成下列步驟,即可使用存取權杖呼叫 Google API:

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

Python

取得存取權杖後,應用程式即可使用該權杖授權於 委派代表角色使用使用者專屬的授權憑證 為您要呼叫的 API 建立服務物件,然後使用該物件 已授權的 API 要求

  1. 為您要呼叫的 API 建立服務物件。您可以透過 使用googleapiclient.discoverybuild 以及使用者憑證 舉例來說,如要呼叫 Drive API 第 3 版,請按照下列步驟操作:
    from googleapiclient.discovery import build
    
    drive = build('drive', 'v2', credentials=credentials)
  2. 使用 服務物件提供的介面。 舉例來說,如要列出已驗證使用者的 Google 雲端硬碟檔案,請按照下列指示操作:
    files = drive.files().list().execute()

小茹

取得存取權杖後,應用程式即可使用該權杖向 委派代表角色使用使用者專屬的授權憑證 為您要呼叫的 API 建立服務物件,然後使用該物件 已授權的 API 要求

  1. 為您要呼叫的 API 建立服務物件。 舉例來說,如要呼叫 Drive API 第 3 版,請按照下列步驟操作:
    drive = Google::Apis::DriveV3::DriveService.new
  2. 設定服務的憑證:
    drive.authorization = credentials
  3. 使用 介面 服務物件 舉例來說,如要列出已驗證使用者的 Google 雲端硬碟檔案,請按照下列指示操作:
    files = drive.list_files

或者,也可以依個別方法提供授權,方法是將 將 options 參數套用至方法:

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

Node.js

取得存取權杖並將其設為 OAuth2 物件後,請使用 呼叫 Google 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

應用程式取得存取權杖後,您可以使用該權杖向 Google 代表指定的 使用者帳戶。方法是加入 加入 access_token 查詢,藉此將存取權杖用於傳送至 API 的要求中 參數或 Authorization HTTP 標頭 Bearer 值。可能的話 HTTP 標頭較為理想,因為這類字串通常會顯示在伺服器記錄中。大多數 使用用戶端程式庫來設定對 Google API 的呼叫 (例如,在 呼叫 Drive Files API)。

您可以試用所有 Google API 並查看相關範圍: OAuth 2.0 Playground

HTTP GET 範例

drive.files 端點 (Drive Files API)Authorization: BearerHTTP 標題可能如下所示。請注意,您必須指定自己的存取權杖:

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. 安裝 Google API 用戶端 使用 Composer 的 PHP 程式庫:
    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'])) {
  // Generate and set state value
  $state = bin2hex(random_bytes(16));
  $client->setState($state);
  $_SESSION['state'] = $state;

  $auth_url = $client->createAuthUrl();
  header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
  // Check the state value
  if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['state']) {
    die('State mismatch. Possible CSRF attack.');
  }
  $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)

小茹

本範例使用 Sinatra 架構。

require 'google/apis/drive_v3'
require 'sinatra'
require 'googleauth'
require 'googleauth/stores/redis_token_store'

configure do
  enable :sessions

  set :client_id, Google::Auth::ClientId.from_file('/path/to/client_secret.json')
  set :scope, Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY
  set :token_store, Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)
  set :authorizer, Google::Auth::WebUserAuthorizer.new(settings.client_id, settings.scope, settings.token_store, '/oauth2callback')
end

get '/' do
  user_id = settings.client_id.id
  credentials = settings.authorizer.get_credentials(user_id, request)
  if credentials.nil?
    redirect settings.authorizer.get_authorization_url(login_hint: user_id, request: request)
  end
  drive = Google::Apis::DriveV3::DriveService.new
  files = drive.list_files(options: { authorization: credentials })
  "<pre>#{JSON.pretty_generate(files.to_h)}</pre>"
end

get '/oauth2callback' do
  target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)
  redirect target_url
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');
const crypto = require('crypto');
const express = require('express');
const session = require('express-session');

/**
 * 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'
];
/* 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 app = express();

  app.use(session({
    secret: 'your_secure_secret_key', // Replace with a strong secret
    resave: false,
    saveUninitialized: false,
  }));

  // Example on redirecting user to Google's OAuth 2.0 server.
  app.get('/', async (req, res) => {
    // Generate a secure random state value.
    const state = crypto.randomBytes(32).toString('hex');
    // Store state in the session
    req.session.state = state;

    // 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,
      // Include the state parameter to reduce the risk of CSRF attacks.
      state: state
    });

    res.redirect(authorizationUrl);
  });

  // Receive the callback from Google's OAuth 2.0 server.
  app.get('/oauth2callback', async (req, res) => {
    // 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 if (q.state !== req.session.state) { //check state value
      console.log('State mismatch. Possible CSRF attack');
      res.end('State mismatch. Possible CSRF attack');
    } 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
  app.get('/revoke', async (req, res) => {
    // 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();
  });


  const server = http.createServer(app);
  server.listen(80);
}
main().catch(console.error);

HTTP/REST

此 Python 範例使用 Flask 架構 和 Requests 程式庫, 2.0 網頁流程。針對這個流程,我們建議使用 Python 適用的 Google API 用戶端程式庫。( 範例,使用的是用戶端程式庫)。

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:
    state = str(uuid.uuid4())
    flask.session['state'] = state
    auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code'
                '&client_id={}&redirect_uri={}&scope={}&state={}').format(CLIENT_ID, REDIRECT_URI,
                                                                          SCOPE, state)
    return flask.redirect(auth_uri)
  else:
    if 'state' not in flask.request.args or flask.request.args['state'] != flask.session['state']:
      return 'State mismatch. Possible CSRF attack.', 400

    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 不得包含 開放式重新導向

    Fragment

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

    字元 重新導向 URI 不得包含特定字元,包括:
    • 萬用字元字元 ('*')
    • 不可列印的 ASCII 字元
    • 百分比編碼無效 (任何未遵循網址編碼的百分比編碼) 的值格式為百分比,後面加上兩個十六進位數字)
    • 空值字元 (編碼的 NULL 字元,例如%00, %C0%80)

    漸進式授權

    在 OAuth 2.0 通訊協定中,應用程式要求存取資源, 依範圍識別要求授權是最佳的使用者體驗 隨時查看所需資源為了進行這項練習,Google 的授權伺服器 支援漸進式授權這項功能可讓您在需要時要求範圍 如果使用者授予新範圍的權限,則會傳回可能是 用來交換的憑證,該憑證包含使用者已授予專案的所有範圍。

    舉例來說,允許使用者取樣音樂曲目及建立合輯的應用程式,可能只需要少數人使用 要求存取資源,比登入使用者名稱更是如此。不過 儲存完成的合輯需要存取他們的 Google 雲端硬碟。大多數人都會看到 就算是在應用程式實際執行當下才要求存取 Google 雲端硬碟,也沒關係 。

    在此情況下,應用程式可能會在登入時要求 openid 和 執行基本登入的 profile 個範圍,然後在之後要求 https://www.googleapis.com/auth/drive.file 範圍,用來儲存 合輯

    如要執行漸進式授權,請完成一般要求存取權的流程 憑證,但請確認授權要求包含先前授予的範圍。這個 ,您的應用程式就不需要管理多個存取權杖。

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

    • 無論資料是隸屬於哪個範圍,都能用來存取相應的資源。 新的合併授權。
    • 當您使用更新權杖取得合併授權以取得存取權杖時, 存取權杖代表了合併授權,可用於任何 回應中包含 scope 個值。
    • 合併授權包括使用者授予 API 專案的所有範圍 則來自不同用戶端的要求舉例來說,如果使用者 一個範圍,然後使用應用程式的桌面用戶端,然後將另一個範圍授予相同的 行動用戶端簽署應用程式時,合併授權將包括這兩個範圍。
    • 如果撤銷代表合併授權的權杖, 同時撤銷關聯使用者的授權範圍。
    ,瞭解如何調查及移除這項存取權。

    步驟 1:設定授權中特定語言的程式碼範例 參數,以及步驟 2: 重新導向至 Google 的 OAuth 2.0 伺服器全部都會使用漸進式授權。程式碼範例 下方也會顯示您必須新增的程式碼,才能使用漸進式授權。

    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')

    小茹

    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 Cloud 服務 沒有使用者時傳回 API。例如,執行備份服務的應用程式 為了在預定時間執行動作,必須能夠重新整理存取權杖, 沒有使用者。存取的預設樣式稱為 online

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

    PHP

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

    $client->setAccessType("offline");

    使用者授予要求範圍的離線存取功能後,您就可以繼續使用這個 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 用戶端存取 Google API。用戶端物件 視需要更新存取權杖。

    小茹

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

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

    使用者授予要求範圍的離線存取功能後,您就可以繼續使用這個 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 用戶端存取 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_typeoffline 方法來接收更新權杖。如果你已將必要權限提供給應用程式 如果不設定適當的限制來接收更新憑證,您必須 重新授權應用程式,接收新的更新權杖。

    如要稍後再設定 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 資源大幅變更。也就是 移除程序中的某個部分可包含 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'})

    小茹

    如要透過程式輔助方式撤銷權杖,請向 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 傳回。