Skip to content

Instantly share code, notes, and snippets.

@coreymcmahon
Created March 17, 2021 04:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save coreymcmahon/9d0872639cdf2c7845f6c2a8b1d505b5 to your computer and use it in GitHub Desktop.
Save coreymcmahon/9d0872639cdf2c7845f6c2a8b1d505b5 to your computer and use it in GitHub Desktop.
Generate a client_secret for "Sign in with Apple"
<?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