Google Identity 服務程式庫可讓使用者使用以瀏覽器為基礎的彈出式視窗或重新導向使用者體驗流程,向 Google 要求授權碼。這樣就會啟動安全的 OAuth 2.0 流程,並產生用於代表使用者呼叫 Google API 的存取權杖。
OAuth 2.0 授權碼流程摘要:
- Google 帳戶擁有者會在瀏覽器中透過點選按鈕等手勢,向 Google 要求授權碼。
- Google 會回應要求,將專屬授權碼傳送至在使用者瀏覽器中執行的 JavaScript 網頁應用程式回呼,或是透過瀏覽器重新導向直接叫用授權碼端點。
- 您的後端平台會託管授權碼端點並接收程式碼。驗證完成後,系統會依使用者存取權交換這組代碼,並使用向 Google 權杖端點的要求重新整理權杖。
- Google 會驗證授權碼、確認來自您的安全平台的要求、核發存取和重新整理權杖,並呼叫平台代管的登入端點以傳回權杖。
- 您的登入端點會接收存取權和更新權杖,並安全地儲存更新權杖以供日後使用。
初始化程式碼用戶端
google.accounts.oauth2.initCodeClient()
方法會初始化程式碼用戶端。
彈出式視窗或重新導向模式
您可以選擇透過「Redirect」或「Popup」模式使用者流程共用驗證碼。使用重新導向模式時,您會在伺服器上代管 OAuth2 授權端點,讓 Google 將使用者代理程式重新導向至這個端點,並將驗證碼做為網址參數共用。在彈出式視窗模式中,您可以定義 JavaScript 回呼處理常式,用於將授權碼傳送至您的伺服器。彈出式視窗模式可用於提供流暢的使用者體驗,讓訪客不必離開網站。
如要初始化用戶端:
將使用者體驗流程重新導向,將
ux_mode
設為redirect
,並將redirect_uri
的值設為平台的授權碼端點。這個值必須與您在 API 控制台中設定的 OAuth 2.0 用戶端的其中一個已授權重新導向 URI 完全相符。您也必須遵守重新導向 URI 驗證規則。彈出式視窗使用者體驗流程,將
ux_mode
設為popup
,並將callback
的值設為您要用來將授權碼傳送至平台的函式名稱。
防範 CSRF 攻擊
為了防止跨網站要求、偽造 (CSRF) 攻擊,我們會採用重新導向和彈出式視窗模式的使用者體驗流程,採用有些不同的技巧。如果是重新導向模式,則會使用 OAuth 2.0 state 參數。如要進一步瞭解如何產生及驗證 state 參數,請參閱 RFC6749 10.12 跨網站要求偽造內容。透過彈出式視窗模式,您可以將自訂 HTTP 標頭新增至要求,然後在伺服器上確認該標頭與預期的值和來源相符。
選擇使用者體驗模式,即可查看顯示驗證碼和 CSRF 處理功能的程式碼片段:
重新導向模式
初始化用戶端,Google 會將使用者的瀏覽器重新導向至驗證端點,並將驗證碼做為網址參數共用。
const client = google.accounts.oauth2.initCodeClient({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly',
ux_mode: 'redirect',
redirect_uri: "https://your.domain/code_callback_endpoint",
state: "YOUR_BINDING_VALUE"
});
彈出式視窗模式
初始化使用者瀏覽器從 Google 接收驗證碼的用戶端,並將該用戶端傳送至您的伺服器。
const client = google.accounts.oauth2.initCodeClient({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly',
ux_mode: 'popup',
callback: (response) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', code_receiver_uri, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// Set custom header for CRSF
xhr.setRequestHeader('X-Requested-With', 'XmlHttpRequest');
xhr.onload = function() {
console.log('Auth code response: ' + xhr.responseText);
};
xhr.send('code=' + response.code);
},
});
觸發 OAuth 2.0 程式碼流程
呼叫程式碼用戶端的 requestCode()
方法,觸發使用者流程:
<button onclick="client.requestCode();">Authorize with Google</button>
這會在將授權碼傳回至重新導向端點或回呼處理常式之前,要求使用者登入 Google 帳戶並同意共用個別範圍。
驗證碼處理
Google 會產生每個使用者專屬授權碼,您可以在後端伺服器上接收並驗證這組授權碼。
在彈出式視窗模式下,callback
指定的處理常式 (在使用者瀏覽器中執行) 會將授權碼轉發至平台代管的端點。
如果是重新導向模式,GET
要求會傳送到 redirect_url
指定的端點,並在網址 code 參數中分享授權碼。如何接收授權碼:
如果您目前沒有實作項目,請建立新的授權端點;或
更新現有的端點,使其接受
GET
要求和網址參數。先前,系統會使用酬載中具有授權碼值的PUT
要求。
授權端點
您的授權碼端點必須使用下列網址查詢字串參數處理 GET
要求:
名稱 | 值 |
---|---|
驗證使用者 | 要求使用者登入驗證 |
代碼 | Google 產生的 OAuth2 授權碼 |
HD 高畫質 | 使用者帳戶的代管網域 |
提示 | 使用者同意聲明對話方塊 |
範圍 | 以空格分隔的清單,列出一或多個要授權的 OAuth2 範圍 |
state | CRSF 狀態變數 |
範例 GET
要求包含網址參數,指向名為 auth-code 的端點,並透過 example.com 代管:
Request URL: https://www.example.com/auth-code?state=42a7bd822fe32cc56&code=4/0AX4XfWiAvnXLqxlckFUVao8j0zvZUJ06AMgr-n0vSPotHWcn9p-zHCjqwr47KHS_vDvu8w&scope=email%20profile%20https://www.googleapis.com/auth/calendar.readonly%20https://www.googleapis.com/auth/photoslibrary.readonly%20https://www.googleapis.com/auth/contacts.readonly%20openid%20https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile&authuser=0&hd=example.com&prompt=consent
當授權碼流程是由先前的 JavaScript 程式庫啟動,或藉由直接呼叫 Google OAuth 2.0 端點而啟動,系統會使用 POST
要求。
範例 POST
要求,在 HTTP 要求主體中以酬載做為酬載:
Request URL: https://www.example.com/auth-code
Request Payload: 4/0AX4XfWhll-BMV82wi4YwbrSaTPaRpUGpKqJ4zBxQldU\_70cnIdh-GJOBZlyHU3MNcz4qaw
驗證要求
請在伺服器上執行下列操作,避免受到 CSRF 攻擊。
檢查 state 參數值 (適用於重新導向模式)。
確認 X-Requested-With: XmlHttpRequest
標頭已設為彈出式模式。
您必須先成功驗證驗證碼要求,才能從 Google 取得更新及存取權杖。
取得存取權並更新權杖
在後端平台收到 Google 提供的授權碼並驗證要求後,請使用驗證碼從 Google 取得存取權及更新權杖以發出 API 呼叫。
從「針對網路伺服器應用程式使用 OAuth 2.0」指南開始的步驟 5:交換授權碼,以便重新整理及存取權杖。
管理權杖
您的平台會以安全的方式儲存更新權杖。在使用者帳戶移除、google.accounts.oauth2.revoke
或直接從 https://myaccount.google.com/permissions 撤銷使用者同意聲明時,刪除已儲存的更新權杖。
或者,您也可以考慮使用 RISC 透過跨帳戶防護來保護使用者帳戶。
一般來說,您的後端平台會使用存取權杖呼叫 Google API。如果網頁應用程式也會直接從使用者的瀏覽器呼叫 Google API,則您必須實作與網頁應用程式分享存取權杖的方法,這麼做不在本指南的範圍內。遵循此方法及使用 JavaScript 適用的 Google API 用戶端程式庫時,請使用 gapi.client.SetToken()
將存取權杖暫時儲存在瀏覽器記憶體中,並讓程式庫呼叫 Google API。