You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase JWT身份验证ID解码验证失败:无效令牌签名问题

Firebase ID令牌验证失败:Invalid token signature 解决方案

核心问题分析

  • 密钥匹配错误:你的代码直接取了公钥列表的第一个密钥,但Firebase的ID令牌是用对应kid(密钥ID)的公钥签名的,必须根据令牌头部的kid选择正确的公钥,否则必然签名验证失败。
  • 参数格式错误:JWT::decode的第三个参数应该是允许的算法数组,你转成对象是错误用法。

修正后的验证代码

<?php

require_once("Firebase/JWT/Key.php");
require_once("Firebase/JWT/ExpiredException.php");
require_once("Firebase/JWT/SignatureInvalidException.php");
require_once("Firebase/JWT/BeforeValidException.php");
require_once("Firebase/JWT/JWT.php");

use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use Firebase\JWT\BeforeValidException;
use Firebase\JWT\JWT;

$publicKeyURL = 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com';
$keyData = json_decode(file_get_contents($publicKeyURL), true);

$token = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjUyNmM2YTg0YWMwNjcwMDVjZTM0Y2VmZjliM2EyZTA4ZTBkZDliY2MiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUm9sZiBCcm9lciIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9zd2lwZS01YWQ5NCIsImF1ZCI6InN3aXBlLTVhZDk0IiwiYXV0aF90aW1lIjoxNzA0NDQ4NjYyLCJ1c2VyX2lkIjoicDNIbnBMOVo5UWVqOXdjajBtY0tITGxuYmh6MSIsInN1YiI6InAzSG5wTDlaOVFlajl3Y2owbWNLSExsbmJoejEiLCJpYXQiOjE3MDQ0NDk5MDAsImV4cCI6MTcwNDQ1MzUwMCwiZW1haWwiOiJyb2xmYnJvZXJAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbInJvbGZicm9lckBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.CrHpVyacsFbqMwrcl1vgWCi4x3M3zlyplyG-amplQuilwBHRAxh4V0GKJh9cVfcPd3w_iw40a_MPmhN0aBwkx0Pg4XwVjnArr0-f8Kwj4OosGo3J0d28LGpVjbk0wlyw9EwG4MGTTbXhvzUkHKEatPpTUX9Ly_8MEvs1msojXRG2bWIFHEfgQuAiN5aWW2-uYpRbAuZ2H02OuYNTKeH26Ok6s6lzf7Gcpy_kAE5WO_p7PRDTiWg2BZAjjuCvfEtAmELPzia9SEr3oLWJfdiiet4G28KJA1jTJb5qvfQzPvcRTWzvJrQevYdQbROP0POF-Jsl3jA979JUq1CwBQWzmA";

try {
    // 解析令牌头部获取密钥ID
    $decodedHeader = JWT::decodeHeader($token);
    $kid = $decodedHeader->kid;

    // 检查密钥列表中是否存在对应的kid
    if (!isset($keyData[$kid])) {
        throw new Exception('Invalid key ID in token header');
    }

    $selectedKey = $keyData[$kid];
    $key = new Key($selectedKey, 'RS256');

    // 第三个参数直接传算法数组,无需转对象
    $decodedToken = JWT::decode($token, $key, ['RS256']);

    // 额外验证Firebase令牌的关键Claims
    $projectId = 'swipe-5ad94'; // 替换成你的Firebase项目ID
    if ($decodedToken->iss !== "https://securetoken.google.com/{$projectId}") {
        throw new Exception('Invalid token issuer');
    }
    if ($decodedToken->aud !== $projectId) {
        throw new Exception('Invalid token audience');
    }
    if ($decodedToken->exp < time()) {
        throw new Exception('Token expired');
    }

    echo "Token validation successful";
    // 这里可以使用$decodedToken中的用户信息
} catch (ExpiredException $e) {
    die('Token expired!');
} catch (SignatureInvalidException $e) {
    die('Invalid token signature!');
} catch (BeforeValidException $e) {
    die('Token not valid yet!');
} catch (Exception $e) {
    die('Error: ' . $e->getMessage());
}
?>

关键改动说明

  1. 根据kid匹配密钥:先解析令牌头部获取kid,再从公钥列表中找到对应的密钥,这是签名验证通过的核心前提。
  2. 修正算法参数:JWT::decode的第三个参数直接传入['RS256']数组,而非转成对象。
  3. 添加Claims验证:额外验证iss(颁发者)、aud(受众)、exp(过期时间),确保令牌确实是你的Firebase项目颁发且合法有效。
  4. 简化异常处理:直接输出错误信息,避免多层异常抛出的冗余。

注意事项

  • 不要用API密钥或项目ID代替公钥,Firebase ID令牌只能用Google提供的上述公钥列表验证。
  • 建议缓存公钥列表(比如缓存1小时),避免每次验证都请求Google的API,提升性能。

内容的提问来源于stack exchange,提问作者Rolf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 00:35:03