266 const AUTH_URL =
'https://appleid.apple.com/auth/authorize';
267 const TOKEN_URL =
'https://appleid.apple.com/auth/token';
268 private const PUBLIC_KEYS_URL =
'https://appleid.apple.com/auth/keys';
270 private const CLIENT_SECRET_EXPIRATION_TIME = 3600;
271 private const DECODE_ALGORITHM =
'RS256';
272 private const BITRIX_APP_BUNDLE_ID =
'com.bitrixsoft.cpmobile';
302 return \CHTTP::URN2URI(
'/bitrix/tools/oauth/apple.php');
305 public function GetAuthUrl($redirect_uri, $state =
''): string
307 return self::AUTH_URL .
308 '?client_id=' . $this->appID .
309 '&redirect_uri=' . urlencode($redirect_uri) .
310 '&response_type=' .
'code' .
312 '&response_mode=' .
'form_post' .
313 ($state <>
'' ?
'&state=' . urlencode($state) :
'');
324 if (is_array($token))
326 $this->access_token = $token[
'OATOKEN'];
327 $this->accessTokenExpires = $token[
'OATOKEN_EXPIRES'];
336 if (isset($token[
'REFRESH_TOKEN']) && $this->
getNewAccessToken($token[
'REFRESH_TOKEN'], $this->userId,
true))
345 if ($this->code ===
false)
351 $request = \Bitrix\Main\Context::getCurrent()->getRequest();
354 $requestData[
'service'] === self::SERVICE_ID
356 $requestData[
'platform'] ===
'ios'
357 || (empty($requestData[
'platform']) && mb_strpos(
$request->getUserAgent(),
'Darwin') !==
false)
361 $this->appID = self::BITRIX_APP_BUNDLE_ID;
366 'grant_type' =>
'authorization_code',
367 'client_secret' => $this->getClientSecret(),
369 'redirect_uri' => $redirect_uri,
373 'socketTimeout' => $this->httpTimeout,
374 'streamTimeout' => $this->httpTimeout,
382 catch (\
Bitrix\Main\ArgumentException $e)
387 if ((isset(
$result[
'access_token']) &&
$result[
'access_token'] <>
''))
389 $this->access_token =
$result[
'access_token'];
390 $this->accessTokenExpires = time() +
$result[
'expires_in'];
391 $this->refresh_token =
$result[
'refresh_token'];
392 $this->idToken =
$result[
'id_token'];
394 $_SESSION[
"OAUTH_DATA"] = [
408 if (!$this->appID || !$this->appSecret)
419 'socketTimeout' => $this->httpTimeout,
420 'streamTimeout' => $this->httpTimeout,
424 'refresh_token' => $refreshToken,
425 'client_id' => $this->appID,
426 'client_secret' => $this->getClientSecret(),
427 'grant_type' =>
'authorization_code',
434 catch (\
Bitrix\Main\ArgumentException $e)
441 $this->access_token =
$arResult[
'access_token'];
442 $this->accessTokenExpires =
$arResult[
'expires_in'] + time();
445 $dbSocservUser = \Bitrix\Socialservices\UserTable::getList(
array(
447 '=EXTERNAL_AUTH_ID' => static::SERVICE_ID,
450 'select' =>
array(
'ID')
452 if ($arOauth = $dbSocservUser->fetch())
454 \Bitrix\Socialservices\UserTable::update($arOauth[
'ID'],
array(
455 'OATOKEN' => $this->access_token,
456 'OATOKEN_EXPIRES' => $this->accessTokenExpires)
469 if ($this->access_token ===
false || $this->idToken ===
false)
476 $user = $this->decodeIdentityToken($this->idToken);
478 catch (Exception $exception)
483 $user = (
array)$user;
485 if (!empty($user[
'sub']) && isset(
$_REQUEST[
'user']))
487 $userData = json_decode(
$_REQUEST[
'user'],
true);
489 if (!empty($userData))
491 $user[
'first_name'] = $userData[
'name'][
'firstName'];
492 $user[
'last_name'] = $userData[
'name'][
'lastName'];
502 'id' => self::BITRIX_APP_BUNDLE_ID
510 return implode(
' ', array_map(
'urlencode', array_unique($this->
getScope())));
513 private function getClientSecret(): string
515 return $this->generateSignedJWT($this->keyId, $this->teamId, $this->appID, $this->secretKey);
518 private function generateSignedJWT(
string $keyId,
string $teamId,
string $clientId,
string $secretKey)
527 'exp' => time() + self::CLIENT_SECRET_EXPIRATION_TIME,
528 'aud' =>
'https://appleid.apple.com',
532 $privateKey = openssl_pkey_get_private($secretKey);
538 $payload = JWT::urlsafeB64Encode(json_encode($header)) .
'.' . JWT::urlsafeB64Encode(json_encode($body));
540 $signResult = openssl_sign($payload, $signature, $privateKey, OPENSSL_ALGO_SHA256);
546 $rawSignature = self::convertDERSignature($signature, 64);
548 return $payload .
'.' . JWT::urlsafeB64Encode($rawSignature);
551 private static function convertDERSignature(
string $der,
int $partLength): string
553 $hex = unpack(
'H*', $der)[1];
554 if (
'30' !== substr($hex, 0, 2))
556 throw new \RuntimeException();
558 if (
'81' === substr($hex, 2, 2))
560 $hex = substr($hex, 6);
564 $hex = substr($hex, 4);
566 if (
'02' !== substr($hex, 0, 2))
568 throw new \RuntimeException();
570 $Rl = hexdec(substr($hex, 2, 2));
571 $R = self::retrievePositiveInteger(substr($hex, 4, $Rl * 2));
572 $R = str_pad($R, $partLength,
'0', STR_PAD_LEFT);
573 $hex = substr($hex, 4 + $Rl * 2);
574 if (
'02' !== substr($hex, 0, 2))
576 throw new \RuntimeException();
578 $Sl = hexdec(substr($hex, 2, 2));
579 $S = self::retrievePositiveInteger(substr($hex, 4, $Sl * 2));
580 $S = str_pad($S, $partLength,
'0', STR_PAD_LEFT);
582 return pack(
'H*', $R . $S);
590 private static function retrievePositiveInteger(
string $data): string
592 while (
'00' === substr(
$data, 0, 2) && substr(
$data, 2, 2) >
'7f')
599 private function fetchPublicKey()
601 $publicKeyDetails = [];
603 $http =
new HttpClient([
604 'socketTimeout' => $this->httpTimeout,
605 'streamTimeout' => $this->httpTimeout,
607 $publicKeys = $http->get(self::PUBLIC_KEYS_URL);
611 $decodedPublicKeys = json_decode($publicKeys,
true);
618 if (!isset($decodedPublicKeys[
'keys']) ||
count($decodedPublicKeys[
'keys']) < 1)
623 $parsedPublicKeys = JWK::parseKeySet($decodedPublicKeys[
'keys']);
625 foreach ($parsedPublicKeys as $keyId => $publicKey)
627 $details = openssl_pkey_get_details($publicKey);
628 $publicKeyDetails[$keyId] = $details[
'key'];
631 return $publicKeyDetails;
634 private function decodeIdentityToken(
string $identityToken)
638 $publicKeys = $this->fetchPublicKey();
639 if (is_array($publicKeys))
641 $payload = JWT::decode($identityToken, $publicKeys, [self::DECODE_ALGORITHM]);
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)