使用 Firebase 驗證使用者

區域 ID

REGION_ID 是 Google 根據您在建立應用程式時選取的地區所指派的簡寫代碼。雖然某些區域 ID 可能看起來與常用的國家/地區代碼相似,但此代碼並非對應國家/地區或省份。如果是 2020 年 2 月後建立的應用程式,App Engine 網址會包含 REGION_ID.r。如果是在此日期之前建立的現有應用程式,網址中則可選擇加入地區 ID。

進一步瞭解區域 ID

將使用者登入流程新增至使用 Firebase 驗證的網路服務。

在本指南的這個步驟中,您會更新網路服務,以便驗證使用者,並在使用者完成驗證後擷取及顯示使用者自己的資訊。請注意,在這個步驟中,網站要求時間仍為全域,而非特定使用者。

事前準備

如果您已完成本指南先前提過的所有步驟,請跳過本節。 否則,請完成下列其中一個步驟:

  • 從「建構 Python 3 應用程式」開始,完成這個步驟之前的所有步驟。

  • 如果您已經有 Google Cloud 專案,接下來可以下載網路服務的複本並新增 Firebase:

    1. 使用 Git 下載範例應用程式存放區:

      git clone https://github.com/GoogleCloudPlatform/python-docs-samples
      

      您也可以下載 zip 格式的範例,然後解壓縮該檔案。

    2. 前往包含上一步驟檔案複本的目錄:

      cd python-docs-samples/appengine/standard_python3/building-an-app/building-an-app-2
      
    3. 將 Firebase 新增至 Google Cloud 專案和網路服務

新增 Firebase 驗證方法

Firebase 提供 JavaScript 方法與變數,您可用來設定網路服務的登入行為。針對此網路服務,您可以新增登出函式、設定登入 UI 的變數,以及新增使用者登入或登出時控制變更內容的函式。

如要新增驗證流程所需的行為,請將 static/script.js 檔案的目前事件監聽器方法替換為下列程式碼:

window.addEventListener('load', function () {
  document.getElementById('sign-out').onclick = function () {
    firebase.auth().signOut();
  };

  // FirebaseUI config.
  var uiConfig = {
    signInSuccessUrl: '/',
    signInOptions: [
      // Comment out any lines corresponding to providers you did not check in
      // the Firebase console.
      firebase.auth.GoogleAuthProvider.PROVIDER_ID,
      firebase.auth.EmailAuthProvider.PROVIDER_ID,
      //firebase.auth.FacebookAuthProvider.PROVIDER_ID,
      //firebase.auth.TwitterAuthProvider.PROVIDER_ID,
      //firebase.auth.GithubAuthProvider.PROVIDER_ID,
      //firebase.auth.PhoneAuthProvider.PROVIDER_ID

    ],
    // Terms of service url.
    tosUrl: '<your-tos-url>'
  };

  firebase.auth().onAuthStateChanged(function (user) {
    if (user) {
      // User is signed in, so display the "sign out" button and login info.
      document.getElementById('sign-out').hidden = false;
      document.getElementById('login-info').hidden = false;
      console.log(`Signed in as ${user.displayName} (${user.email})`);
      user.getIdToken().then(function (token) {
        // Add the token to the browser's cookies. The server will then be
        // able to verify the token against the API.
        // SECURITY NOTE: As cookies can easily be modified, only put the
        // token (which is verified server-side) in a cookie; do not add other
        // user information.
        document.cookie = "token=" + token;
      });
    } else {
      // User is signed out.
      // Initialize the FirebaseUI Widget using Firebase.
      var ui = new firebaseui.auth.AuthUI(firebase.auth());
      // Show the Firebase login button.
      ui.start('#firebaseui-auth-container', uiConfig);
      // Update the login state indicators.
      document.getElementById('sign-out').hidden = true;
      document.getElementById('login-info').hidden = true;
      // Clear the token cookie.
      document.cookie = "token=";
    }
  }, function (error) {
    console.log(error);
    alert('Unable to log in: ' + error)
  });
});

請注意,onAuthStateChanged() 方法會儲存使用者的 ID 權杖做為 Cookie,藉此控制使用者登入或登出時的變更內容。這個 ID 權杖是 Firebase 在使用者成功登入時自動產生的專屬權杖,可供伺服器用於驗證使用者。

更新網路服務以使用權杖

接下來,使用專屬的 Firebase ID 憑證在伺服器上驗證使用者,然後對使用者的憑證進行解密,以便將資料列印後回傳給使用者。

如要使用 Firebase ID 憑證:

  1. 擷取、驗證並解密位在 main.py 檔案的 root 方法中的憑證。

    from flask import Flask, render_template, request
    from google.auth.transport import requests
    from google.cloud import datastore
    import google.oauth2.id_token
    
    firebase_request_adapter = requests.Request()
    @app.route("/")
    def root():
        # Verify Firebase auth.
        id_token = request.cookies.get("token")
        error_message = None
        claims = None
        times = None
    
        if id_token:
            try:
                # Verify the token against the Firebase Auth API. This example
                # verifies the token on each page load. For improved performance,
                # some applications may wish to cache results in an encrypted
                # session store (see for instance
                # http://flask.pocoo.org/docs/1.0/quickstart/#sessions).
                claims = google.oauth2.id_token.verify_firebase_token(
                    id_token, firebase_request_adapter
                )
            except ValueError as exc:
                # This will be raised if the token is expired or any other
                # verification checks fail.
                error_message = str(exc)
    
            # Record and fetch the recent times a logged-in user has accessed
            # the site. This is currently shared amongst all users, but will be
            # individualized in a following step.
            store_time(datetime.datetime.now(tz=datetime.timezone.utc))
            times = fetch_times(10)
    
        return render_template(
            "index.html", user_data=claims, error_message=error_message, times=times
        )
    
    
  2. 確認您的 requirements.txt 檔案包含所有必要的依附元件:

    Flask==3.0.0
    google-cloud-datastore==2.15.1
    google-auth==2.17.3
    requests==2.28.2
    

測試網路服務

請在虛擬環境中本機執行網路服務,以便進行測試:

  1. 在專案的主要目錄中執行下列指令,以安裝新的依附元件並執行網路服務。如果您尚未設定本機測試用的虛擬環境,請參閱「測試您的網路服務」。

    pip install -r requirements.txt
    python main.py
    
  2. 請在網路瀏覽器中輸入下列網址,以便查看網路服務:

    http://localhost:8080
    

部署您的網路服務

現在您已在本機執行驗證,便可將網路服務重新部署至 App Engine。

您可以在 app.yaml 檔案所在的專案根目錄中執行下列指令:

gcloud app deploy

系統會自動將所有流量轉送至您部署的新版本。

如要進一步瞭解如何管理版本,請參閱管理服務和版本

查看服務

如要快速啟動瀏覽器並前往 https://PROJECT_ID.REGION_ID.r.appspot.com 使用網路服務,請執行下列指令:

gcloud app browse

後續步驟

現在您已經設定使用者驗證,可以開始瞭解如何更新網路服務,將已驗證使用者的資料個人化。