使用 OAuth 驗證 Looker API

Looker 採用 OAuth,讓 OAuth 用戶端應用程式向 Looker API 驗證,而不向執行 OAuth 用戶端應用程式的瀏覽器,公開用戶端 ID 和用戶端密鑰。

使用 OAuth 的網頁應用程式必須符合下列規定:

  • 使用 OAuth 驗證僅適用於 Looker API 4.0。
  • 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。

OAuth 驗證總覽

OAuth 驗證程序大致如下:

  1. 在 Looker 執行個體註冊 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 主機名稱上的 /token 端點,例如 https://instance_name.looker.com:19999。API 主機名稱可能與 Looker UI 主機名稱相同,也可能不同。/token 端點僅存在於 Looker API 主機上,/auth 端點僅存在於 Looker UI 主機上。

  6. 如果傳遞至 /token 端點的授權碼有效,Looker 會傳回 API access_token,為來自 OAuth 用戶端應用程式網域的 CORS API 要求啟用。

註冊 OAuth 用戶端應用程式

每個 OAuth 用戶端應用程式嘗試使用 OAuth 向 Looker API 驗證身分時,都必須先向 Looker 執行個體註冊,Looker 才會授權存取。

Looker 管理員可以直接在 Looker「管理」面板的「管理 OAuth 用戶端」頁面 (依序點選「管理」>「驗證」>「管理 OAuth 用戶端」),註冊及管理自訂 OAuth 用戶端應用程式。管理員可以透過這個介面執行下列工作:

  • 為應用程式設定一或多個授權的重新導向 URI,以支援多個環境和通訊埠。
  • 限制只有特定 Looker 使用者群組的成員才能取得授權 (Restrict to Group ID)。
  • 查看、編輯、啟用、停用或刪除已註冊的自訂應用程式。

如需在管理面板中註冊自訂用戶端的逐步說明,請參閱「管理員設定 - 管理 OAuth 用戶端」說明文件頁面。

使用 API 透過程式輔助方式註冊

或者,管理員可以透過程式輔助方式註冊 OAuth 用戶端應用程式,方法是直接呼叫註冊 OAuth 應用程式端點,或在 Looker API Explorer 中執行。參數包括:

  • client_guid:應用程式的專屬字串 ID (開頭不得為 com.looker.)。
  • redirect_uris:一組已授權的回呼 URI,應用程式會在這些 URI 接收 OAuth 重新導向和授權碼。
  • display_name:向使用者顯示的應用程式名稱。
  • description:OAuth 用戶端應用程式的說明。
  • group_id (選用):Looker 群組的數字 ID,用於限制存取權。

使用 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_token,不需使用者互動。refresh_token 的生命週期為一個月。妥善保存 refresh_token。

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