Как на PHP реализовать авторизацию через Sign In with Apple с проверкой id_token на сервере?
Авторизация через Sign In with Apple на PHP требует нескольких шагов: получения id_token от клиента, его верификации на сервере и идентификации пользователя. Разберём процесс подробно.
**1. Как работает Sign In with Apple**
После успешной авторизации на клиенте (iOS, Web) Apple возвращает id_token — JWT-токен, подписанный приватным ключом Apple. Задача сервера — проверить подпись этого токена и извлечь данные пользователя.
**2. Установка зависимостей**
Для работы с JWT на PHP используйте библиотеку firebase/php-jwt:
composer require firebase/php-jwt
**3. Получение публичных ключей Apple**
Apple публикует публичные ключи по адресу:
https://appleid.apple.com/auth/keys
Запросите их через HTTP и закешируйте на короткое время:
php
function getApplePublicKeys(): array {
$response = file_get_contents(‘https://appleid.apple.com/auth/keys’);
return json_decode($response, true)[‘keys’];
}
**4. Декодирование заголовка токена**
Чтобы выбрать нужный ключ по kid (Key ID), декодируйте заголовок JWT без проверки подписи:
php
function getTokenHeader(string $token): array {
$parts = explode(‘.’, $token);
return json_decode(base64_decode(str_pad(strtr($parts[0], ‘-_’, ‘+/’), strlen($parts[0]) % 4, ‘=’, STR_PAD_RIGHT)), true);
}
**5. Конвертация JWK в PEM**
Apple возвращает ключи в формате JWK. Их нужно преобразовать в PEM для проверки подписи:
php
use FirebaseJWTJWK;
$keys = getApplePublicKeys();
$jwks = [‘keys’ => $keys];
$parsedKeys = JWK::parseKeySet($jwks);
Библиотека firebase/php-jwt поддерживает это из коробки начиная с версии 6.x.
**6. Верификация id_token**
php
use FirebaseJWTJWT;
use FirebaseJWTJWK;
function verifyAppleToken(string $idToken, string $clientId): object {
$keysData = json_decode(file_get_contents(‘https://appleid.apple.com/auth/keys’), true);
$publicKeys = JWK::parseKeySet($keysData);
$decoded = JWT::decode($idToken, $publicKeys);
// Проверка издателя и аудитории
if ($decoded->iss !== ‘https://appleid.apple.com’) {
throw new Exception(‘Invalid issuer’);
}
if ($decoded->aud !== $clientId) {
throw new Exception(‘Invalid audience’);
}
if ($decoded->exp < time()) {
throw new Exception('Token expired');
}
return $decoded;
}
**7. Извлечение данных пользователя**
После успешной верификации в объекте $decoded доступны:
— `sub` — уникальный идентификатор пользователя Apple (используйте как user_id)
— `email` — email (может отсутствовать при повторных входах)
— `email_verified` — подтверждён ли email
**8. Важные нюансы**
— Email Apple возвращает только при первом входе пользователя. При повторных — только sub.
— Кешируйте публичные ключи Apple (например, на 1 час), чтобы не делать лишние запросы.
— clientId для веб-приложений — это Services ID из Apple Developer Console, для iOS — Bundle ID.
— Используйте HTTPS и защищайте endpoint от перебора.
Такой подход обеспечивает безопасную серверную верификацию без обращения к дополнительным API Apple.
