Looker 會使用 OAuth,讓 OAuth 用戶端應用程式向 Looker API 進行驗證,而不會將用戶端 ID 和用戶端密鑰公開給執行 OAuth 用戶端應用程式的瀏覽器。
使用 OAuth 的網路應用程式必須符合下列規定:
- 只有 Looker API 4.0 支援使用 OAuth 驗證。
- OAuth 用戶端應用程式必須先使用 API 向 Looker 註冊,使用者才能透過應用程式驗證 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 要求啟用該 API。
註冊 OAuth 用戶端應用程式
凡是嘗試使用 OAuth 驗證 Looker API 的 OAuth 用戶端應用程式,都必須先向 Looker 執行個體註冊,Looker 才會授權存取。如要註冊 OAuth 用戶端應用程式,請按照下列步驟操作:
- 在 Looker 執行個體中開啟 API Explorer。
- 使用版本下拉式選單,選擇 API 的「4.0 - 穩定」版本。
在「Auth」方法下方,找出
register_oauth_client_app()
API 端點。您也可以在「搜尋」欄位中搜尋「OAuth 應用程式」。您可以使用register_oauth_client_app()
向 Looker 註冊 OAuth 用戶端應用程式。按一下「執行」按鈕,在 API Explorer 中輸入參數,然後再次按一下「執行」,即可註冊 OAuth 用戶端應用程式,或以程式輔助方式使用register_oauth_client_app()
API 端點。必要register_oauth_client_app()
參數如下:client_guid
:應用程式的全球專屬 IDredirect_uri
:應用程式將接收 OAuth 重新導向的 URI,其中包含授權碼display_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 管理員隨時可以撤銷這個系統中的所有權杖。