使用 OAuth 驗證 Looker API

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 驗證程序的簡要說明如下:

  1. 使用 Looker API 註冊 OAuth 用戶端應用程式
  2. 將 OAuth 用戶端應用程式的來源新增至嵌入網域許可清單,以便進行程式碼交換 API 呼叫和後續的 CORS API 呼叫。
  3. 當 OAuth 用戶端應用程式嘗試驗證使用者時,將瀏覽器網址重新導向至 Looker UI 主機名稱 (而非 Looker API 主機名稱) 上的 /auth 端點。例如:https://instance_name.looker.com
  4. 如果使用者已成功驗證並登入 Looker,Looker 會立即將 OAuth 重新導向傳回至 OAuth 用戶端應用程式。如果使用者尚未在裝置和瀏覽器上登入 Looker,系統會顯示 Looker 登入畫面,並提示使用者使用一般驗證協定登入 Looker 使用者帳戶。
  5. 使用 OAuth 重新導向中傳回的授權碼,OAuth 用戶端應用程式接下來應呼叫 Looker API 主機名稱 (例如 https://instance_name.looker.com:19999) 的 /token 端點。API 主機名稱可能與 Looker UI 主機名稱相同,也可能不同。/token 端點只會出現在 Looker API 主機上,/auth 端點則只會出現在 Looker UI 主機上。
  6. 如果傳遞至 /token 端點的授權碼有效,Looker 會傳回 API access_token,讓 OAuth 用戶端應用程式網域的 CORS API 要求啟用。

註冊 OAuth 用戶端應用程式

每個嘗試透過 OAuth 驗證 Looker API 的 OAuth 用戶端應用程式,都必須先向 Looker 執行個體註冊,才能獲得 Looker 的存取權授權。如要註冊 OAuth 用戶端應用程式,請按照下列步驟操作:

  1. 在 Looker 執行個體上開啟 API Explorer
  2. 使用版本下拉式選單,選擇 API 的 4.0 - stable 版本。
  3. 在「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:應用程式的全球唯一 ID
    • redirect_uri:應用程式接收包含授權碼的 OAuth 重新導向 URI
    • display_name:向應用程式使用者顯示的應用程式名稱
    • description:當使用者首次透過應用程式登入時,在揭露和確認頁面向使用者顯示的應用程式說明

    client_guidredirect_uri 參數中的值必須與 OAuth 用戶端應用程式提供的值「完全相符」,否則系統會拒絕驗證。

使用 OAuth 執行使用者登入作業

  1. 將使用者導向 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 登入作業會立即完成,不會中斷使用者瀏覽畫面。
  2. Looker API 會將 OAuth 重新導向傳回至 OAuth 用戶端應用程式。儲存 URI 參數中列出的授權碼。以下是 OAuth 重新導向 URI 範例:

    https://mywebapp.com:3000/authenticated?&code=asdfasdfassdf&state=...
    

    授權碼會顯示在 URI 中的 &code= 後方。在這個範例中,授權碼為 asdfasdfassdf

  3. 向 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_tokenrefresh_token 的生命週期為一個月。安全地儲存 refresh_token

    Looker 管理員可隨時撤銷系統中的所有權杖。