如何在 PHP 中使用 OPENID,而不使用库?
我使用的是 Twitch OAuth2.0,在构建身份验证后,我得到了“id_token”和“access_token”。
如何使用 PHP 验证 id_token 和 access_token 来获取代码?
ID Token是一种JWT。 JWT 是 JWS 或 JWE 的一种。以下为相关规格。
如果您想在不使用库的情况下验证 ID 令牌,那么第一步,您必须了解规范。然后,使用 PHP 标准函数进行解码、解密和验证签名等所有操作。
注意:无论ID Token是否加密,都会附有签名。要验证签名,如果签名的算法是非对称的,则必须获取与签名所用私钥相对应的公钥。
另一方面,访问令牌的格式因实现而异。
它可能无法完全回答问题。 我仔细研究了它,并试图找到一个快速而小的代码来完成它。 我需要帮助找到简单的例子。我自己想出来了,想在这里分享。
我将在此处展示的代码仅使用一个库来生成零件。
该示例可与 Google 或 Microsoft 一起使用。
第一部分是存储您的客户端 ID、孩子和证书 URL。
<?php
class AuthConstants
{
const GOOGLE_KID = 'WRITE_HERE_THE_KID';
const GOOGLE_CLIENT_ID = 'WRITE_HERE_YOUR_CLIENT_ID';
const GOOGLE_CERTS_URL = 'https://www.googleapis.com/oauth2/v3/certs';
const MICROSOFT_KID = 'WRITE_HERE_THE_KID';
const MICROSOFT_CLIENT_ID = 'WRITE_HERE_YOUR_CLIENT_ID';
const MICROSOFT_CERTS_URL = 'https://login.microsoftonline.com/common/discovery/v2.0/keys';
}
一些帮手:
<?php
class CommonHelpers {
public static function base64_url_decode($arg)
{
$res = $arg;
$res = str_replace('-', '+', $res);
$res = str_replace('_', '/', $res);
switch (strlen($res) % 4) {
case 0:
break;
case 2:
$res .= "==";
break;
case 3:
$res .= "=";
break;
default:
break;
}
$res = base64_decode($res);
return $res;
}
}
以及带有逻辑的
IdToken
类来验证它
<?php
require_once 'vendor/autoload.php';
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Math\BigInteger;
class IdToken
{
private ?string $id_token;
public array $headers;
public array $claims;
public function __construct($id_token)
{
$this->id_token = $id_token;
}
public function load_and_validate(): bool
{
$token_arr = explode('.', $this->id_token);
$headers_enc = $token_arr[0];
$claims_enc = $token_arr[1];
$this->headers = json_decode(CommonHelpers::base64_url_decode($headers_enc), TRUE);
$this->claims = json_decode(CommonHelpers::base64_url_decode($claims_enc), TRUE);
if (time() > $this->claims['exp'])
return false; // The token expired
$certs_url = self::get_validated_certs_url();
if (!$certs_url)
return false;
$public_key = self::generate_public_key($certs_url);
$token_sig_enc = $token_arr[2];
$signature = CommonHelpers::base64_url_decode($token_sig_enc);
$token_valid = openssl_verify($headers_enc . '.' . $claims_enc, $signature, $public_key, OPENSSL_ALGO_SHA256);
return $token_valid == 1;
}
function get_validated_certs_url(): ?string
{
$kid = $this->headers['kid'];
if (!$kid)
return null;
$aud = $this->claims['aud'];
if (!$aud)
return null;
if ($kid === AuthConstants::GOOGLE_KID && $aud === AuthConstants::GOOGLE_CLIENT_ID)
return AuthConstants::GOOGLE_CERTS_URL;
if ($kid === AuthConstants::MICROSOFT_KID && $aud === AuthConstants::MICROSOFT_CLIENT_ID)
return AuthConstants::MICROSOFT_CERTS_URL;
return null;
}
function generate_public_key($certs_url)
{
$publicKeys = json_decode(file_get_contents($certs_url));
$keyId = $this->headers['kid'];
$publicKey = null;
foreach ($publicKeys->keys as $key) {
if ($key->kid == $keyId) {
$publicKey = $key;
break;
}
}
if (!$publicKey)
return null; // Unable to find public key
$modulus = $publicKey->n;
$exponent = $publicKey->e;
$key = PublicKeyLoader::load([
'n' => new BigInteger(CommonHelpers::base64_url_decode($modulus), 256),
'e' => new BigInteger(CommonHelpers::base64_url_decode($exponent), 256),
]);
$formattedPublicKey = $key->toString('PKCS8');
if (!is_string($formattedPublicKey)) {
return null; // 'Failed to initialize the key'
}
return $formattedPublicKey;
}
}
您需要在
IdToken.php
所在的位置运行此命令:
composer require phpseclib/phpseclib:~3.0
要安装 Composer,请参阅:https://getcomposer.org/doc/00-intro.md
要验证 id_token,请调用:
$id_token = "eyj................" // replace this line with real id_token
$id_token = new IdToken($id_token);
$is_valid = $id_token->load_and_validate();