gpt4 book ai didi

php - 如何使用 PHP(JWT)验证 Firebase ID token ?

转载 作者:IT王子 更新时间:2023-10-28 23:30:20 26 4
gpt4 key购买 nike

我有一个共享托管计划,其中只有 PHP(没有 Java,没有 node.js)。我需要从我的 android 应用程序发送 firebase ID token 并通过 PHP-JWT 进行验证。

我正在学习教程:Verify Firebase ID tokens

上面写着:

If your backend is in a language that doesn't have an officialFirebase Admin SDK, you can still verify ID tokens. First, find athird-party JWT library for your language. Then, verify the header,payload, and signature of the ID token.

我找到了那个库:Firebase-PHP-JWT .在 gitHub 示例中;我看不懂

$关键部分:

`$key = "example_key";` 

$token 部分:

`$token = array(
"iss" => "http://example.org",
"aud" => "http://example.com",
"iat" => 1356999524,
"nbf" => 1357000000
);`

我的问题:

  1. $key 变量应该是什么?
  2. 为什么 &token 变量是一个数组?将从移动应用发送的 token 是一个字符串。
  3. 如果有人可以发布一个使用 PHP-JWT 验证 Firebase ID 的完整示例,我将不胜感激。

编辑:

好吧,我明白了。 GitHub 示例展示了如何生成 JWT 代码(编码)以及如何对其进行解码。在我的情况下,我只需要解码由 firebase 编码的 jwt。所以,我只需要使用这段代码:

$decoded = JWT::decode($jwt, $key, array('HS256'));

在此代码部分中,$jwt 是 Firebase ID token 。对于 $key 变量文档说:

Finally, ensure that the ID token was signed by the private key corresponding to the token's kid claim. Grab the public key from https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com and use a JWT library to verify the signature. Use the value of max-age in the Cache-Control header of the response from that endpoint to know when to refresh the public keys.

我不明白如何将这个公钥传递给解码函数。键是这样的:

"-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIIZ36AHgMyvnQwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE\nAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMTcw\nMjA4MDA0NTI2WhcNMTcwMjExMDExNTI2WjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tl\nbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBANBNTpiQplOYizNeLbs+r941T392wiuMWr1gSJEVykFyj7fe\nCCIhS/zrmG9jxVMK905KwceO/FNB4SK+l8GYLb559xZeJ6MFJ7QmRfL7Fjkq7GHS\n0/sOFpjX7vfKjxH5oT65Fb1+Hb4RzdoAjx0zRHkDIHIMiRzV0nYleplqLJXOAc6E\n5HQros8iLdf+ASdqaN0hS0nU5aa/cPu/EHQwfbEgYraZLyn5NtH8SPKIwZIeM7Fr\nnh+SS7JSadsqifrUBRtb//fueZ/FYlWqHEppsuIkbtaQmTjRycg35qpVSEACHkKc\nW05rRsSvz7q1Hucw6Kx/dNBBbkyHrR4Mc/wg31kCAwEAAaM4MDYwDAYDVR0TAQH/\nBAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ\nKoZIhvcNAQEFBQADggEBAEuYEtvmZ4uReMQhE3P0iI4wkB36kWBe1mZZAwLA5A+U\niEODMVKaaCGqZXrJTRhvEa20KRFrfuGQO7U3FgOMyWmX3drl40cNZNb3Ry8rsuVi\nR1dxy6HpC39zba/DsgL07enZPMDksLRNv0dVZ/X/wMrTLrwwrglpCBYUlxGT9RrU\nf8nAwLr1E4EpXxOVDXAX8bNBl3TCb2fu6DT62ZSmlJV40K+wTRUlCqIewzJ0wMt6\nO8+6kVdgZH4iKLi8gVjdcFfNsEpbOBoZqjipJ63l4A3mfxOkma0d2XgKR12KAfYX\ncAVPgihAPoNoUPJK0Nj+CmvNlUBXCrl9TtqGjK7AKi8=\n-----END CERTIFICATE-----\n"

在传递之前我需要将这个公钥转换成什么东西吗?我试图删除所有 "\n""-----BEGIN CERTIFICATE-----", "-----BEGIN CERTIFICATE -----"...但是没有运气。我仍然收到无效签名错误。有什么建议吗?

最佳答案

仅当您使用密码对 token 进行签名时才使用 HS256。 Firebase 在发布 token 时使用 RS256,因此,您需要来自给定 URL 的公钥,并且您需要将算法设置为 RS256。

还要注意,您在应用程序中获得的 token 不应是一个数组,而是一个包含 3 部分的字符串:header , body , 和 signature .每个部分由 . 分隔,因此它给你一个简单的字符串:header.body.signature

为了验证 token ,您需要做的是从 given URL 下载公钥。定期(检查 Cache-Control header 以获取该信息)并将其(JSON)保存在文件中,因此您不必每次需要检查 JWT 时都检索它。然后你可以读入文件并解码 JSON。解码后的对象可以传递给 JWT::decode(...)功能。这是一个简短的示例:

$pkeys_raw = file_get_contents("cached_public_keys.json");
$pkeys = json_decode($pkeys_raw, true);

$decoded = JWT::decode($token, $pkeys, ["RS256"]);

现在 $decoded变量包含 token 的有效负载。一旦你有解码的对象,你仍然需要验证它。根据the guide在 ID token 验证时,您必须检查以下事项:

  • exp在未来
  • iat过去了
  • iss : https://securetoken.google.com/<firebaseProjectID>
  • aud : <firebaseProjectID>
  • sub非空

因此,例如,您可以检查 iss像这样(其中 FIREBASE_APP_ID 是来自 firebase 控制台的应用 ID):

$iss_is_valid = isset($decoded->iss) && $decoded->iss === "https://securetoken.google.com/" . FIREBASE_APP_ID;

这里是刷新和检索 key 的完整示例。

Disclaimer: I haven't tested it and this is basically for informational purposes only.

$keys_file = "securetoken.json"; // the file for the downloaded public keys
$cache_file = "pkeys.cache"; // this file contains the next time the system has to revalidate the keys

/**
* Checks whether new keys should be downloaded, and retrieves them, if needed.
*/
function checkKeys()
{
if (file_exists($cache_file)) {
$fp = fopen($cache_file, "r+");

if (flock($fp, LOCK_SH)) {
$contents = fread($fp, filesize($cache_file));
if ($contents > time()) {
flock($fp, LOCK_UN);
} elseif (flock($fp, LOCK_EX)) { // upgrading the lock to exclusive (write)
// here we need to revalidate since another process could've got to the LOCK_EX part before this
if (fread($fp, filesize($this->cache_file)) <= time()) {
$this->refreshKeys($fp);
}
flock($fp, LOCK_UN);
} else {
throw new \RuntimeException('Cannot refresh keys: file lock upgrade error.');
}
} else {
// you need to handle this by signaling error
throw new \RuntimeException('Cannot refresh keys: file lock error.');
}

fclose($fp);
} else {
refreshKeys();
}
}

/**
* Downloads the public keys and writes them in a file. This also sets the new cache revalidation time.
* @param null $fp the file pointer of the cache time file
*/
function refreshKeys($fp = null)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);

$data = curl_exec($ch);

$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = trim(substr($data, 0, $header_size));
$raw_keys = trim(substr($data, $header_size));

if (preg_match('/age:[ ]+?(\d+)/i', $headers, $age_matches) === 1) {
$age = $age_matches[1];

if (preg_match('/cache-control:.+?max-age=(\d+)/i', $headers, $max_age_matches) === 1) {
$valid_for = $max_age_matches[1] - $age;
ftruncate($fp, 0);
fwrite($fp, "" . (time() + $valid_for));
fflush($fp);
// $fp will be closed outside, we don't have to

$fp_keys = fopen($keys_file, "w");
if (flock($fp_keys, LOCK_EX)) {
fwrite($fp_keys, $raw_keys);
fflush($fp_keys);
flock($fp_keys, LOCK_UN);
}
fclose($fp_keys);
}
}
}

/**
* Retrieves the downloaded keys.
* This should be called anytime you need the keys (i.e. for decoding / verification).
* @return null|string
*/
function getKeys()
{
$fp = fopen($keys_file, "r");
$keys = null;

if (flock($fp, LOCK_SH)) {
$keys = fread($fp, filesize($keys_file));
flock($fp, LOCK_UN);
}

fclose($fp);

return $keys;
}

最好的办法是安排一个 cronjob 来调用 checkKeys()随时需要,但我不知道您的提供商是否允许这样做。取而代之的是,您可以为每个请求执行此操作:

checkKeys();
$pkeys_raw = getKeys(); // check if $raw_keys is not null before using it!

关于php - 如何使用 PHP(JWT)验证 Firebase ID token ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42098150/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com