Apple Sign in "Invalid_Client", Signing Jwt for Authentication Using PHP and Openssl

Apple Sign In invalid_client, signing JWT for authentication using PHP and openSSL

As indicated here, the problem is actually in the signature generated by openSSL.

Using ES256, the digital signature is the concatenation of two unsigned integers, denoted as R and S, which are the result of the Elliptic Curve (EC) algorithm. The length of R || S is 64.

The openssl_sign function generates a signature which is a DER-encoded ASN.1 structure (with size > 64).

The solution is to convert the DER-encoded signature into a raw concatenation of the R and S values. In this library a function "fromDER" is present which perform such a conversion:

    /**
* @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;
}

Another point is that a .pem key should be provided to the open_ssl_sign function. Starting from the .p8 key downloaded from the Apple developer I have created the .pem one by using openSSL:

openssl pkcs8 -in AuthKey_KEY_ID.p8 -nocrypt -out AuthKey_KEY_ID.pem

In the following my new generateJWT function code which uses the .pem key and the fromDER function to convert the signature generated by openSSL:

    function generateJWT($kid, $iss, $sub) {

$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(file_get_contents('AuthKey_.pem'));
if (!$privKey){
return false;
}

$payload = $this->encode(json_encode($header)).'.'.$this->encode(json_encode($body));

$signature = '';
$success = openssl_sign($payload, $signature, $privKey, OPENSSL_ALGO_SHA256);
if (!$success) return false;

$raw_signature = $this->fromDER($signature, 64);

return $payload.'.'.$this->encode($raw_signature);
}

Hope it helps

Always occurs invalid_client error trying to get refresh token by authorisation code during sign with apple implementation

off the top of my head, try two things:

  1. add an Accept header: 'Accept: application/x-www-form-urlencoded'
  2. pass your post data not inside another array: http_build_query($send_data)

If I think of more things I'll edit my answer.

Sign in with Apple = invalid_client

The problem was this special encryption.
In this blog they use PHP for everything except the client_secret generation.
https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple

And in the text the author explains this sentence:

Some JWT libraries don’t support elliptic curve methods, so make sure yours does before you start trying this out.

Now it's working fine with exactly the code in the top - only replaced the client_secret generation.

How to verify code from Sign In with Apple?

The problem for me was that I forgot to verify my domain under the Service Id section of the Apple dev portal.

You need to download the key they give you, and upload it to:
https://example.com/.well-known/apple-developer-domain-association.txt

The website doesn't verify automatically, you have to click the verify button and get a green tick next to the domain to be sure. After this, I had no more invalid_client issues.

Sample Image

Update 2020.07

As the flow was changed, you just have to add the Domain and the Communication Email to:

Certificates, Identifiers & Profiles > More > Configure



Related Topics



Leave a reply



Submit