OpenID 連接

Google 的 OpenID Connect 端點已通過 OpenID 認證。

Google 的 OAuth 2.0 API 可用於身份驗證和授權。本文檔描述了我們用於身份驗證的 OAuth 2.0 實現,它符合OpenID Connect規範,並通過了OpenID 認證使用 OAuth 2.0 訪問 Google API中的文檔也適用於該服務。如果您想以交互方式探索此協議,我們建議您使用Google OAuth 2.0 Playground 。要獲得有關Stack Overflow的幫助,請使用“google-oauth”標記您的問題。

設置 OAuth 2.0

在您的應用程序可以使用 Google 的 OAuth 2.0 身份驗證系統進行用戶登錄之前,您必須在 Google API Console 中設置一個項目以獲取 OAuth 2.0 憑據,設置重定向 URI,並(可選)自定義您的用戶在用戶同意屏幕。您還可以使用 API Console 創建服務帳戶、啟用計費、設置過濾和執行其他任務。有關更多詳細信息,請參閱Google API Console幫助

獲取 OAuth 2.0 憑據

您需要 OAuth 2.0 憑據(包括客戶端 ID 和客戶端密碼)來驗證用戶身份並獲得對 Google API 的訪問權限。

要查看给定OAuth 2.0凭据的客户端ID和客户端密钥,请单击以下文本: 选择凭据 。在打开的窗口中,选择您的项目和所需的凭证,然后单击“ 查看”

或者,从API Console的“ 凭据”页面中查看您的客户端ID和客户端密钥:

  1. Go to the Credentials page.
  2. 单击您的凭证名称或铅笔( )图标。您的客户ID和密码位于页面顶部。

設置重定向 URI

您在 API Console 中設置的重定向 URI 確定 Google 將響應發送到您的身份驗證請求的位置。

To create, view, or edit the redirect URIs for a given OAuth 2.0 credential, do the following:

  1. Go to the Credentials page.
  2. In the OAuth 2.0 client IDs section of the page, click a credential.
  3. View or edit the redirect URIs.

If there is no OAuth 2.0 client IDs section on the Credentials page, then your project has no OAuth credentials. To create one, click Create credentials.

自定義用戶同意屏幕

對於您的用戶,OAuth 2.0 身份驗證體驗包括一個同意屏幕,其中描述了用戶發布的信息和適用的條款。例如,當用戶登錄時,他們可能會被要求授予您的應用訪問他們的電子郵件地址和基本帳戶信息的權限。您使用您的應用程序在其身份驗證請求中包含的scope參數請求訪問此信息。您還可以使用範圍請求訪問其他 Google API。

用戶同意屏幕還顯示品牌信息,例如您的產品名稱、徽標和主頁 URL。您可以控制 API Console中的品牌信息。

要啟用項目的同意屏幕:

  1. Consent Screen page中打開Google API Console 。
  2. If prompted, select a project, or create a new one.
  3. 填寫表格,然後點擊保存

以下同意對話框顯示了當請求中同時存在 OAuth 2.0 和 Google Drive 範圍時用戶會看到什麼。 (此通用對話框是使用Google OAuth 2.0 Playground生成的,因此它不包含將在 API Console中設置的品牌信息。)

同意頁面截圖

訪問服務

Google 和第三方提供了庫,您可以使用這些庫來處理驗證用戶和獲取對 Google API 的訪問權限的許多實施細節。示例包括可用於各種平台的Google 登錄Google 客戶端庫

如果您選擇不使用庫,請按照本文檔其餘部分中的說明進行操作,其中描述了作為可用庫基礎的 HTTP 請求流。

對用戶進行身份驗證

對用戶進行身份驗證涉及獲取 ID 令牌並對其進行驗證。 ID 令牌OpenID Connect的一項標準化功能,旨在用於在 Internet 上共享身份聲明。

驗證用戶和獲取 ID 令牌的最常用方法稱為“服務器”流程和“隱式”流程。服務器流程允許應用程序的後端服務器使用瀏覽器或移動設備驗證人員的身份。當客戶端應用程序(通常是在瀏覽器中運行的 JavaScript 應用程序)需要直接訪問 API 而不是通過其後端服務器時,使用隱式流。

本文檔描述瞭如何執行服務器流程來驗證用戶。由於在客戶端處理和使用令牌的安全風險,隱式流程要復雜得多。如果您需要實現隱式流程,我們強烈建議您使用Google Sign-In

服務器流

確保您在 API Console中設置您的應用程序,以使其能夠使用這些協議並對您的用戶進行身份驗證。當用戶嘗試使用 Google 登錄時,您需要:

  1. 創建防偽狀態令牌
  2. 向 Google 發送身份驗證請求
  3. 確認防偽狀態令牌
  4. 訪問令牌和 ID 令牌的交換code
  5. 從 ID 令牌中獲取用戶信息
  6. 驗證用戶

1.創建防偽狀態令牌

您必須通過防止請求偽造攻擊來保護用戶的安全。第一步是創建一個唯一的會話令牌,用於保存您的應用程序和用戶客戶端之間的狀態。稍後,您將此唯一會話令牌與 Google OAuth 登錄服務返回的身份驗證響應相匹配,以驗證用戶發出請求而不是惡意攻擊者。這些令牌通常稱為跨站點請求偽造 ( CSRF ) 令牌。

狀態令牌的一個不錯的選擇是使用高質量隨機數生成器構造的 30 個左右字符的字符串。另一個是通過使用在後端保密的密鑰對一些會話狀態變量進行簽名而生成的哈希。

以下代碼演示了生成唯一會話令牌。

PHP

您必須下載用於 PHP 的 Google API 客戶端庫才能使用此示例。

// Create a state token to prevent request forgery.
// Store it in the session for later validation.
$state = bin2hex(random_bytes(128/8));
$app['session']->set('state', $state);
// Set the client ID, token state, and application name in the HTML while
// serving it.
return $app['twig']->render('index.html', array(
    'CLIENT_ID' => CLIENT_ID,
    'STATE' => $state,
    'APPLICATION_NAME' => APPLICATION_NAME
));

爪哇

您必須下載適用於 Java 的 Google API 客戶端庫才能使用此示例。

// Create a state token to prevent request forgery.
// Store it in the session for later validation.
String state = new BigInteger(130, new SecureRandom()).toString(32);
request.session().attribute("state", state);
// Read index.html into memory, and set the client ID,
// token state, and application name in the HTML before serving it.
return new Scanner(new File("index.html"), "UTF-8")
    .useDelimiter("\\A").next()
    .replaceAll("[{]{2}\\s*CLIENT_ID\\s*[}]{2}", CLIENT_ID)
    .replaceAll("[{]{2}\\s*STATE\\s*[}]{2}", state)
    .replaceAll("[{]{2}\\s*APPLICATION_NAME\\s*[}]{2}",
    APPLICATION_NAME);

Python

您必須下載適用於 Python 的 Google API 客戶端庫才能使用此示例。

# Create a state token to prevent request forgery.
# Store it in the session for later validation.
state = hashlib.sha256(os.urandom(1024)).hexdigest()
session['state'] = state
# Set the client ID, token state, and application name in the HTML while
# serving it.
response = make_response(
    render_template('index.html',
                    CLIENT_ID=CLIENT_ID,
                    STATE=state,
                    APPLICATION_NAME=APPLICATION_NAME))

2.向谷歌發送認證請求

下一步是使用適當的 URI 參數形成一個 HTTPS GET請求。請注意在此過程的所有步驟中使用 HTTPS 而不是 HTTP; HTTP 連接被拒絕。您應該使用authorization_endpoint元數據值從Discovery 文檔中檢索基本 URI。以下討論假設基本 URI 是https://accounts.google.com/o/oauth2/v2/auth

對於基本請求,請指定以下參數:

  • client_id ,您從 API ConsoleCredentials page獲得。
  • response_type ,在基本授權碼流請求中應該是code 。 (在response_type閱讀更多內容。)
  • scope ,在基本請求中應該是openid email 。 (在scope閱讀更多內容。)
  • redirect_uri應該是您服務器上的 HTTP 端點,它將接收來自 Google 的響應。該值必須與您在 API ConsoleCredentials page中配置的 OAuth 2.0 客戶端的授權重定向 URI 之一完全匹配。如果此值與授權 URI 不匹配,則請求將失敗並出現redirect_uri_mismatch錯誤。
  • state應該包括防偽唯一會話令牌的值,以及當用戶返回到您的應用程序時恢復上下文所需的任何其他信息,例如起始 URL。 (閱讀更多state 。)
  • nonce是您的應用程序生成的隨機值,當存在時啟用重放保護。
  • login_hint可以是用戶的電子郵件地址,也可以是sub字符串,相當於用戶的 Google ID。如果您未提供login_hint並且用戶當前已登錄,則同意屏幕會包含請求批准將用戶的電子郵件地址發佈到您的應用程序的請求。 (在login_hint閱讀更多內容。)
  • 使用hd參數為特定 G Suite 域的用戶優化 OpenID Connect 流程。 (閱讀更多hd內容。)

下面是一個完整的 OpenID Connect 身份驗證 URI 示例,帶有換行符和空格以便於閱讀:

https://accounts.google.com/o/oauth2/v2/auth?
 response_type=code&
 client_id=424911365001.apps.googleusercontent.com&
 scope=openid%20email&
 redirect_uri=https%3A//oauth2.example.com/code&
 state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2-login-demo.example.com%2FmyHome&
 login_hint=jsmith@example.com&
 nonce=0394852-3190485-2490358&
 hd=example.com

如果您的應用程序請求有關他們的任何新信息,或者如果您的應用程序請求他們以前未批准的帳戶訪問權限,則用戶必須給予同意。

3.確認防偽狀態令牌

響應將發送到您在請求中指定的redirect_uri 。所有響應都在查詢字符串中返回,如下所示:

https://oauth2.example.com/code?state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foa2cb.example.com%2FmyHome&code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&scope=openid%20email%20https://www.googleapis.com/auth/userinfo.email

在服務器上,您必須確認從 Google 收到的state與您在第 1 步中創建的會話令牌相匹配。這種往返驗證有助於確保發出請求的是用戶,而不是惡意腳本。

以下代碼演示了確認您在步驟 1 中創建的會話令牌:

PHP

您必須下載用於 PHP 的 Google API 客戶端庫才能使用此示例。

// Ensure that there is no request forgery going on, and that the user
// sending us this connect request is the user that was supposed to.
if ($request->get('state') != ($app['session']->get('state'))) {
  return new Response('Invalid state parameter', 401);
}

爪哇

您必須下載適用於 Java 的 Google API 客戶端庫才能使用此示例。

// Ensure that there is no request forgery going on, and that the user
// sending us this connect request is the user that was supposed to.
if (!request.queryParams("state").equals(
    request.session().attribute("state"))) {
  response.status(401);
  return GSON.toJson("Invalid state parameter.");
}

Python

您必須下載適用於 Python 的 Google API 客戶端庫才能使用此示例。

# Ensure that the request is not a forgery and that the user sending
# this connect request is the expected user.
if request.args.get('state', '') != session['state']:
  response = make_response(json.dumps('Invalid state parameter.'), 401)
  response.headers['Content-Type'] = 'application/json'
  return response

4. access token 和 ID token 的兌換code

響應包括一個code參數,一個一次性授權代碼,您的服務器可以用它來交換訪問令牌和 ID 令牌。您的服務器通過發送 HTTPS POST請求進行此交換。 POST請求被發送到令牌端點,您應該使用token_endpoint元數據值從發現文檔中檢索該端點。以下討論假設端點是https://oauth2.googleapis.com/token 。請求必須在POST正文中包含以下參數:

字段
code初始請求返回的授權代碼。
client_id您從 API ConsoleCredentials page獲取的客戶端 ID,如獲取 OAuth 2.0 憑據中所述。
client_secret您從 API ConsoleCredentials page獲取的客戶端密碼,如獲取 OAuth 2.0 憑據中所述。
redirect_uri在 API ConsoleCredentials page中指定的給定client_id的授權重定向 URI,如設置重定向 URI中所述。
grant_type如 OAuth 2.0 規範中所定義,該字段必須包含一個值authorization_code

實際請求可能類似於以下示例:

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

對此請求的成功響應在 JSON 數組中包含以下字段:

字段
access_token可以發送到 Google API 的令牌。
expires_in訪問令牌的剩餘生命週期(以秒為單位)。
id_token包含由 Google 數字簽名的用戶身份信息的JWT
scope access_token授予的訪問範圍,以空格分隔、區分大小寫的字符串列表表示。
token_type標識返回的令牌類型。此時,該字段的值始終為Bearer
refresh_token (選修的)

僅當在身份驗證請求中將access_type參數設置為offline時,此字段才存在。有關詳細信息,請參閱刷新令牌

5.從ID token中獲取用戶信息

ID Token 是JWT (JSON Web Token),即加密簽名的 Base64 編碼的 JSON 對象。通常,在使用ID 令牌之前驗證它至關重要,但由於您通過無中介的 HTTPS 通道直接與 Google 通信,並使用您的客戶端密碼向 Google 驗證您自己,因此您可以確信您使用的令牌是接收確實來自谷歌並且是有效的。如果您的服務器將 ID 令牌傳遞給應用程序的其他組件,那麼其他組件在使用令牌之前驗證令牌非常重要。

由於大多數 API 庫將驗證與解碼 base64url 編碼值和解析其中的 JSON 的工作結合在一起,因此您可能最終會在訪問 ID 令牌中的聲明時驗證令牌。

ID 令牌的有效負載

ID 令牌是包含一組名稱/值對的 JSON 對象。這是一個示例,為便於閱讀而格式化:

{
  "iss": "https://accounts.google.com",
  "azp": "1234987819200.apps.googleusercontent.com",
  "aud": "1234987819200.apps.googleusercontent.com",
  "sub": "10769150350006150715113082367",
  "at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q",
  "hd": "example.com",
  "email": "jsmith@example.com",
  "email_verified": "true",
  "iat": 1353601026,
  "exp": 1353604926,
  "nonce": "0394852-3190485-2490358"
}

Google ID 令牌可能包含以下字段(稱為聲明):

宣稱假如描述
aud總是此 ID 令牌的目標受眾。它必須是您的應用程序的 OAuth 2.0 客戶端 ID 之一。
exp總是不得接受 ID 令牌的過期時間或之後。以 Unix 時間(整數秒)表示。
iat總是頒發 ID 令牌的時間。以 Unix 時間(整數秒)表示。
iss總是響應的頒發者的頒發者標識符。對於 Google ID 令牌,始終使用https://accounts.google.comaccounts.google.com
sub總是用戶的標識符,在所有 Google 帳戶中唯一且從未重複使用。一個 Google 帳戶可以在不同的時間點擁有多個電子郵件地址,但sub值永遠不會改變。在您的應用程序中使用sub作為用戶的唯一標識符鍵。最大長度為 255 個區分大小寫的 ASCII 字符。
at_hash訪問令牌哈希。提供訪問令牌與身份令牌綁定的驗證。如果在服務器流中使用access_token值頒發 ID 令牌,則始終包含此聲明。此聲明可用作防止跨站點請求偽造攻擊的替代機制,但如果您遵循步驟 1步驟 3 ,則無需驗證訪問令牌。
azp授權演示者的client_id 。僅當請求 ID 令牌的一方與 ID 令牌的受眾不同時才需要此聲明。在 Google 的混合應用程序中可能就是這種情況,其中 Web 應用程序和 Android 應用程序具有不同的 OAuth 2.0 client_id但共享相同的 Google API 項目。
email用戶的電子郵件地址。此值可能不是該用戶唯一的,不適合用作主鍵。僅當您的範圍包含email範圍值時才提供。
email_verified如果用戶的電子郵件地址已經過驗證,則為真;否則為假。
family_name用戶的姓氏或姓氏。存在name聲明時可能會提供。
given_name用戶的名字或名字。存在name聲明時可能會提供。
hd用戶的託管 G Suite 域。僅當用戶屬於託管域時提供。
locale用戶的區域設置,由BCP 47語言標記表示。存在name聲明時可能會提供。
name用戶的全名,以可顯示的形式。可能在以下情況下提供:
  • 請求範圍包括字符串“profile”
  • ID 令牌從令牌刷新中返回

當存在name聲明時,您可以使用它們來更新應用程序的用戶記錄。請注意,永遠不能保證此聲明存在。

nonce您的應用在身份驗證請求中提供的nonce值。您應該通過確保僅顯示一次來強制防止重放攻擊。
picture用戶個人資料圖片的 URL。可能在以下情況下提供:
  • 請求範圍包括字符串“profile”
  • ID 令牌從令牌刷新中返回

當存在picture聲明時,您可以使用它們來更新應用的用戶記錄。請注意,永遠不能保證此聲明存在。

profile用戶個人資料頁面的 URL。可能在以下情況下提供:
  • 請求範圍包括字符串“profile”
  • ID 令牌從令牌刷新中返回

當存在profile聲明時,您可以使用它們來更新應用程序的用戶記錄。請注意,永遠不能保證此聲明存在。

6. 驗證用戶

從 ID 令牌中獲取用戶信息後,您應該查詢應用的用戶數據庫。如果該用戶已存在於您的數據庫中,那麼如果 Google API 響應滿足所有登錄要求,您應該為該用戶啟動一個應用程序會話。

如果您的用戶數據庫中不存在該用戶,您應該將用戶重定向到您的新用戶註冊流程。您可以根據從 Google 收到的信息自動註冊用戶,或者至少您可以預先填寫註冊表單上所需的許多字段。除了 ID 令牌中的信息之外,您還可以在我們的用戶配置文件端點獲取其他用戶配置文件信息

高級主題

以下部分更詳細地描述了 Google OAuth 2.0 API。此信息適用於對身份驗證和授權有高級要求的開發人員。

訪問其他 Google API

使用 OAuth 2.0 進行身份驗證的優勢之一是,在您對用戶進行身份驗證的同時,您的應用程序可以獲得代表用戶使用其他 Google API(例如 YouTube、Google Drive、日曆或聯繫人)的權限。為此,請在發送給 Google 的身份驗證請求中包含您需要的其他範圍。例如,要將用戶的年齡組添加到您的身份驗證請求中,請傳遞openid email https://www.googleapis.com/auth/profile.agerange.read的範圍參數。在同意屏幕上適當地提示用戶。您從 Google 收到的訪問令牌允許您訪問與您請求並被授予的訪問範圍相關的所有 API。

刷新令牌

在您的 API 訪問請求中,您可以請求在code交換期間返回一個刷新令牌。當用戶不在您的應用程序中時,刷新令牌可讓您的應用程序持續訪問 Google API。要請求刷新令牌,請在您的身份驗證請求中添加將access_type參數設置為offline

注意事項:

  • 請務必安全且永久地存儲刷新令牌,因為您只能在第一次執行代碼交換流程時獲得刷新令牌。
  • 頒發的刷新令牌的數量有限制:每個客戶端/用戶組合有一個限制,所有客戶端的每個用戶都有另一個限制。如果您的應用程序請求太多刷新令牌,它可能會遇到這些限制,在這種情況下,舊的刷新令牌將停止工作。

有關詳細信息,請參閱刷新訪問令牌(離線訪問)

您可以通過在您的身份驗證請求中將prompt參數設置為consent來提示用戶重新授權您的應用程序。當包含prompt=consent時,每次您的應用請求授權訪問範圍時都會顯示同意屏幕,即使所有範圍之前都已授予您的 Google API 項目。因此,僅在必要時才包含prompt=consent

有關prompt參數的更多信息,請參閱身份驗證 URI 參數表中的prompt

身份驗證 URI 參數

下表對 Google 的 OAuth 2.0 身份驗證 API 接受的參數進行了更完整的描述。

範圍必需的描述
client_id (必需的)您從 API ConsoleCredentials page獲取的客戶端 ID 字符串,如獲取 OAuth 2.0 憑據中所述。
nonce (必需的)由您的應用生成的隨機值,可啟用重放保護。
response_type (必需的)如果值為code ,則啟動基本授權代碼流,需要向令牌端點發送POST以獲取令牌。如果值為token id_tokenid_token token ,則啟動隱式流,要求在重定向 URI 處使用 JavaScript 從URI #fragment identifier中檢索令牌。
redirect_uri (必需的)確定發送響應的位置。此參數的值必須與您在 API ConsoleCredentials page 中設置的授權重定向值之一完全匹配(包括 HTTP 或 HTTPS 方案、大小寫和結尾的“/”,如果有)。
scope (必需的)

範圍參數必須以openid值開頭,然後包括profile值、 email值或兩者。

如果存在profile範圍值,則 ID 令牌可能(但不保證)包含用戶的默認profile聲明。

如果存在email範圍值,則 ID 令牌包括emailemail_verified聲明。

除了這些特定於 OpenID 的範圍之外,您的範圍參數還可以包含其他範圍值。所有範圍值必須用空格分隔。例如,如果您希望按文件訪問用戶的 Google Drive,則您的範圍參數可能是openid profile email https://www.googleapis.com/auth/drive.file

有關可用範圍的信息,請參閱 Google API 的OAuth 2.0 範圍或您要使用的 Google API 的文檔。

state (可選,但強烈推薦)

在協議中往返的不透明字符串;也就是說,它在 Basic 流中作為 URI 參數返回,在 Implicit 流中作為 URI #fragment標識符返回。

state對於關聯請求和響應很有用。因為您的redirect_uri可以被猜到,所以使用state值可以增加您對傳入連接是您的應用程序發起的身份驗證請求的結果的保證。如果您在此state變量中生成隨機字符串或編碼某些客戶端狀態(例如 cookie)的散列,則可以驗證響應以另外確保請求和響應源自同一瀏覽器。這提供了針對諸如跨站點請求偽造之類的攻擊的保護。

access_type (選修的)允許的值是offlineonline 。效果記錄在Offline Access中;如果正在請求訪問令牌,則客戶端不會收到刷新令牌,除非指定了offline值。
display (選修的)一個 ASCII 字符串值,用於指定授權服務器如何顯示身份驗證和同意用戶界面頁面。 Google 服務器指定並接受以下值,但對其行為沒有任何影響: pagepopuptouchwap
hd (選修的)

hd (託管域)參數簡化了 G Suite 託管帳戶的登錄過程。通過包含 G Suite 用戶的域(例如mycollege.edu ),您可以指示應針對該域中的帳戶優化帳戶選擇 UI。要優化 G Suite 帳戶而不是僅針對一個域,請設置星號 ( * ) 的值: hd=*

不要依賴此 UI 優化來控制誰可以訪問您的應用程序,因為可以修改客戶端請求。請務必驗證返回的 ID 令牌是否具有符合您期望的hd聲明值(例如mycolledge.edu )。與請求參數不同,ID 令牌hd聲明包含在來自 Google 的安全令牌中,因此可以信任該值。

include_granted_scopes (選修的)如果此參數的值為true ,並且授權請求被授予,則授權將包括之前授予此用戶/應用程序組合的其他範圍的任何授權;請參閱增量授權

請注意,您不能對已安裝的應用程序流程進行增量授權。

login_hint (選修的)當您的應用程序知道它正在嘗試驗證哪個用戶時,它可以將此參數作為提示提供給驗證服務器。傳遞此提示會抑制帳戶選擇器並預先填寫登錄表單上的電子郵件框,或選擇正確的會話(如果用戶使用多重登錄),這可以幫助您避免應用程序出現的問題登錄錯誤的用戶帳戶。該值可以是電子郵件地址或sub字符串,相當於用戶的 Google ID。
prompt (選修的)以空格分隔的字符串值列表,指定授權服務器是否提示用戶重新進行身份驗證和同意。可能的值是:
  • none

    授權服務器不顯示任何身份驗證或用戶同意屏幕;如果用戶尚未經過身份驗證並且尚未為請求的範圍預先配置同意,它將返回錯誤。您可以使用none檢查現有的身份驗證和/或同意。

  • consent

    授權服務器在向客戶端返回信息之前提示用戶同意。

  • select_account

    授權服務器提示用戶選擇用戶帳戶。這允許在授權服務器上擁有多個帳戶的用戶在他們可能具有當前會話的多個帳戶中進行選擇。

如果未指定任何值且用戶之前未授權訪問,則向用戶顯示同意屏幕。

驗證 ID 令牌

您需要驗證服務器上的所有 ID 令牌,除非您知道它們直接來自 Google。例如,您的服務器必須驗證它從您的客戶端應用程序接收到的任何 ID 令牌都是真實的。

以下是您可能會將 ID 令牌發送到服務器的常見情況:

  • 發送帶有需要驗證的請求的 ID 令牌。 ID 令牌告訴您發出請求的特定用戶以及為哪個客戶端授予了該 ID 令牌。

ID 令牌很敏感,如果被攔截,可能會被濫用。您必須確保僅通過 HTTPS 並且僅通過 POST 數據或在請求標頭中傳輸這些令牌來安全地處理這些令牌。如果您在服務器上存儲 ID 令牌,則還必須安全地存儲它們。

使 ID 令牌有用的一件事是,您可以在應用程序的不同組件中傳遞它們。這些組件可以使用 ID 令牌作為對應用程序和用戶進行身份驗證的輕量級身份驗證機制。但在您可以使用 ID 令牌中的信息或將其作為用戶已通過身份驗證的斷言之前,您必須對其進行驗證。

驗證 ID 令牌需要幾個步驟:

  1. 驗證 ID 令牌是否由發行者正確簽名。 Google 頒發的令牌使用在Discovery 文檔jwks_uri元數據值中指定的 URI 中找到的證書之一進行簽名。
  2. 驗證 ID 令牌中iss聲明的值是否等於https://accounts.google.comaccounts.google.com
  3. 驗證 ID 令牌中aud聲明的值是否等於您應用的客戶端 ID。
  4. 驗證 ID 令牌的到期時間( exp聲明)尚未過去。
  5. 如果您在請求中指定了hd 參數值,請驗證 ID 令牌具有與接受的 G Suite 託管域匹配的hd聲明。

步驟 2 到 5 僅涉及非常簡單的字符串和日期比較,因此我們不會在此處詳細說明。

第一步更複雜,涉及加密簽名檢查。出於調試目的,您可以使用 Google 的tokeninfo端點與您的服務器或設備上實現的本地處理進行比較。假設您的 ID 令牌的值為XYZ123 。然後,您將取消引用 URI https://oauth2.googleapis.com/tokeninfo?id_token= XYZ123 。如果令牌簽名有效,則響應將是其解碼的 JSON 對象形式的 JWT 有效負載。

tokeninfo端點對於調試很有用,但對於生產目的,從密鑰端點檢索 Google 的公鑰並在本地執行驗證。您應該使用jwks_uri元數據值從發現文檔中檢索密鑰 URI。對調試端點的請求可能會受到限製或以其他方式受到間歇性錯誤的影響。

由於 Google 很少更改其公鑰,因此您可以使用 HTTP 響應的緩存指令緩存它們,並且在絕大多數情況下,執行本地驗證比使用tokeninfo端點更有效。此驗證需要檢索和解析證書,並進行適當的加密調用以檢查簽名。幸運的是,有多種語言提供的經過良好調試的庫可以完成此任務(請參閱jwt.io )。

獲取用戶配置文件信息

要獲取有關用戶的其他配置文件信息,您可以使用訪問令牌(您的應用程序在身份驗證流程中收到)和OpenID Connect標準:

  1. 要符合 OpenID,您必須在身份驗證請求中包含openid profile範圍值。

    如果您希望包含用戶的電子郵件地址,您可以指定email的附加範圍值。要同時指定profileemail ,您可以在身份驗證請求 URI 中包含以下參數:

    scope=openid%20profile%20email
  2. 將您的訪問令牌添加到授權標頭並向 userinfo 端點發出 HTTPS GET請求,您應該使用userinfo_endpoint元數據值從Discovery 文檔中檢索該請求。 userinfo 響應包括有關用戶的信息,如OpenID Connect Standard Claims和 Discovery 文檔的claims_supported元數據值中所述。用戶或其組織可能會選擇提供或保留某些字段,因此您可能無法獲得授權訪問範圍內每個字段的信息。

發現文件

OpenID Connect 協議需要使用多個端點來對用戶進行身份驗證,以及請求包括令牌、用戶信息和公鑰在內的資源。

為了簡化實現並提高靈活性,OpenID Connect 允許使用“發現文檔”,這是一個位於眾所周知的位置的 JSON 文檔,其中包含提供有關 OpenID Connect 提供者配置的詳細信息的鍵值對,包括授權的 URI 、令牌、撤銷、用戶信息和公鑰端點。 Google OpenID Connect 服務的 Discovery 文檔可以從以下位置檢索:

https://accounts.google.com/.well-known/openid-configuration

要使用 Google 的 OpenID Connect 服務,您應該將 Discovery-document URI ( https://accounts.google.com/.well-known/openid-configuration ) 硬編碼到您的應用程序中。您的應用程序獲取文檔,在響應中應用緩存規則,然後根據需要從中檢索端點 URI。例如,要對用戶進行身份authorization_endpoint ,您的代碼將檢索 authentication_endpoint 元數據值(以下示例中的https://accounts.google.com/o/oauth2/v2/auth )作為發送到的身份驗證請求的基本 URI谷歌。

這是此類文檔的示例;字段名稱是OpenID Connect Discovery 1.0中指定的名稱(請參閱該文檔以了解其含義)。這些值純粹是說明性的,可能會發生變化,儘管它們是從實際 Google Discovery 文檔的最新版本中復製而來的:

{
  "issuer": "https://accounts.google.com",
  "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
  "device_authorization_endpoint": "https://oauth2.googleapis.com/device/code",
  "token_endpoint": "https://oauth2.googleapis.com/token",
  "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
  "revocation_endpoint": "https://oauth2.googleapis.com/revoke",
  "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
  "response_types_supported": [
    "code",
    "token",
    "id_token",
    "code token",
    "code id_token",
    "token id_token",
    "code token id_token",
    "none"
  ],
  "subject_types_supported": [
    "public"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "scopes_supported": [
    "openid",
    "email",
    "profile"
  ],
  "token_endpoint_auth_methods_supported": [
    "client_secret_post",
    "client_secret_basic"
  ],
  "claims_supported": [
    "aud",
    "email",
    "email_verified",
    "exp",
    "family_name",
    "given_name",
    "iat",
    "iss",
    "locale",
    "name",
    "picture",
    "sub"
  ],
  "code_challenge_methods_supported": [
    "plain",
    "S256"
  ]
}

您可以通過緩存 Discovery 文檔中的值來避免 HTTP 往返。使用標準 HTTP 緩存標頭,應予以尊重。

客戶端庫

以下客戶端庫通過與流行框架集成,使 OAuth 2.0 的實現更加簡單:

OpenID Connect 合規性

Google 的 OAuth 2.0 身份驗證系統支持OpenID Connect Core規範所需的功能。任何設計用於 OpenID Connect 的客戶端都應與此服務互操作( OpenID 請求對象除外)。