Looker 使用 OAuth,讓 OAuth 用戶端應用程式驗證 Looker API,而不會將用戶端 ID 和用戶端密碼公開給執行 OAuth 用戶端應用程式的瀏覽器。
使用 OAuth 的網路應用程式必須符合下列規定:
- 您只能使用 Looker API 4.0 驗證。
- 應用程式的使用者必須先使用 API 向 Looker 註冊 OAuth 用戶端應用程式,才能向 Looker 驗證。
- 用戶端應用程式必須使用 HTTPS 傳送所有 Looker API 要求。如要使用瀏覽器提供的
SubtleCrypto
API,用戶端應用程式必須託管在 HTTPS 上。
Looker API CORS 支援
Looker API 支援在瀏覽器中和跨來源使用跨來源資源共享 (CORS) 通訊協定進行呼叫。Looker 的 CORS 支援功能有以下需求條件:
- 只有內嵌網域許可清單中列出的來源,才能使用 CORS 呼叫 API。
只有從 OAuth 或呼叫
/login
API 端點取得的存取權權杖,才能使用 CORS 呼叫 Looker API。您無法使用 CORS 要求呼叫
/login
API 端點。如要使用 CORS 要求呼叫 Looker API,用戶端應用程式必須使用「使用 OAuth 執行使用者登入」一文所述的 OAuth 登入程序,或是從應用程式伺服器或非 CORS API 呼叫中擷取權杖。
OAuth 驗證總覽
OAuth 驗證程序的簡要說明如下:
- 使用 Looker API 註冊 OAuth 用戶端應用程式。
- 將 OAuth 用戶端應用程式的來源新增至嵌入網域許可清單,以便進行程式碼交換 API 呼叫和後續的 CORS API 呼叫。
- 當 OAuth 用戶端應用程式嘗試驗證使用者時,將瀏覽器網址重新導向至 Looker UI 主機名稱 (而非 Looker API 主機名稱) 上的
/auth
端點。例如:https://instance_name.looker.com
。 - 如果使用者已成功驗證並登入 Looker,Looker 會立即將 OAuth 重新導向傳回至 OAuth 用戶端應用程式。如果使用者尚未在裝置和瀏覽器上登入 Looker,系統會顯示 Looker 登入畫面,並提示使用者使用一般驗證協定登入 Looker 使用者帳戶。
- 使用 OAuth 重新導向中傳回的授權碼,OAuth 用戶端應用程式接下來應呼叫 Looker API 主機名稱 (例如
https://instance_name.looker.com:19999
) 的/token
端點。API 主機名稱可能與 Looker UI 主機名稱相同,也可能不同。/token
端點只會出現在 Looker API 主機上,/auth
端點則只會出現在 Looker UI 主機上。 - 如果傳遞至
/token
端點的授權碼有效,Looker 會傳回 APIaccess_token
,讓 OAuth 用戶端應用程式網域的 CORS API 要求啟用。
註冊 OAuth 用戶端應用程式
每個嘗試透過 OAuth 驗證 Looker API 的 OAuth 用戶端應用程式,都必須先向 Looker 執行個體註冊,才能獲得 Looker 的存取權授權。如要註冊 OAuth 用戶端應用程式,請按照下列步驟操作:
- 在 Looker 執行個體上開啟 API Explorer。
- 使用版本下拉式選單,選擇 API 的 4.0 - stable 版本。
在「Auth」方法下方,找出
register_oauth_client_app()
API 端點。您也可以在「搜尋」欄位中搜尋「oauth app」。您可以使用register_oauth_client_app()
將 OAuth 用戶端應用程式註冊至 Looker。按一下「Run It」按鈕,然後在 API Explorer 中輸入參數,並再次按一下「Run It」即可註冊 OAuth 用戶端應用程式,或是以程式輔助方式使用register_oauth_client_app()
API 端點。必要的register_oauth_client_app()
參數如下:client_guid
:應用程式的全球唯一 IDredirect_uri
:應用程式接收包含授權碼的 OAuth 重新導向 URIdisplay_name
:向應用程式使用者顯示的應用程式名稱description
:當使用者首次透過應用程式登入時,在揭露和確認頁面向使用者顯示的應用程式說明
client_guid
和redirect_uri
參數中的值必須與 OAuth 用戶端應用程式提供的值「完全相符」,否則系統會拒絕驗證。
使用 OAuth 執行使用者登入作業
將使用者導向 UI 主機上的
/auth
端點。例如:async function oauth_login() { const code_verifier = secure_random(32) const code_challenge = await sha256_hash(code_verifier) const params = { response_type: 'code', client_id: '123456', redirect_uri: 'https://mywebapp.com:3000/authenticated', scope: 'cors_api', state: '1235813', code_challenge_method: 'S256', code_challenge: code_challenge, } const url = `${base_url}?${new URLSearchParams(params).toString()}` // Replace base_url with your full Looker instance's UI host URL, plus the `/auth` endpoint. log(url) // Stash the code verifier we created in sessionStorage, which // will survive page loads caused by login redirects // The code verifier value is needed after the login redirect // to redeem the auth_code received for an access_token // sessionStorage.setItem('code_verifier', code_verifier) document.location = url } function array_to_hex(array) { return Array.from(array).map(b => b.toString(16).padStart(2,'0')).join('') } function secure_random(byte_count) { const array = new Uint8Array(byte_count); crypto.getRandomValues(array); return array_to_hex(array) } async function sha256_hash(message) { const msgUint8 = new TextEncoder().encode(message) const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8) return base64.urlEncode(hashBuffer)) // Refers to the implementation of base64.encode stored at https://gist.github.com/jhurliman/1250118 }
Looker 會嘗試使用 Looker 例項所設定的驗證系統驗證使用者。
- 如果使用者已在目前的瀏覽器中登入 Looker (也就是說,有有效的登入 Cookie 狀態),系統就不會提示使用者輸入登入憑證。
- 如果這是使用者首次透過 OAuth 用戶端應用程式登入,Looker 會顯示揭露和確認頁面,供使用者確認並接受。系統會顯示應用程式註冊時使用的
description
參數文字。說明應指出應用程式打算如何使用使用者的 Looker 帳戶。使用者點選「接受」後,頁面會重新導向至應用程式redirect_uri
。 - 如果使用者已在目前的瀏覽器中登入 Looker,且已確認揭露資訊頁面,則 OAuth 登入作業會立即完成,不會中斷使用者瀏覽畫面。
Looker API 會將 OAuth 重新導向傳回至 OAuth 用戶端應用程式。儲存 URI 參數中列出的授權碼。以下是 OAuth 重新導向 URI 範例:
https://mywebapp.com:3000/authenticated?&code=asdfasdfassdf&state=...
授權碼會顯示在 URI 中的
&code=
後方。在這個範例中,授權碼為asdfasdfassdf
。向 Looker API 中的
/token
端點提出網路要求,並傳遞授權碼和應用程式資訊。例如:async function redeem_auth_code(response_str) { const params = new URLSearchParams(response_str) const auth_code = params.get('code') if (!auth_code) { log('ERROR: No authorization code in response') return } log(`auth code received: ${auth_code}`) log(`state: ${params.get('state')}`) const code_verifier = sessionStorage.getItem('code_verifier') if (!code_verifier) { log('ERROR: Missing code_verifier in session storage') return } sessionStorage.removeItem('code_verifier') const response = await fetch('https://mycompany.looker.com:19999/api/token', { // This is the URL of your Looker instance's API web service method: 'POST', mode: 'cors', // This line is required so that the browser will attempt a CORS request. body: stringify({ grant_type: 'authorization_code', client_id: '123456', redirect_uri: 'https://mywebapp.com:3000/authenticated', code: auth_code, code_verifier: code_verifier, }), headers: { 'x-looker-appid': 'Web App Auth & CORS API Demo', // This header is optional. 'Content-Type': 'application/json;charset=UTF-8' // This header is required. }, }).catch((error) => { log(`Error: ${error.message}`) }) const info = await response.json() log(`/api/token response: ${stringify(info)}`) // Store the access_token and other info, // which in this example is done in sessionStorage const expires_at = new Date(Date.now() + (info.expires_in * 1000)) info.expires_at = expires_at log(`Access token expires at ${expires_at.toLocaleTimeString()} local time.`) sessionStorage.setItem('access_info', stringify(info)) access_info = info }
成功的回應會為 OAuth 用戶端應用程式提供 API
access_token
。回應也會包含refresh_token
,您之後可以使用該值,在無需使用者互動的情況下取得新的access_token
。refresh_token
的生命週期為一個月。安全地儲存refresh_token
。Looker 管理員可隨時撤銷系統中的所有權杖。