Last active
September 15, 2022 10:01
-
-
Save mingalevme/c6fca8df82b40e5b60a009dc4b754e67 to your computer and use it in GitHub Desktop.
Example of Generating Apple SignIn Access Token (based on Laravel/Lumen Command)
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); | |
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; | |
} | |
} |
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 | |
// 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