Как интегрировать вход в Apple для приложений React Native

Вырежьте свои регистрационные формы с помощью входа в систему с интеграцией Apple

Войти через Apple была выпущена вместе с iOS 13 и предоставляет пользователям простой и конфиденциальный способ аутентификации в своих приложениях и веб-сайтах, поддерживающих сервис. С помощью функции Войти через Apple пользователи могут использовать свой Apple ID в качестве надежного идентификатора для приложений и веб-сайтов, избавляя от необходимости создавать новую учетную запись с данным приложением.

В этой статье рассматривается процесс интеграции входа в систему Apple в контексте React Native и раскрывается код, лежащий в основе этого бесшовного механизма аутентификации. Несколько ключевых пакетов будут использоваться для облегчения интеграции входа в систему с Apple, а именно react-native-apple-authentication для стороны React Native и apple-signin-auth для стороны сервера Node.js.

Прежде чем перейти к коду, необходимому для запуска и работы этой службы в React Native, в следующем разделе подробно рассказывается, чего ожидать от службы «Вход через Apple» как с точки зрения пользователя, так и с точки зрения разработки. Это даст нам достаточно контекста, чтобы продолжить и интегрировать сервис.

Как работает вход в Apple

У пользователей и разработчиков будут разные взгляды на сервис. Пользователь получает быстрый и удобный процесс аутентификации, тогда как нам, разработчикам, предстоит более сложная задача - понять API, настроить необходимые учетные данные, понять рабочий процесс службы и реализовать этот рабочий процесс в виде компонентов React Native и логики на стороне сервера с помощью Node.js. .

Вышеизложенное может показаться трудоемким, но на самом деле четкое понимание процесса поможет в быстрой реализации - именно это и позволит эта статья.

Войдите в систему с Apple с точки зрения пользователя

При правильной реализации пользовательский интерфейс довольно прост и элегантен. Все, что требуется от пользователя, - это нажать кнопку Войти через Apple (которая соответствует рекомендациям Apple по стилю), после чего появится запрос Touch ID / Face ID для аутентификации. После успешной аутентификации пользователь должен войти в приложение без каких-либо дополнительных действий. Когда пользователь выполняет этот процесс впервые, у него есть возможность поделиться или скрыть свое имя и адрес электронной почты для соответствующего приложения.

На следующей диаграмме представлены этапы входа в Apple с помощью приложения, которое я разрабатываю лично. Нажатие кнопки «Войти с помощью Apple» запрашивает аутентификацию с помощью Touch ID. После успешной аутентификации пользователь входит в систему и перенаправляется на свою панель управления:

Чтобы найти золотую середину между предоставлением или скрытием своего адреса электронной почты, функция «Войти через Apple» дает пользователям возможность использовать случайный адрес электронной почты, принадлежащий компании Apple, который создается и связывается с идентификатором Apple ID пользователя. Преимущество этого заключается в том, что пользователи по-прежнему могут получать сообщения из приложения по электронной почте, только электронные письма будут отправляться на этот управляемый адрес Apple, прежде чем они будут перенаправлены на фактический адрес электронной почты пользователя. Эти адреса электронной почты «частной ретрансляции» имеют формат <random_string>@privaterelay.appleid.com.

Мы, как разработчики, несем ответственность за предоставление Apple надежных доменных имен и адресов электронной почты для связи с пользователями с частных адресов ретрансляции электронной почты Apple. Все эти детали можно настроить на портале разработчика. Здесь мы кратко рассмотрим этот процесс, а также дадим ссылки на другие источники, которые описывают этот процесс.

Войдите в систему с Apple с точки зрения разработки

С точки зрения разработчика, процесс аутентификации генерирует некоторые ключевые данные, которые нам необходимо обработать и проверить на стороне сервера. токен удостоверения генерируется со стороны Apple вместе с учетной записью идентификатором, представляющим Apple ID пользователя при аутентификации. Этот идентификатор не является идентификатором Apple ID пользователя (адрес электронной почты, который вы используете для входа в свою учетную запись Apple), а скорее случайной строкой, которая может быть связана с аутентифицируемой учетной записью Apple. Эти данные можно сохранить в базе данных приложения для будущих аутентификаций.

Каждый раз, когда пользователь аутентифицируется, создается другой уникальный идентификатор, но каждый из них будет связан с одним и тем же идентификатором учетной записи.

Поэтому, когда аутентификация происходит в React Native, токен идентификации будет сгенерирован вместе с другими связанными с ним метаданными. В этом пошаговом руководстве мы будем затем отправлять эти данные на сервер, где будет проверяться токен идентификации. Когда токен удостоверения успешно проверен, идентификатор учетной записи возвращается в теле ответа проверки, который затем можно использовать для создания новой учетной записи или получения существующей учетной записи в базе данных вашего приложения.

В качестве дополнительного соображения, скорее всего, ваше приложение уже имеет собственный механизм аутентификации и генерирует собственные токены аутентификации - и это нормально. Фактически, эта статья демонстрирует, как использовать «Вход через Apple» вместе с вашими существующими токенами аутентификации.

После проверки токена идентификации ваш сервер может создать свой стандартный токен аутентификации, который будет использоваться для аутентификации дальнейших запросов API от пользователя.

С этим пониманием мы теперь можем приступить к настройке учетных данных и возможностей Xcode, необходимых для работы входа в систему с Apple.

Настройка Xcode и учетных данных разработчика

Первый порт вызова - настроить необходимые учетные данные и возможности Xcode, чтобы войти в систему с помощью Apple. Основная часть этой настройки происходит на вашем портале разработчика; именно этому в первую очередь посвящен этот раздел. Также в ваш проект Xcode необходимо добавить одну возможность.

В Xcode перейдите на вкладку Подписание и возможности в Xcode, нажмите + Возможность и добавьте возможность Войти с помощью Apple. Сохраните проект Xcode и перейдите на портал разработчика.

Настройка идентификатора приложения, основного и группового идентификаторов

Есть несколько учетных данных, связанных с входом в систему Apple, которые можно настроить на портале разработчика. Начиная с возможности, добавленной из Xcode на предыдущем шаге, теперь вы можете перейти к настройкам конфигурации идентификатора приложения в своих идентификаторах. Прокрутите вниз до пункта Войти через Apple и нажмите Изменить.

Получившееся модальное окно показывает, что ваш идентификатор приложения был назначен в качестве основного идентификатора приложения, и вы можете использовать этот идентификатор приложения для входа в любую сборку вашего приложения, будь то для iOS, Mac или Интернет. Обратной стороной этого является отсутствие различий между этими платформами или отсутствие контекста того, откуда пользователь входит в систему.

Чтобы решить эту проблему, можно также сгруппировать идентификаторы других приложений (или идентификаторы служб в случае веб-приложения) с идентификатором основного приложения. Для этого по-прежнему потребуется только одно согласие пользователя для всей группы, а не согласие на каждый из сгруппированных идентификаторов.

Идентификаторы служб также настраиваются в разделе Идентификаторы и используются, если вы настраиваете вход с помощью Apple для веб-сайта. Приложения просто используют свой идентификатор приложения, чтобы связать себя с функцией «Войти через Apple». Вы можете увидеть в Интернете несколько учебных пособий, в которых вам будет предложено настроить идентификатор службы, но в этом нет необходимости, если вы настраиваете свое приложение исключительно для входа в систему с помощью Apple. Чтобы просмотреть свои идентификаторы служб, щелкните раскрывающееся меню Идентификаторы приложений в правом верхнем углу страницы и выберите Идентификаторы служб.

В контексте входа с помощью Apple идентификатор службы и идентификатор приложения также называют идентификатором клиента. Вы можете увидеть эту терминологию, используемую в документации в Интернете, поэтому просто знайте, что это относится либо к идентификатору приложения, либо к идентификатору службы, который действует как идентификатор домена для входа в систему.

В этой части мы сосредоточимся только на идентификаторе основного приложения, поэтому здесь не потребуется никакой дополнительной настройки. Хотя если читатель хочет расширить свою экосистему до Интернета или другой платформы, он может просто переключить вновь созданный идентификатор на Группировать с существующим основным идентификатором приложения и выбрать основной идентификатор приложения из раскрывающегося списка. предоставлена.

Также обратите внимание, что здесь можно настроить URL-адрес с сервера на серверную конечную точку уведомления, где вы можете получать события стиля уведомлений о входе в систему с помощью Apple. Опять же, это не критично для работы службы, но может быть полезно для анализа использования или проверки того, что служба работает должным образом.

Настроить ключ

Перейдите в список Ключи и добавьте новый ключ для Войти через Apple. Добавьте свой (первичный) идентификатор приложения к ключу и, наконец, зарегистрируйте ключ. Обязательно загрузите закрытый ключ и храните его в надежном месте.

Настроить вход через Apple для связи по электронной почте

Последний требуемый шаг настройки здесь - настройка связи по электронной почте. Это сделано исключительно для обеспечения связи с частной службой ретрансляции электронной почты, упомянутой во введении (или когда пользователь решает скрыть свой реальный адрес электронной почты Apple ID и вместо этого использовать случайно сгенерированный адрес электронной почты, принадлежащий Apple). В идеале мы, как разработчики приложений, по-прежнему хотим связываться с пользователем по электронной почте, особенно для критических обновлений, поэтому этот дополнительный шаг жизненно важен для поддержания этой связи.

В разделе Сертификаты, идентификаторы и профили нажмите Дополнительно в боковом меню и нажмите Настроить, чтобы открыть настройки электронной почты.

Добавьте домены и адреса электронной почты, которые вы планируете использовать. Например, mydomain.com в качестве домена и [email protected] в качестве адреса электронной почты.

Также обратите внимание, что уведомления ретрансляции частной электронной почты включены по умолчанию. Переключите этот параметр с помощью кнопки Настройки.

Эти источники также должны быть аутентифицированы, чтобы подтвердить их право собственности, и самый простой способ сделать это - с помощью аутентификации SPF.

Apple набросает инструкции, как это сделать для ряда поставщиков почтовых услуг (SendGrid, MailChimp и т. д.) - разверните Аутентификация вашего домена по предыдущей ссылке, чтобы увидеть эти подробности.

Я использовал SendGrid в качестве поставщика услуг электронной почты для моего приложения при создании приложения, связанного с этой частью. Чтобы настроить аутентификацию SPF с помощью SendGrid, просто добавьте следующую запись TXT в свой домен:

v=spf1 include:sendgrid.net ~all

Если нажать Повторно подтвердить SPF вскоре после добавления записи SPF, источник будет успешно подтвержден.

Приведенное выше правило позволит SendGrid отправлять электронную почту со всех поддоменов домена, связанного с записью. В Интернете есть множество статей, в которых объясняется, как настраивать правила SPF. Чтобы получить самые свежие источники, выполните поиск по запросу «настройка правил SPF».

Эта настройка, безусловно, нетривиальна и требует понимания того, как идентификаторы и ключи используются вместе. Если читатель обнаружил, что этому разделу трудно следовать, react-native-apple-authentication также включает инструкции со снимками экрана в свою docs/ папку, которую можно просмотреть здесь.

Теперь мы полностью настроены и готовы к реализации React Native.

Вход из React Native

В этом разделе основное внимание будет уделено реализации кнопки Войти с помощью Apple и ее onPress обработчика, который будет генерировать токен удостоверения вместе с его метаданными. Суть полного компонента React, о котором здесь говорилось, содержится в конце этого раздела.

Для этого используется пакет @invertase/react-native-apple-authentication. Добавьте его как зависимость проекта, чтобы получить доступ к его API:

yarn add @invertase/react-native-apple-authentication

Отображение входа с помощью кнопки Apple

Сам дисплей кнопок прямой; он упакован в AppleButton компонент и поддерживает свойства для определения его типа и стиля. Предоставляемые типы кнопок изменяют текст кнопки, в то время как стиль темы кнопки, поддерживая светлые или темные темы, а также тему контура.

Определите кнопку «Войти» следующим образом - мы также импортировали другие элементы react-native-apple-authentication, поскольку мы будем использовать их в функции обработчика onPress кнопки:

// displaying the Sign in with Apple button component
import appleAuth, {
  AppleButton,
  AppleAuthError,
  AppleAuthRequestScope,
  AppleAuthRequestOperation,
} from '@invertase/react-native-apple-authentication'
const AppleAuth = (props) => {
  const onAppleButtonPress = async () => {
     /* Sign in logic will go here */
  }
 
  return (
    <AppleButton
      buttonStyle={AppleButton.Style.WHITE}
      buttonType={AppleButton.Type.SIGN_IN}
      style={styles.appleButton}
      onPress={() => onAppleButtonPress()}
    />
  );
}

Если вы ориентируетесь на несколько платформ, используйте логическое значение appleAuth.isSupported, чтобы определить, отображать ли кнопку входа в систему. Это гарантирует, что он не будет отображаться, например, на Android.

Обратите внимание, что width и height кнопки определены в параметре style. Кнопка также будет реагировать на поля и даже поддерживать тень:

// button styling
appleButton: {
  width: '100%',
  height: 45,
  shadowColor: '#555',
  shadowOpacity: 0.5,
  shadowOffset: {
    width: 0,
    height: 3
  },
  marginVertical: 15,
}

Рекомендуется поэкспериментировать со стилем кнопки, чтобы она максимально соответствовала существующей теме. Размер шрифта, радиус границы и цвета нельзя изменить за пределами buttonStyle - это, скорее всего, связано с рекомендациями Apple по стилю. При необходимости настройте существующую тематику в соответствии с кнопкой «Войти», чтобы компенсировать ее отсутствие настраиваемости.

Кнопка onPress логика и запрос на вход

Теперь давайте обратим наше внимание на функцию-обработчик onPress, которую мы назвали onAppleButtonPress. Во-первых, вся логика должна быть заключена в блок try catch, где могут обрабатываться AppleAuthError исключений. Здесь должна быть определена логика, относящаяся к отмене входа пользователем:

// try catch block to handle sign in exceptions
const onAppleButtonPress = async () => {
  try {

  } catch (error) {
    if (error.code === AppleAuthError.CANCELED) {
      // user cancelled Apple Sign-in
  
    } else {
      // other unknown error
    }
  }
}

Теперь мы можем сделать запрос на вход в блоке try. Этот запрос на вход вызовет приглашение на вход, встроенное в iOS, где пользователь может затем аутентифицироваться с помощью такого метода, как Touch ID или Face ID. В случае отмены этого запроса будет вызвано исключение, которое мы определили выше. С другой стороны, успешная аутентификация вернет identityToken, среди других деталей, таких как идентификатор учетной записи с именем appleUserId, nonce, связанный с identityToken, и другие детали.

Следующий пример запроса включает две области для адреса электронной почты и имени пользователя соответственно, что означает, что эти учетные данные будут запрошены, если пользователь входит в систему в первый раз. Напомним, что нет гарантии, что пользователь предоставит эти данные, и на этом этапе он также может выбрать частную ретрансляцию электронной почты. Этот этап также называется этапом согласия.

В любом случае сделайте асинхронный запрос так:

// sign in request 
const appleAuthRequestResponse = await appleAuth.performRequest({
  requestedOperation: AppleAuthRequestOperation.LOGIN,
  requestedScopes: [
    AppleAuthRequestScope.EMAIL,
    AppleAuthRequestScope.FULL_NAME
  ],
});
const {
  identityToken,
} = appleAuthRequestResponse;

Теперь у нас есть все необходимое для продолжения процесса проверки и входа на стороне сервера.

Хотя в appleAuthRequestResponse возвращаются другие метаданные, такие как согласованные пользователем области действия, в этом пошаговом руководстве нас будет интересовать только identityToken для продолжения аутентификации. Как мы увидим, проверка токена на стороне сервера также вернет значения ключей, такие как appleUserId, которые позволят связать identityToken с учетной записью.

Если ваше приложение не имеет серверного API и полностью полагается исключительно на Вход с помощью Apple, этого ответа будет достаточно, чтобы подтвердить успешную аутентификацию пользователя. Однако это определенно крайний случай - почти все службы будут иметь данные учетной записи, сохраненные на стороне сервера, и свои собственные механизмы аутентификации. Чтобы удовлетворить большинство случаев использования, мы отправим identityToken (безопасно) серверную часть, где ее затем можно будет проверить и интегрировать в существующий поток аутентификации.

На этом этапе вы можете использовать другие возвращаемые значения, особенно поля email и fullName, чтобы обновить пользовательский интерфейс и состояние компонентов или сохранить эти значения на устройстве. В этой статье мы продолжим фокусироваться на процессе аутентификации, но признаем, что на этом этапе может быть произведена дальнейшая обработка.

Отправка identityToken на стороне сервера

Последнее, что нужно сделать onAppleButtonPress, - это отправить этот запрос на ваш сервер и обработать либо успешный, либо неудачный проверочный ответ.

Следующий код отправляет на сервер весь appleAuthRequestResponse, но нас в первую очередь будет интересовать использование значений identityToken и appleUserId для проверки в следующем разделе. После проверки ваш сервер должен ответить сообщением об успехе или неудаче:

// request to verify identity token
if (identityToken) {
  // sending entire auth response to server in `fetch` request
  const { ack, response } = await authFetch({
    uri: '/sign-in-with-apple',
    body: {
      ...appleAuthRequestResponse,
    }
  });
  
  if (ack === 'success') {
    // successful request, process sign in
    // state updates / Redux dispatches / etc
    handleSignIn(response);
 } else {
    // failed to verify `identityToken`
    handleFailedSignIn();     
  }
} else {
  // no `identityToken`, also a failed sign in
  handleFailedSignIn();
}

authFetch - это функция, которую я использую в разработке, которая объединяет обычный запрос fetch с асинхронной функцией, обеспечивая упрощенный шаблон.

Вот Суть, которая объединяет весь этот раздел в компоненте React:

В следующем разделе будет описано, как использовать другой пакет в среде Node.js Express для проверки identityToken и как связать его с идентификатором учетной записи appleUserId с учетной записью пользователя.

Проверка на стороне сервера

Теперь давайте проверим identityToken. Здесь используется другой пакет, apple-signin-auth. Идите и добавьте это в свой проект:

yarn add apple-signin-auth

Настройка конечной точки и проверка identityToken

Назовем конечную точку /sign-in-with-apple. Сначала мы заключим его логику в блок try catch и будем ссылаться на переменные, отправленные из приложения:

// sign-in-with-apple endpoint
const appleSignin = require('apple-signin-auth');
router.post('/sign-in-with-apple', async function (req, res, next) {
  try {
    const { body } = req;
    const {
      email,
      fullName,
      identityToken
    } = body;
  } catch (e) {
    next(e);
  }
});

Метод appleSignin.verifyIdToken() будет использоваться для проверки identityToken. В следующем примере будет предпринята попытка проверки:

// verifying the provided identityToken
try {
 const clientId = <your_app_id>;
  // verify token (will throw error if failure)
  const { sub: userAppleId } = await   
    appleSignin.verifyIdToken(identityToken, {
      audience: clientId,
      ignoreExpiration: true, // ignore token expiry (never expires)
  });
} catch (e) {
  res.json({
    ack: 'error', 
    message: 'failed to verify identityToken'
  });
  next(e);
}

Теперь мы к чему-то приближаемся. Метод verifyIdToken() в случае успеха вернет appleUserId, связанный с этим identityToken. Вот особенности этого метода:

  • Мы предоставили значение clientId для свойства audience. Этот clientId - идентификатор приложения, связанный с ранее настроенным входом в систему Apple. Имя clientId используется здесь, чтобы соответствовать официальной документации apple-signin-auth.
  • Для свойства ignoreExpiration установлено значение true, что означает, что этот токен будет проверяться, даже если срок его действия истек. Это полезно при реализации входа с помощью Apple в сочетании с вашими собственными механизмами аутентификации, благодаря чему вы не подчиняетесь механизмам, внешним по отношению к процессу аутентификации.
  • Мы определили здесь дополнительный блок try catch, чтобы изолировать ошибки проверки и отправить ответ об ошибке обратно в приложение, если оно не будет успешно проверено.
  • В качестве примечания: в официальной документации также указано свойство nonce. Одноразовый номер также был сгенерирован на стороне React Native и отправлен на сервер также как nonce. Однако при предоставлении этого значения nonce для verifyIdToken() токен не проходит успешную проверку. Может показаться интуитивно понятным предоставить это значение nonce, но, похоже, это не поддерживается при проверке уже созданного identityToken. Помните об этом, чтобы не запутаться в этой двусмысленности.

Обработка успешной проверки

Если в результате успешной проверки возвращается appleUserId, можно выполнить одну последнюю проверку относительно идентификатора appleUserId, отправленного на сервер, который должен совпадать с сгенерированным:

// checking apple User IDs match
if(appleUserId === req.body.appleUserId) {
   //details match, continue with sign in process
}

Обратите внимание, что appleUserId будет одинаковым во всех последующих входах, а identityToken - нет.

Теперь вы можете аутентифицировать пользователя, а также создать новую учетную запись, если это первый раз, когда пользователь проходит аутентификацию. Следующие данные теперь доступны для сохранения во вновь созданной учетной записи:

  • Поле fullName, в котором указывается полное имя и псевдоним пользователя, если он указан.
  • Поле email, которое может быть null, личным адресом ретранслятора или настоящим адресом электронной почты Apple ID пользователя, в зависимости от того, как они решили поделиться им.
  • Поле appleUserId, которое может выступать в качестве идентификатора для конкретного пользователя Apple. Это можно рассматривать как имя пользователя, и его следует использовать как идентификатор учетной записи более email, который действительно может быть null.

В заключение этого раздела вы, возможно, захотите придерживаться следующей логической схемы, чтобы завершить реализацию вашей конечной точки:

// logic flow for verification and app sign in 
endpoint receives request
  |_verify identityToken
    |_ return failure if not successful
  |_ check appleUserId's match
    |_ return failure if they do not match
    |_ check account with appleUserId exists
      |_ create account if account does not exist
    |_ generate authentication token via standard means
    |_ persist token to user account record
    |_ return token and account details to app

Если вы хотите узнать, как кодировать свою внутреннюю систему аутентификации для поддержки стандартного адреса электронной почты и пароля с обработкой токенов аутентификации, ознакомьтесь с моей статьей, посвященной этому: React Native: аутентификация пользователя Объяснение потока .

Ответ на аннулирование доступа

Также стоит кратко коснуться прослушивателя событий, который react-native-apple-authentication предоставляет для ответа на отмену доступа пользователя к вашему приложению. Следующий хук useEffect инициализирует и размонтирует этот прослушиватель событий, позволяя вам реагировать на такой сценарий такими действиями, как выход пользователя из его учетной записи:

// responding to a user revoking access
let authCredentialListener = null;
useEffect(() => {
  authCredentialListener = appleAuth.onCredentialRevoked(async () => {
    //user credentials have been revoked. Sign out of account
  });
  return (() => {
   if (authCredentialListener.remove !== undefined) {
      authCredentialListener.remove();
    }
  })
}, []);

В сводке

В этой статье сделана попытка обрисовать процесс интеграции Sign in с Apple в контексте React Native. Хотя этот процесс влечет за собой несколько громоздкий процесс настройки всех учетных данных на портале разработчика, интеграция React Native и Node относительно проста благодаря простым API, которые предоставляют react-native-apple-authentication и apple-signin-auth.

Конечным результатом этой интеграции является значительно более упрощенный процесс регистрации и входа для ваших конечных пользователей, что, вероятно, увеличит количество подписок для приложений, требующих процесса регистрации для доступа к контенту. Регистрационная форма больше не нужна пользователям, которые решили использовать «Войти через Apple», что значительно снижает барьер для содержимого вашего приложения.

Обратной стороной этого подхода является отсутствие информации о пользователях, которую вы можете получить в этом начальном процессе регистрации. Это ограничение можно снять, запросив у пользователя дополнительную информацию, когда она понадобится приложению. Этот подход, безусловно, набрал обороты с момента появления функции «Вход с помощью Apple», что говорит о том, что пользователи в целом устали от регистрации и ценят менее подробные процессы аутентификации.

Если у вас возникнут вопросы и проблемы, пожалуйста, оставьте сообщение в разделе комментариев, и я обязательно отвечу.