web-dev-qa-db-fra.com

Comment vérifier le code de "Se connecter avec Apple"?

J'essaie de vérifier le code que j'ai obtenu du service "Se connecter avec Apple" sur mon Uri de redirection. J'ai utilisé les informations de la documentation pour créer les données de publication et générer le "client_secret".

La réponse que je reçois est: {"error":"invalid_client"}.

Mes fonctions pour générer le "client_secret" se trouvent ci-dessous:

function encode($data) {
    $encoded = strtr(base64_encode($data), '+/', '-_');
    return rtrim($encoded, '=');
}

function generateJWT($kid, $iss, $sub, $key) {
    $header = [
        'alg' => 'ES256',
        'kid' => $kid
    ];
    $body = [
        'iss' => $iss,
        'iat' => time(),
        'exp' => time() + 3600,
        'aud' => 'https://appleid.Apple.com',
        'sub' => $sub
    ];

    $privKey = openssl_pkey_get_private($key);
    if (!$privKey) return false;

    $payload = encode(json_encode($header)).'.'.encode(json_encode($body));
    $signature = '';
    $success = openssl_sign($payloads, $signature, $privKey, OPENSSL_ALGO_SHA256);
    if (!$success) return false;

    return $payload.'.'.encode($signature);
}

Mes variables dans cet exemple:

$ kid est mon identifiant pour ma clé privée. Dans cet exemple, c'est JYJ5GS7N9K. J'ai obtenu l'identifiant d'ici https://developer.Apple.com/account/resources/authkeys/list

$ iss est l'identifiant d'équipe de mon compte développeur. Dans cet exemple, c'est WGL33ABCD6.

$ sub est la même valeur que "client_id". Mon "client_id" dans cet exemple est "dev.hanashi.sign-in-with-Apple". J'ai obtenu l'ID client des identifiants d'application ici: https://developer.Apple.com/account/resources/identifiers/list

$ key est ma clé privée générée par le compte développeur. La clé a un format comme celui-ci:

-----BEGIN PRIVATE KEY-----
myrandomgeneratedkeybyappledeveloperaccount
-----END PRIVATE KEY-----

Voici le code php pour faire la demande:

$key = <<<EOD
-----BEGIN PRIVATE KEY-----
myrandomgeneratedkeybyappledeveloperaccount
-----END PRIVATE KEY-----
EOD; // replaced with correct key

$kid = 'JYJ5GS7N9K'; // identifier for private key
$iss = 'WGL33ABCD6'; // team identifier
$sub = 'dev.hanashi.sign-in-with-Apple'; // my app id

$jwt = generateJWT($kid, $iss, $sub, $key);

$data = [
    'client_id' => $sub,
    'client_secret' => $jwt,
    'code' => $_POST['code'],
    'grant_type' => 'authorization_code',
    'request_uri' => 'https://myurl.tld/redirect.php'
];
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://appleid.Apple.com/auth/token');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6');

$serverOutput = curl_exec($ch);

curl_close ($ch);
echo $serverOutput;

Je reçois maintenant la réponse {"error":"invalid_client"} depuis le serveur Apple. Qu'est-ce que je fais mal? Est-ce que je ne peux pas générer le jeton JWT?

15
Hanashi

Le {"error":"invalid_client"} le message peut être lié à une signature non valide générée par la fonction openssl_sign. L'algorithme ES256 doit être utilisé pour signer le JWT et la signature générée doit être la concaténation de deux entiers non signés, notés R et S. Il s'avère que la fonction openssl_sign génère une signature ASN.1 codée DER qui n'est pas correcte pour Apple (voir ici ).

La solution consiste donc à convertir la signature ASN.1 codée DER générée par openSSL en une simple concaténation des valeurs R et S.

Cela peut être fait en utilisant la fonction suivante:

/**
 * @param string $der
 * @param int    $partLength
 *
 * @return string
 */
public static function fromDER(string $der, int $partLength)
{
    $hex = unpack('H*', $der)[1];
    if ('30' !== mb_substr($hex, 0, 2, '8bit')) { // SEQUENCE
        throw new \RuntimeException();
    }
    if ('81' === mb_substr($hex, 2, 2, '8bit')) { // LENGTH > 128
        $hex = mb_substr($hex, 6, null, '8bit');
    } else {
        $hex = mb_substr($hex, 4, null, '8bit');
    }
    if ('02' !== mb_substr($hex, 0, 2, '8bit')) { // INTEGER
        throw new \RuntimeException();
    }
    $Rl = hexdec(mb_substr($hex, 2, 2, '8bit'));
    $R = self::retrievePositiveInteger(mb_substr($hex, 4, $Rl * 2, '8bit'));
    $R = str_pad($R, $partLength, '0', STR_PAD_LEFT);
    $hex = mb_substr($hex, 4 + $Rl * 2, null, '8bit');
    if ('02' !== mb_substr($hex, 0, 2, '8bit')) { // INTEGER
        throw new \RuntimeException();
    }
    $Sl = hexdec(mb_substr($hex, 2, 2, '8bit'));
    $S = self::retrievePositiveInteger(mb_substr($hex, 4, $Sl * 2, '8bit'));
    $S = str_pad($S, $partLength, '0', STR_PAD_LEFT);
    return pack('H*', $R.$S);
}
/**
 * @param string $data
 *
 * @return string
 */
private static function preparePositiveInteger(string $data)
{
    if (mb_substr($data, 0, 2, '8bit') > '7f') {
        return '00'.$data;
    }
    while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') <= '7f') {
        $data = mb_substr($data, 2, null, '8bit');
    }
    return $data;
}
/**
 * @param string $data
 *
 * @return string
 */
private static function retrievePositiveInteger(string $data)
{
    while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') > '7f') {
        $data = mb_substr($data, 2, null, '8bit');
    }
    return $data;
}

qui peut être trouvé dans la bibliothèque this . Plus de détails ici Connexion Apple, signez JWT pour l'authentification en utilisant PHP et openSSL

1
Andrea Gorrieri

J'ai eu cette erreur plusieurs fois. Voici les causes que j'ai pu trouver:

  • Vérification de domaine incorrecte et redirect_uri invalide
  • L'ID client n'est pas correct: votre ID client peut être incorrect.
  • L'encodeur JWT ne fonctionne pas correctement: peut-être qu'il ne prend pas en charge l'algorithme ES256?
  • Type de demande: pour/auth/authorize, vous devez utiliser x-www-form-urlencode, sinon vous obtenez invalid_client les erreurs.

Lorsque j'ai résolu ces problèmes, j'ai commencé à obtenir invalid_grant Erreur. Voici les étapes que j'avais faites:

  • J'ai créé client_secret via JWT
  • J'ai accédé à https://appleid.Apple.com/auth/authorize?response_type=code&state=abcdefg&client_id=com.company.Apple-sign-in-abcd&scope=openid&redirect_uri=https://app.com/redirect_uri manuellement sur le navigateur Web,
  • autorisé
  • il a redirigé vers l'URL backend (qui a donné 404 car il n'était pas encore déployé).
  • J'ai copié l'argument de code dans la barre d'url lorsque
  • Avec le code copié, j'ai POSÉ le https://appleid.Apple.com/auth/token point de terminaison avec arguments x-www-form-urlencoded:
    • code
    • identité du client
    • client_secret
    • redirect_uri
    • grant_type = "code_autorisation"

Si vous perdez quelques secondes, code est invalidé et vous obtiendrez invalid_grant Erreur. Si vous copiez et collez immédiatement en quelques secondes, vous obtiendrez votre réponse:

{
    "access_token": "abcdefg",
    "token_type": "Bearer",
    "expires_in": 3600,
    "refresh_token": "abcdefg",
    "id_token": "abcdefghijklmnopqrstu"
}

La prochaine étape serait de décoder id_token avec la clé publique d'Apple.

0
aladagemre

J'ai fait un petit paquet pour générer Apple secret client en php, basé sur jwt-framework: https://github.com/kissdigital-com/Apple-sign-in- client-secret-generator

0
Maciej Płocki