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()); } ?>
关键改动说明
- 根据kid匹配密钥:先解析令牌头部获取
kid,再从公钥列表中找到对应的密钥,这是签名验证通过的核心前提。 - 修正算法参数:
JWT::decode的第三个参数直接传入['RS256']数组,而非转成对象。 - 添加Claims验证:额外验证
iss(颁发者)、aud(受众)、exp(过期时间),确保令牌确实是你的Firebase项目颁发且合法有效。 - 简化异常处理:直接输出错误信息,避免多层异常抛出的冗余。
注意事项
- 不要用API密钥或项目ID代替公钥,Firebase ID令牌只能用Google提供的上述公钥列表验证。
- 建议缓存公钥列表(比如缓存1小时),避免每次验证都请求Google的API,提升性能。
内容的提问来源于stack exchange,提问作者Rolf
相关产品推荐
相关产品推荐

