Skip to content

Instantly share code, notes, and snippets.

@mingalevme
Last active September 15, 2022 10:01
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 mingalevme/c6fca8df82b40e5b60a009dc4b754e67 to your computer and use it in GitHub Desktop.
Save mingalevme/c6fca8df82b40e5b60a009dc4b754e67 to your computer and use it in GitHub Desktop.
Example of Generating Apple SignIn Access Token (based on Laravel/Lumen Command)
<?php
declare(strict_types=1);
namespace App\Console\Commands\Apple;
use App\Console\Command;
use App\Helpers\Jwt;
use Carbon\Carbon;
use GuzzleHttp\ClientInterface as GuzzleClient;
use GuzzleHttp\Exception\GuzzleException;
class GenerateAccessToken extends Command
{
protected $signature = 'mfeed:apple:sing-in:token {--kid= : KID} {--iss=} {--aud=} {--sub=} {--iat=} {--exp=} {--private-key=} {--code=} {--redirect-uri=}';
/**
* @throws GuzzleException
*/
public function handle(GuzzleClient $guzzle): int
{
$kid = $this->requireOption('kid'); // SignIn Key ID
$iss = $this->requireOption('iss'); // Developer ID
$aud = $this->getOption('aud') ?: 'https://appleid.apple.com';
$sub = $this->requireOption('sub'); // mobile app id
$iat = intval($this->getOption('iat') ?: Carbon::now()->getTimestamp());
$exp = intval($this->getOption('exp') ?: 86400 * 30); // access token expiration timeout
$privateKey = $this->requireOption('private-key'); // SignIn Key: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
$code = $this->requireOption('code'); // auth code from client
$redirectUri = $this->requireOption('redirect-uri');
$clientSecret = (string)Jwt::issue($privateKey, $kid, $iss, $aud, $sub, $iat, $exp);
$response = $guzzle->request('POST', 'https://appleid.apple.com/auth/token', [
'form_params' => [
'client_id' => $sub,
'code' => $code,
'client_secret' => $clientSecret,
'grant_type' => 'authorization_code',
'redirect_uri' => $redirectUri,
],
]);
$this->line($response->getBody()->getContents());
return 0;
}
private function requireOption(string $key): string
{
/** @var string|string[]|null $value */
$value = $this->option($key);
if (!$value) {
$this->error("The \"--$key\" option does not exist.");
exit(1);
}
if (!is_string($value)) {
$this->error("The \"--$key\" option is a single value option.");
exit(1);
}
return $value;
}
private function getOption(string $key): ?string
{
/** @var string|string[]|null $value */
$value = $this->option($key);
if (!$value) {
return null;
}
if (!is_string($value)) {
$this->error("The \"--$key\" option is a single value option.");
exit(1);
}
return $value;
}
}
<?php
// composer require lcobucci/jwt
declare(strict_types=1);
namespace App\Helpers;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Ecdsa\Sha256;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Token;
class Jwt
{
public static function issue(string $privateKey, string $kid, string $iss, string $aud, string $sub, int $iat, int $exp): Token
{
$signer = new Sha256();
return (new Builder())
->issuedBy($iss)
->permittedFor($aud)
->relatedTo($sub)
->issuedAt($iat)
->expiresAt($exp)
->withHeader('kid', $kid)
->withHeader('alg', 'ES256')
->withHeader('type', 'JWT')
->getToken($signer, new Key($privateKey));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment