How to verify code from "Sign In with Apple"?
I had this error several times. Here are the causes I could find:
- Improper Domain Verification and invalid redirect_uri
- Client ID is not correct: your client ID could be incorrect.
- JWT encoder is not working properly: maybe it doesn't support the ES256 algorithm?
- Request type: for /auth/authorize, you need to use x-www-form-urlencode, oherwise you get
invalid_client
errors.
When I solved these problems, I started to get invalid_grant
error. Here were the steps I had been doing:
- I created client_secret via JWT
- I navigated to
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
manually on web browser, - authorized
- it redirected back to backend url (which gave 404 because it was not deployed yet).
- I copied the code argument in the url bar when
- With the copied
code
, I POSTeed thehttps://appleid.apple.com/auth/token
endpoint with x-www-form-urlencoded arguments:- code
- client_id
- client_secret
- redirect_uri
- grant_type="authorization_code"
If you lose a few seconds, code
gets invalidated and you'll get invalid_grant
error. If you copy and paste immediately within second, you'll get your response:
{
"access_token": "abcdefg",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "abcdefg",
"id_token": "abcdefghijklmnopqrstu"
}
The next step would be decoding id_token with Apple's public key.
The {"error":"invalid_client"}
message could be related to an invalid signature generated by the openssl_sign function. The ES256 algorithm must be used in order to sign the JWT and the generated signature should be the concatenation of two unsigned integers, denoted as R and S. It turns out that openssl_sign function generates a DER-encoded ASN.1 signature which is not correct for Apple (see here).
So the solution is to convert the DER-encoded ASN.1 signature generated by openSSL into a simple concatenation of the R and S values.
This can be done by using the following function:
/**
* @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;
}
which can be found in this library. More details here Apple Sign In, sign JWT for authentication using PHP and openSSL
I made a little package to generate apple client secret in php, based on jwt-framework: https://github.com/kissdigital-com/apple-sign-in-client-secret-generator
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.
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