第一种方法:验证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

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐