Created
March 17, 2021 04:28
-
-
Save coreymcmahon/9d0872639cdf2c7845f6c2a8b1d505b5 to your computer and use it in GitHub Desktop.
Generate a client_secret for "Sign in with Apple"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
use Lcobucci\JWT\Signer\Ecdsa\Sha256; | |
use Lcobucci\JWT\Signer\Key\InMemory; | |
use Lcobucci\JWT\Encoding\UnixTimestampDates; | |
use Lcobucci\JWT\Decoder; | |
use Lcobucci\JWT\Encoder; | |
use Lcobucci\JWT\Encoding\CannotDecodeContent; | |
use Lcobucci\JWT\Encoding\CannotEncodeContent; | |
use Lcobucci\JWT\SodiumBase64Polyfill; | |
use JsonException; | |
class SignInWithApple | |
{ | |
public function createClientSecret(string $id, string $team, string $keyId, string $privateKey): string | |
{ | |
$configuration = Configuration::forSymmetricSigner( | |
Sha256::create(), | |
InMemory::plainText($privateKey), | |
new CustomEncoder() | |
); | |
$now = new \DateTimeImmutable(); | |
$token = $configuration->builder() | |
// Configures the issuer (iss claim) | |
->issuedBy($team) | |
// Configures the audience (aud claim) | |
->permittedFor('https://appleid.apple.com') | |
// Configures the time that the token was issue (iat claim) | |
->issuedAt($now) | |
// Configures the expiration time of the token (exp claim) | |
->expiresAt($now->modify('+1 months')) | |
// Configures the subject (sub claim) | |
->relatedTo($id) | |
// Configures the key ID header | |
->withHeader('kid', $keyId) | |
// Builds a new token | |
->getToken($configuration->signer(), $configuration->signingKey()); | |
return $token->toString(); | |
} | |
} | |
/** | |
* We need to override the JoseEncoder because Apple expects unix timestamps (ints instead of floats) | |
*/ | |
class CustomEncoder implements Encoder, Decoder | |
{ | |
private const JSON_DEFAULT_DEPTH = 512; | |
/** @inheritdoc */ | |
public function jsonEncode($data): string | |
{ | |
if (Arr::get($data, 'iat')) { | |
$data['iat'] = (int) $data['iat']; | |
} | |
if (Arr::get($data, 'exp')) { | |
$data['exp'] = (int) $data['exp']; | |
} | |
try { | |
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); | |
} catch (JsonException $exception) { | |
throw CannotEncodeContent::jsonIssues($exception); | |
} | |
} | |
/** @inheritdoc */ | |
public function jsonDecode(string $json) | |
{ | |
try { | |
return json_decode($json, true, self::JSON_DEFAULT_DEPTH, JSON_THROW_ON_ERROR); | |
} catch (JsonException $exception) { | |
throw CannotDecodeContent::jsonIssues($exception); | |
} | |
} | |
public function base64UrlEncode(string $data): string | |
{ | |
return SodiumBase64Polyfill::bin2base64( | |
$data, | |
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING | |
); | |
} | |
public function base64UrlDecode(string $data): string | |
{ | |
return SodiumBase64Polyfill::base642bin( | |
$data, | |
SodiumBase64Polyfill::SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment