Как прикрутить вход через Apple в React Native и не сойти с ума
Репозиторий давно не обновлялся
Последнее обновление было 8 месяцев назад.
Если вы когда-нибудь выкатывали мобильное приложение с авторизацией через Google или соцсети в App Store, вы наверняка сталкивались с жестким правилом ревьюеров: добавил сторонний вход — изволь поставить Sign In with Apple. Иначе реджект прилетит быстрее, чем соберется релизный билд.
Казалось бы, задача тривиальная. Но когда начинаешь разбираться с нативными вызовами, спецификой работы на симуляторах и причудами яблочного API, вылезает масса подводных камней. Ребята из команды Invertase (те самые, кто делает React Native Firebase) написали библиотеку react-native-apple-authentication, которая закрывает этот вопрос как для iOS, так и для Android.

Что под капотом и зачем это нужно
Библиотека дает полный доступ к яблочному механизму аутентификации. Работает на чистом React Native от версии 0.60, дружит с Expo (через prebuild и конфиг entitlements в app.json), поддерживает macOS и даже умеет авторизовывать пользователей на Android через браузерный flow.
Самое приятное тут — типизация из коробки и готовый нативный компонент кнопки AppleButton. Дизайн кнопки строго регламентирован гайдлайнами Apple. Если нарисовать ее отсебятиной, ревьюеры с удовольствием завернут приложение. Здесь же стили и локализация подтягиваются прямо из нативной системы.
Как устроен базовый сценарий на iOS
Установка стандартная: ставим пакет через yarn или npm, в папке ios прогоняем pod install. Модуль поддерживает автолинкинг, поэтому копаться в AppDelegate не придется.
Для кнопки и логики входа хватает буквально пары импортов:
import React from 'react';
import { View } from 'react-native';
import { AppleButton, appleAuth } from '@invertase/react-native-apple-authentication';
export function LoginScreen() {
async function handleAppleLogin() {
// Запускаем нативный запрос авторизации
const authResponse = await appleAuth.performRequest({
requestedOperation: appleAuth.Operation.LOGIN,
// Порядок скоупов имеет значение
requestedScopes: [appleAuth.Scope.FULL_NAME, appleAuth.Scope.EMAIL],
});
// Проверяем статус пользователя
const credentialState = await appleAuth.getCredentialStateForUser(authResponse.user);
if (credentialState === appleAuth.State.AUTHORIZED) {
// Пользователь подтвержден, отправляем токены на бэкенд
console.log('User ID:', authResponse.user);
console.log('Identity Token:', authResponse.identityToken);
}
}
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<AppleButton
buttonStyle={AppleButton.Style.BLACK}
buttonType={AppleButton.Type.SIGN_IN}
style={{ width: 200, height: 45 }}
onPress={handleAppleLogin}
/>
</View>
);
}
Грабли, на которые наступают все новички
У яблочной авторизации есть неочевидные особенности, о которых полезно узнать до того, как начнешь отлаживать код до трех ночи.
Имя и почта приходят только один раз
Apple помешана на приватности. При первом входе пользователь может выбрать, скрыть реальную почту или показать. Данные профиля (fullName и email) нативный фреймворк отдаст ровно один раз — во время самой первой успешной авторизации.
При повторных вызовах эти поля придут пустыми (null). Если вы не сохранили имя и email в свою базу сразу, получить их заново через клиентский API не выйдет.
Чтобы сбросить тестовое состояние на айфоне и снова получить профиль, приходится лезть руками в системные настройки: Настройки > Apple ID > Пароль и безопасность > Приложения, использующие Apple ID, удалять там свое тестовое приложение и логиниться заново.
Кстати, в репозитории проекта отдельно подмечен баг самого яблочного API: скоупы нужно передавать именно в порядке [FULL_NAME, EMAIL]. Если поменять местами, имя может потеряться уже на первом запросе.
Симуляторы и ошибка 1000
Метод getCredentialStateForUser проверяет подлинность девайса. На симуляторе iOS он регулярно падает с ошибкой com.apple.AuthenticationServices.AuthorizationError error 1000. Полноценно протестировать цепочку авторизации получится только на физическом устройстве.
Если симулятор намертво заклинило на этой ошибке, разработчики советуют зайти на сайт управления Apple ID, найти там список привязанных устройств и выкинуть симулятор из аккаунта.


Логаут — это фикция
В перечислениях модуля есть операция appleAuth.Operation.LOGOUT, но на практике в нативном iOS SDK она ничего не делает. Apple не предоставляет метода принудительного разлогина из системы на уровне устройства. Логаут на клиенте сводится к тому, что вы просто стираете локальную сессию, сохраненные токены и очищаете стейт приложения.
Отзыв доступа
Пользователь может в любой момент зайти в настройки iOS и отозвать разрешение на вход для вашей программы. Чтобы вовремя среагировать и выкинуть юзера на экран логина, в библиотеке есть слушатель событий:
useEffect(() => {
const unsubscribe = appleAuth.onCredentialRevoked(async () => {
// Токены больше не валидны, сбрасываем локальное состояние
console.warn('Доступ отозван пользователем в настройках Apple ID');
});
return () => unsubscribe();
}, []);
Что делать с Android
Многие удивляются, но пускать через Apple ID можно и пользователей Android. Механика там другая: вместо нативного фреймворка открывается защищенное окно браузера через OAuth 2.0 Web Flow.
Библиотека берет рутину на себя через модуль appleAuthAndroid:
import { appleAuthAndroid } from '@invertase/react-native-apple-authentication';
import 'react-native-get-random-values';
import { v4 as uuid } from 'uuid';
async function handleAndroidAppleLogin() {
const rawNonce = uuid();
const state = uuid();
appleAuthAndroid.configure({
clientId: 'com.example.client-android', // Service ID из консоли Apple Developer
redirectUri: 'https://example.com/auth/callback',
responseType: appleAuthAndroid.ResponseType.ALL,
scope: appleAuthAndroid.Scope.ALL,
nonce: rawNonce,
state,
});
const response = await appleAuthAndroid.signIn();
// Отправляем response.code и response.id_token на сервер
}
Для Android потребуется настроить Service ID в Apple Developer Console и указать Redirect URL. Главное правило: ссылка перенаправления должна символ в символ совпадать с тем, что вбито в консоли разработчика, никаких query-параметров.
Серверная валидация
Никогда не доверяйте клиентскому user ID на слово. Клиент получает identityToken (это подписанный JWT). На сервере вы декодируете его, проверяете подпись публичными ключами Apple и сверяете nonce.
Модуль автоматически считает SHA256-хэш от переданного nonce перед отправкой в Apple (по аналогии с тем, как это делает Firebase Auth). Учитывайте этот момент на бэкенде: сверять нужно хэшированное значение.
Если перед вами стоит задача быстро закрыть требования App Store Review или дать пользователям удобный вход в один тап через Face ID, библиотека от Invertase — самый надежный выбор в экосистеме React Native. Она избавит от написания собственного нативного моста на Swift/Objective-C, аккуратно решает вопрос со стилями кнопок и стабильно обновляется авторами. Поставил, настроил сертификаты в Apple Developer Console, прописал обработчик — и релиз готов к отправке.
