-
-
Save ameen-sarsour/e14a1d5bae5b61080dfdd5b1430c3e10 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* This file to test apple sign in, | |
* Need to app information from Apple | |
* | |
* Team Id | |
* Client Id | |
* Key Id | |
* and Private key | |
* | |
* and from App need to use code | |
* | |
* Note: Some of this codde is taken from firebase JWT library, I just copy&paste to make | |
* this code runnable withouy any dependency | |
* | |
*/ | |
const TEAM_ID = 'XXXXXXXXXX'; | |
const CLIENT_ID = 'com.XXXXXXXX.App'; | |
const KEY_ID = 'XXXXXXXXXX'; | |
$key = <<<KEY | |
-----BEGIN PRIVATE KEY----- | |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
XXXXXXXX | |
-----END PRIVATE KEY----- | |
KEY; | |
$ecdsa_key = openssl_pkey_get_private($key); | |
$header = array('typ' => 'JWT', 'alg' => 'ES256'); | |
$header['kid'] = KEY_ID; | |
$payload = [ | |
'iss' => TEAM_ID, | |
'iat' => time(), | |
'exp' => time() + 86400*180, | |
'aud' => 'https://appleid.apple.com', | |
'sub' => CLIENT_ID, | |
]; | |
$segments = array(); | |
$segments[] = urlsafeB64Encode(json_encode($header)); | |
$segments[] = urlsafeB64Encode(json_encode($payload)); | |
$signing_input = \implode('.', $segments); | |
$signature = ''; | |
$success = \openssl_sign($signing_input, $signature, $key, 'SHA256'); | |
if (!$success) { | |
throw new DomainException("OpenSSL unable to sign data"); | |
} else { | |
if ($header['alg'] === 'ES256') { | |
$signature = signatureFromDER($signature, 256); | |
} | |
} | |
$segments[] = urlsafeB64Encode($signature); | |
$clientSecret = \implode('.', $segments); | |
$code = 'CODE GENERATED FROM APP'; | |
$curl = curl_init(); | |
$postData = [ | |
"client_id=CLIENT_ID", | |
"client_secret=$clientSecret", | |
"code=$code", | |
"grant_type=authorization_code" | |
]; | |
curl_setopt_array($curl, array( | |
CURLOPT_URL => "https://appleid.apple.com/auth/token", | |
CURLOPT_RETURNTRANSFER => true, | |
CURLOPT_ENCODING => "", | |
CURLOPT_MAXREDIRS => 10, | |
CURLOPT_TIMEOUT => 0, | |
CURLOPT_FOLLOWLOCATION => true, | |
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, | |
CURLOPT_CUSTOMREQUEST => "POST", | |
CURLOPT_POSTFIELDS => implode('&', $postData), | |
)); | |
$response = curl_exec($curl); | |
curl_close($curl); | |
echo $response; | |
function urlsafeB64Encode($input) | |
{ | |
return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_')); | |
} | |
function signatureFromDER($der, $keySize) | |
{ | |
// OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE | |
list($offset, $_) = readDER($der); | |
list($offset, $r) = readDER($der, $offset); | |
list($offset, $s) = readDER($der, $offset); | |
// Convert r-value and s-value from signed two's compliment to unsigned | |
// big-endian integers | |
$r = \ltrim($r, "\x00"); | |
$s = \ltrim($s, "\x00"); | |
// Pad out r and s so that they are $keySize bits long | |
$r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT); | |
$s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT); | |
return $r . $s; | |
} | |
function readDER($der, $offset = 0) | |
{ | |
$ASN1_BIT_STRING = 0x03; | |
$pos = $offset; | |
$size = \strlen($der); | |
$constructed = (\ord($der[$pos]) >> 5) & 0x01; | |
$type = \ord($der[$pos++]) & 0x1f; | |
// Length | |
$len = \ord($der[$pos++]); | |
if ($len & 0x80) { | |
$n = $len & 0x1f; | |
$len = 0; | |
while ($n-- && $pos < $size) { | |
$len = ($len << 8) | \ord($der[$pos++]); | |
} | |
} | |
// Value | |
if ($type == $ASN1_BIT_STRING) { | |
$pos++; // Skip the first contents octet (padding indicator) | |
$data = \substr($der, $pos, $len - 1); | |
$pos += $len - 1; | |
} elseif (!$constructed) { | |
$data = \substr($der, $pos, $len); | |
$pos += $len; | |
} else { | |
$data = null; | |
} | |
return array($pos, $data); | |
} |
it's working for me, I used it in real application.
@ihemantkumar, do you faced any issue?
It only worked to me once, and I created my application on the base of that and later Apple started throwing me an error, invalid_grant
. I was positive that the error wasn't at my end but it has to be the issue on the auth_token
from the App. But I am not the App developer. App developer was only saying 1 thing, they have to send the auth_token
only, nothing else is to be at their end. We abandon this feature now. Added this feature from the App instead of the Server end.
I think on line 52 $key should be $ecdsa_key because its not used anywhere, right?
Yes, that's right.
https://sarunw.com/posts/sign-in-with-apple-3/
$key is constantly changing, you can't hardcode it, that's why this doesn't work.
Hi @ameen-sarsour
I am getting error {"error":"invalid_client"}
- and from App need to use code
Is this code generated from flutter appappleCredential.identityToken
I am getting error {"error":"invalid_client"}
me too invalid_client
if o create the client_id on ruby it works but client_id created on php doesnt works!
me too invalid_client . I am using this code and create client_secret but show this error
Client error:
POST https://appleid.apple.com/auth/token
resulted in a400 Bad Request
response: {"error":"invalid_client","email_verified":false}
Is this code working for anyone?