使用 Firebase 对用户进行身份验证

区域 ID

REGION_ID 是 Google 根据您在创建应用时选择的区域分配的缩写代码。此代码不对应于国家/地区或省,尽管某些区域 ID 可能类似于常用国家/地区代码和省代码。对于 2020 年 2 月以后创建的应用,REGION_ID.r 包含在 App Engine 网址中。对于在此日期之前创建的现有应用,网址中的区域 ID 是可选的。

详细了解区域 ID

将用户登录流添加到使用 Firebase 身份验证的 Web 服务。

在指南的此步骤中,您将更新 Web 服务以对用户进行身份验证,并在用户进行身份验证后检索并显示用户自己的信息。请注意,对于此步骤,站点请求时间仍然是全局的而不是特定于用户的。

准备工作

如果您已完成本指南中前面的所有步骤,请跳过此部分。否则,请完成以下任一操作:

添加 Firebase 身份验证方法

Firebase 提供了可用于配置 Web 服务登录行为的 JavaScript 方法和变量。对于此 Web 服务,您将要添加一个退出登录函数、一个配置登录界面的变量,以及一个控制用户登录或退出登录时所发生的更改的函数。

如需添加身份验证流程所需的行为,请将 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 在用户成功登录时自动生成的唯一令牌,并由服务器用于对用户进行身份验证。

更新 Web 服务以使用令牌

接下来,使用用户的唯一 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
    

测试您的 Web 服务

通过在虚拟环境中本地运行 Web 服务来对其进行测试:

  1. 在项目的主目录中运行以下命令,以安装新的依赖项并运行 Web 服务。如果您尚未为本地测试设置虚拟环境,请参阅测试您的 Web 服务

    pip install -r requirements.txt
    python main.py
    
  2. 在网络浏览器中输入以下地址,以查看您的 Web 服务:

    http://localhost:8080
    

部署 Web 服务

在本地完成身份验证后,您可以将 Web 服务重新部署到 App Engine。

app.yaml 文件所在项目的根目录运行以下命令:

gcloud app deploy

所有流量都会自动路由到您部署的新版本。

如需详细了解如何管理版本,请参阅管理服务和版本

查看您的服务

如需快速启动浏览器并通过 https://PROJECT_ID.REGION_ID.r.appspot.com 访问您的 Web 服务,请运行以下命令:

gcloud app browse

后续步骤

现在您已经设置了用户身份验证,接下来可以了解如何更新 Web 服务为经过身份验证的用户个性化数据。