验证 ID 令牌

如果您的 Firebase 客户端应用与自定义后端服务器通信,您可能需要标识该服务器上当前已登录的用户。若要安全地执行此操作,请在用户成功登录后,使用 HTTPS 将该用户的 ID 令牌发送到您的服务器。然后,在服务器上验证 ID 令牌的完整性和真实性,并从中检索 uid。您可以使用以这种方式传输的 uid,安全地标识服务器上当前已登录的用户。

准备工作

为了使用 Firebase Admin SDK 验证 ID 令牌,您必须拥有一个服务帐号。请参阅 Admin SDK 设置说明,详细了解如何使用服务帐号初始化 Admin SDK。

在客户端上检索 ID 令牌

当用户或设备成功登录后,Firebase 会为其创建一个相应的 ID 令牌作为唯一标识,并向其授予多个资源(例如 Firebase Realtime Database 和 Cloud Storage)的访问权限。您还可以将该 ID 令牌用于标识自定义后端服务器上的用户或设备。如需从客户端检索 ID 令牌,请确保用户已登录,然后从已登录的用户处获取 ID 令牌:

iOS+

Objective-C
FIRUser *currentUser = [FIRAuth auth].currentUser;
[currentUser getIDTokenForcingRefresh:YES
                           completion:^(NSString *_Nullable idToken,
                                        NSError *_Nullable error) {
          if (error) {
            // Handle error
            return;
          }

          // Send token to your backend via HTTPS
          // ...
}];
Swift
let currentUser = FIRAuth.auth()?.currentUser
currentUser?.getIDTokenForcingRefresh(true) { idToken, error in
  if let error = error {
    // Handle error
    return;
  }

  // Send token to your backend via HTTPS
  // ...
}

Android

FirebaseUser mUser = FirebaseAuth.getInstance().getCurrentUser();
mUser.getIdToken(true)
    .addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
        public void onComplete(@NonNull Task<GetTokenResult> task) {
            if (task.isSuccessful()) {
                String idToken = task.getResult().getToken();
                // Send token to your backend via HTTPS
                // ...
            } else {
                // Handle error -> task.getException();
            }
        }
    });

Unity

Firebase.Auth.FirebaseUser user = auth.CurrentUser;
user.TokenAsync(true).ContinueWith(task => {
  if (task.IsCanceled) {
    Debug.LogError("TokenAsync was canceled.");
   return;
  }

  if (task.IsFaulted) {
    Debug.LogError("TokenAsync encountered an error: " + task.Exception);
    return;
  }

  string idToken = task.Result;

  // Send token to your backend via HTTPS
  // ...
});

C++

firebase::auth::User user = auth->current_user();
if (user.is_valid()) {
  firebase::Future<std::string> idToken = user.GetToken(true);

  // Send token to your backend via HTTPS
  // ...
}

Web

firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
  // Send token to your backend via HTTPS
  // ...
}).catch(function(error) {
  // Handle error
});

拥有 ID 令牌后,您就可以将该 JWT 发送到您的后端,并使用 Firebase Admin SDK 进行验证,或者使用第三方 JWT 库(如果您的服务器使用某种非 Firebase 原生支持的语言编写)进行验证。

使用 Firebase Admin SDK 验证 ID 令牌

Firebase Admin SDK 内置了用于验证和解码 ID 令牌的方法。如果提供的 ID 令牌格式正确、未过期且经过正确签名,则该方法会返回已解码的 ID 令牌。您可以从已解码的令牌中获取用户或设备的 uid

请按照 Admin SDK 安装说明,使用服务帐号初始化 Admin SDK。然后,使用 verifyIdToken() 方法验证 ID 令牌:

Node.js

// idToken comes from the client app
getAuth()
  .verifyIdToken(idToken)
  .then((decodedToken) => {
    const uid = decodedToken.uid;
    // ...
  })
  .catch((error) => {
    // Handle error
  });

Java

// idToken comes from the client app (shown above)
FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken);
String uid = decodedToken.getUid();

Python

# id_token comes from the client app (shown above)

decoded_token = auth.verify_id_token(id_token)
uid = decoded_token['uid']

Go

client, err := app.Auth(ctx)
if err != nil {
	log.Fatalf("error getting Auth client: %v\n", err)
}

token, err := client.VerifyIDToken(ctx, idToken)
if err != nil {
	log.Fatalf("error verifying ID token: %v\n", err)
}

log.Printf("Verified ID token: %v\n", token)

C#

FirebaseToken decodedToken = await FirebaseAuth.DefaultInstance
    .VerifyIdTokenAsync(idToken);
string uid = decodedToken.Uid;

如需验证 ID 令牌,您需要一个项目 ID。Firebase Admin SDK 将通过以下方法之一,尝试获取项目 ID:

  • 如果您是使用显式 projectId 应用选项对 SDK 执行初始化,则 SDK 将使用该选项的值。
  • 如果您是使用服务帐号凭据对 SDK 执行初始化,则 SDK 将使用服务帐号 JSON 对象的 project_id 字段。
  • 如果您设置了 GOOGLE_CLOUD_PROJECT 环境变量,则 SDK 会将此环境变量值作为项目 ID。在 Google 基础架构(如 App Engine 和 Compute Engine)上运行的代码可使用此环境变量。

使用第三方 JWT 库验证 ID 令牌

即使您的后端使用的语言不受 Firebase Admin SDK 支持,您也仍然可以验证 ID 令牌。首先,找到适合您的语言的第三方 JWT 库。然后验证 ID 令牌的标头、载荷和签名。

验证 ID 令牌的标头符合以下限制:

ID 令牌标头声明
alg 算法 "RS256"
kid 密钥 ID 必须与以下网址上列出的其中一个公钥相对应: https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com

验证 ID 令牌的载荷符合以下限制:

ID 令牌载荷声明
exp 到期时间 必须是将来的时间。时间从 UNIX 计时原点开始计算,以秒为单位。
iat 颁发时间 必须是过去的时间。时间从 UNIX 计时原点开始计算,以秒为单位。
aud 受众 必须是您的 Firebase 项目 ID(您的 Firebase 项目的唯一标识符),可在该项目控制台的网址中找到。
iss 颁发者 必须是 "https://securetoken.google.com/<projectId>",其中 <projectId> 是用于上述 aud 的同一项目 ID。
sub 主体 必须是非空字符串,并且必须是用户或设备的 uid
auth_time 身份验证时间 必须是过去的时间。指的是用户通过身份验证的时间。

最后,请确保 ID 令牌是通过与令牌的 kid 声明相对应的私钥进行签名的。从 https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com 获取公钥并使用 JWT 库来验证签名。使用来自该端点的响应的 Cache-Control 标头中的 max-age 的值来了解何时刷新公钥。

如果上述所有验证都成功完成,您就可以使用 ID 令牌的主体 (sub) 作为相应用户或设备的 uid