【PHP】苹果登录(sign in with apple)后端验证
第一种方法:通过苹果JWK来验证identityTokenAPP端登录成功后,会拿到一个identityToken的字段,里面的值是一个JWT,以.符号分隔,分为header、payload、signature三部分,类似长这样:eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY2
第一种方法:验证identityToken
APP端登录成功后,会拿到一个identityToken
的字段,里面的值是一个JWT,以.
符号分隔,分为header、payload、signature三部分,类似长这样:
xxxx.yyyy.zzzz
APP需要把这个identityToken
传给后端,后端进行验证。JWT里的signature部分是苹果使用私钥对其进行的签名,要验证这个签名,需要先获得苹果的公钥,而公钥可以通过JWKS(JSON Web Key Set)来转换获得。获取JWKS的接口是https://appleid.apple.com/auth/keys
接口返回的内容如上图,可以看到目前苹果有三个JWK(后面可能会随时变更,因此不能写死在服务端,必须通过接口获取!
)。然后我们需要用到一个名为php-jwt的第三方库,使用composer
命令安装:
composer require firebase/php-jwt
安装完后可以使用这个库来验证JWT签名和获取payload里的信息了,代码如下:
require_once(__DIR__ . "/vendor/autoload.php");
use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
// APP登录成功后获得的identityToken
$identityToken = 'xxxx.yyyy.zzzz';
// 调用苹果接口获取JWKS
$apiResponse = file_get_contents('https://appleid.apple.com/auth/keys');
$jwkSet = json_decode($apiResponse, true);
if (!is_array($jwkSet)) {
exit("Get JWK failed");
}
// 将JWKS转换为公钥
try {
$pubKeys = JWk::parseKeySet($jwkSet);
} catch (\Exception $e) {
exit("Parse key set failed:" . $e->getMessage());
}
// 使用公钥对JWT进行检验
try {
$payload = JWT::decode($identityToken, $pubKeys, ['RS256']);
} catch (\Exception $e) {
exit("Decode JWT failed:" . $e->getMessage());
}
// 若检验通过,会得到JWT里的payload信息
echo(json_encode($payload));
下图是payload里包含的信息例子:
获得payload信息后,还需要检查以下字段,确保:
- iss的值等于https://appleid.apple.com
- exp是JWT的过期时间,当前时间不能大于exp,也就是不能过期
- aud的值等于你的应用的APP ID
全部通过后,就可以使用sub字段(用户的苹果账号ID)和email字段(用户的邮箱地址)完成后面的登录逻辑啦。
第二种方法:验证authorizationCode
APP端登录成功后,除了会拿到identityToken
字段外,还可以拿到一个authorizationCode
字段,下面介绍验证authorizationCode
的方法。
验证authorizationCode
需要有p8格式的私钥文件、私钥ID(Key ID)、Team ID这三样东西,私钥文件可以在苹果开发者后台下载,Key ID和Team ID也同样,见下图:
下载p8格式私钥后,使用下面命令转换为pem格式:
openssl pkcs8 -nocrypt -in AuthKey.p8 -out AuthKey.pem
最后通过苹果提供的接口来验证:
require_once(__DIR__ . "/vendor/autoload.php");
use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
$clientId = ''; // APP的BUNDLE_ID
$authCode = ''; // APP登录成功后获得的authorizationCode
$teamId = ''; // Team ID
$keyId = ''; // Key ID
$privateKeyFilePath = "/path/to/AuthKey.pem"; // pem格式私钥文件的路径
// 使用私钥来生成client_secret
$privateKey = openssl_pkey_get_private("file://$privateKeyFilePath");
if (!$privateKey) {
exit("Get private key failed");
}
$payload = [
'iss' => $teamId,
'iat' => time(),
'exp' => time() + 86400,
'aud' => 'https://appleid.apple.com',
'sub' => $clientId,
];
$clientSecret = JWT::encode($payload, $privateKey, 'ES256', $keyId);
// 调用苹果接口验证authorizationCode
$postParams = [
'client_id' => $clientId,
'client_secret' => $clientSecret,
'code' => $authCode,
'grant_type' => 'authorization_code',
];
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://appleid.apple.com/auth/token',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($postParams),
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
var_dump($httpCode, $response);
若验证成功,苹果会返回200 HTTP状态码,并且有返回一个id_token
字段,里面是一个包含用户信息的JWT,base64decode后再json_decode,就可以获得用户信息。
参考文章:
https://sarunw.com/posts/sign-in-with-apple-3/#how-to-verify-the-token
https://sarunw.com/posts/sign-in-with-apple-4/#create-a-sign-in-with-apple-private-key
更多推荐
所有评论(0)