firebase/php-jwt解码报$keyMaterial类型错误排查
问题根因
抛出$keyMaterial must be a string, resource, or OpenSSLAsymmetricKey错误的直接原因是传参非法,同时代码存在逻辑错误会导致后续验签失败:
- 直接将包含多个证书的完整数组
$pkeys传入Key类构造函数,该类只接受单个公钥字符串、OpenSSL资源或OpenSSLAsymmetricKey实例作为密钥材料,不接受数组类型参数 - 谷歌接口返回的是X.509格式证书,不是直接可用的公钥字符串,即使传入单个证书内容不做解析,也会被判定为无效密钥材料
- 逻辑错误:当前拉取的谷歌公钥用于验签谷歌自动生成的Firebase ID Token,和你自己持有的服务账号私钥不属于同一密钥对,哪怕密钥格式问题修复,用这个公钥验你自己签发的JWT也会报签名无效。
修复步骤
- 拆分JWT结构,解码头部的
kid字段,从公钥列表里匹配对应ID的单个证书,禁止传入整个公钥数组 - 用
openssl_pkey_get_public()函数解析X.509证书内容,得到合法的OpenSSLAsymmetricKey实例后再传入Key类 - 修正验签逻辑:验自己签发的JWT时,使用和签发私钥配对的公钥;验Firebase官方生成的用户ID Token时,再使用当前拉取的谷歌公钥
- 删除多余的手动引入JWT组件异常文件的代码,composer autoload已经实现自动加载,手动引入容易在版本更新时出现路径匹配错误。
修正后的验签代码
将原代码中if ($pKeys_raw) {块内的逻辑替换为以下内容:
$pkeys = json_decode($pKeys_raw, true); // 解析JWT头部拿匹配的公钥ID $tokenParts = explode('.', $token); if (count($tokenParts) !== 3) { throw new \RuntimeException('Invalid JWT structure'); } $header = json_decode(base64_decode(strtr($tokenParts[0], '-_', '+/')), true); if (!isset($header['kid'], $pkeys[$header['kid']])) { throw new \RuntimeException('No matching public key found for current JWT'); } // 从X.509证书解析出合法公钥 $cert = $pkeys[$header['kid']]; $publicKey = openssl_pkey_get_public($cert); if (!$publicKey) { throw new \RuntimeException('Failed to parse public key from X.509 certificate'); } // 执行验签解码 $decodejwt = JWT::decode($token, new Key($publicKey, 'RS256')); $decoded_array = (array) $decodejwt; echo "Decode:\n" . print_r($decoded_array, true) . "\n";
内容的提问来源于stack exchange,提问作者madsongr
相关产品推荐
相关产品推荐

