×Закрыть

Реализуем авторизацию Apple через сторонние сервисы

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

Меня зовут Сергей Навка, я разработчик с пятилетним опытом разработки приложений под iOS. Сейчас сотрудничаю с сервисной IT компанией Yalantis. Люблю программирование и активный отдых.

Кому будет полезна статья: всем, кто уже реализовал авторизацию либо собирается это сделать через сторонние сервисы (Google, Facebook and etc). Так как с выходом iOS 13 Sign in with Apple стала обязательной при использовании third party сервисов.

Исключением может быть:

  1. Использование только собственного способ аутентификации, иными словами не использовать сторонние сервисы.
  2. Это приложение для образования, предприятия или бизнеса, которое требует от пользователя входа в систему с существующей учетной записью для образования или предприятия.
  3. Приложение использует государственную систему идентификации граждан для аутентификации пользователей.
  4. Приложение является клиентом для определенной сторонней службы, и пользователи должны войти в свою почту, социальные сети или другую стороннюю учетную запись напрямую, чтобы получить доступ к своему контенту.

Guideline Link (4.8 Sign in with Apple)

Note: Sign in with Apple работает только с профилями в которых включена 2FA.

Подготовка использования Sign in with Apple (iOS)

Настройка iOS приложения:

1. Переходим https://developer.apple.com/ -> Certificates, Identifiers & Profiles -> Identifiers

Выбираем нужный BundleID, ставим галочку напротив Sign in with Apple.

Нажимаем Configure/Edit

Выбираем APP ID конфигурацию и указываем Server to Server Notification Endpoint на который будут приходит обновления (удаление/изменения аккаунта).

2. Далее следует перегенерировать все нужные provisioning profiles и использовать их при сборке приложения.

3. Добавляем Capabilities Sign in with Apple в xCode.

Реализация iOS

Для iOS используется нативное SDK. Делаем import AuthenticationServices

Для начала нужно добавить кнопку ASAuthorizationAppleIDButton на UI либо создать кнопку которая будет соответствовать гайдлайнам. По ее нажатию вызываем код:

let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]

let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()

Также есть ASAuthorizationPasswordProvider который позволяет получать связки «логин-пароль» из Keychain.

Для того что бы SDK понимала где показывать нативный popup нужно реализовать ASAuthorizationControllerPresentationContextProviding где нужно вернуть window на котором покажется UI авторизации:

func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
    return UIApplication.shared.delegate!.window!!
}

Реализуем делегат ASAuthorizationControllerDelegate, который возвращает результат (success/failure):

public func authorizationController(
  controller: ASAuthorizationController, 
  didCompleteWithAuthorization authorization: ASAuthorization
) {
guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential,
let code = appleIDCredential.authorizationCode,
let codeStr = String(data: code, encoding: .utf8) else {
        // не валидный код, обработать как ошибку
        return
}

let email = appleIDCredential.email
let firstName = appleIDCredential.fullName?.givenName
let lastName = appleIDCredential.fullName?.familyName

// Если для авторизации обязателен email
If email != nil {
	// Так как email возвращается только при первой попытке 
	// требуется его локально сохранить в безопасном месте на случай если запрос на сервер не обработается
// и очистить при успешной авторизации
}

	// создать или залогиниться в своей системе с помощью кода авторизации codeStr
}

public func authorizationController(
  controller: ASAuthorizationController,
  didCompleteWithError error: Error
) {
  // обработка ошибки
}

Полученный authorizationCode мы отправляем на сервер. Сервер валидирует с Apple, возвращает результат клиенту.

P. S.: authorizationCode валиден 5 минут и используется только 1 раз.

Отслеживание текущего состояния аккаунта

            let provider = ASAuthorizationAppleIDProvider()
            
            provider.getCredentialState(forUserID: getStoredUserIdentifier()) { (credentialState, error) in
                switch credentialState {
                case .authorized:
                    break // Sign in with Apple выполнен, все ок
                case .revoked:
                    break // Юзер приостановил использования Sign in with Apple для этого приложения
                case .notFound:
                    break // Не найдено авторизованного аккаунта для этого приложения
                case .transferred:
                    break // вызывается при передачи/продажи приложения в AppStore
                @unknown default:
                    break
                }
            }

Как повторить возврат email

Так как email возвращается только при первой попытке возникают трудности с отладкой.

Apple предоставляет возможность остановки использования Apple Sign in извне приложения.

После того как остановили при повторной авторизации email будет в ответе (но только в 1 раз).

P. S. Так же можно остановить использование в личном кабинете через web.

Подготовка использования Sign in with Apple (WEB/ANDROID)

Чтобы использовать Sign in with Apple API нужно создать ServiceID, KEY

Заходим на developer.apple.com -> Certificates, Identifiers & Profiles -> Identifiers, тапаем на + -> Выбираем Services IDs -> придумываем identifier к примеру: com.yourbundle.signin

Далее тапаем на Register. Выбираем из списка только что созданный ServiceID, включаем Sign in with Apple

Нажимаем на Configure

1. Выбираем APPID/BundleID с которым будет связана Apple авторизация.

2. Указываем через запятую без пробелов с какими доменами и поддоменами будет взаимодействовать, в данном примере с 3 окружениями (development/staging/production).

3. Указать endpoints по котором придет callback при успешной авторизации(так же через запятую и без пробелов).

Далее нажимаем Continue проверяем и Save.

Создание приватного ключа: Ключ нужен для передачи информации от нашего сервера к серверу Apple в зашифрованном виде.
Будет использоваться сервером для валидации авторизованного юзера. Заходим на developer.apple.com -> Certificates, Identifiers & Profiles -> Keys. Тапаем на +. Включаем Sign in with Apple для этого ключа и Configure

Выбираем с каким AppID/BundleID будет взаимодействовать ключ и сохраняем.

Скачиваем ключ .p8 и сохраняем в безопасном месте (ключ доступен к скачиванию только 1 раз). Нажимаем Done.

Запоминаем значения:

  • Key ID
  • ClientID (identifier который был указан при создании ServiceID)
  • TeamID (можно найти в developer apple portal)
  • BundleID (для валидации authorization_code с ios клиентами вместо ClientID)
  • Key.p8

И передаем бэкэнд разработчику эти данные будут нужны для валидации authorization_code на сервере.

О валидации Apple ID на сервере можно почитать здесь.

Особенность для валидации iOS на сервере

При валидация authorization_code полученного с iOS клиента следует в поле client_id использовать BundleID приложения вместо client_id(identifier) установленного для ServiceID.

Реализация WEB

Для реализации потребуется кнопка соответствующая гайдлайнам Apple ссылка. Или же сделать кастомную кнопку Sign in with Apple можно сделать здесь.

Так как автор этой статьи iOS разработчик всю информацию по реализации для web можно найти по ссылке (там не сложно :) ).

Параметры которые требуются для реализации:

  • client_id (identifier который был указан при создании ServiceID)
  • scope — здесь можно запросить дополнительные данные такие как email, name (если указать email то у пользователя будет возможность показывать/скрывать почту и Apple гарантированно вернет ее в первый раз).
  • redirect-uri — эндпоинт на который перекинет после успешного выполнения
  • state — используется для дополнительной безопасности(можно оставить пустым)**
  • nonce — используется для дополнительной безопасности(можно оставить пустым)**
  • use-popup — показывать как окно поверх веб страницы(принимает true/false)

** nonce && state — уникальная строка которую указываем в запросе, в успешном ответе получите json, внутри будет лежать authorization > id_token это JWT который нужно задекодить и получить json, из полученного json вытаскиваем nonce его нужно сравнить с тем nonce который указывали при запросе авторизации.

Отправка имейлов через скрытую почту

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

Заходим на Certificates, Identifiers & Profiles -> More -> Sign in with Apple for Email Communication Configure

Где нужно указать домены и почты с которых будут отправляться письма.

Также требуется поддержка Sender Policy Framework (SPF) DNS TXT на доменах информация доступна по ссылке.

Server to Server обновления об изменениях в AppleID

Во время конфигурации Sign in with Apple на developer.apple.com для AppID/BundleID указывали Server to Server Notification Endpoint по которому будут приходить обновления по зарегестированым аккаунтам.

Будет приходит json такого вида:

{ “payload”: JWT }

По ключу payload будет доступен JWT который нужно декодить и в результате получим вот такой json:

{
	"iss": "https://appleid.apple.com",
	"aud": <bundle-identifier>,
	"iat": "2020-12-00 00:00:00 +0000",
	"jti": <unique event stream id>,
	"event": {
		"type": "email-disabled", 
		"sub": "001667.7f1e9b1f0d41426d8d8616e7d06be6f1.0433",
		"event_time": 1608693364100,
		"email": "<unique-alphanumeric-string>@privaterelay.appleid.com",
		"is_private_email": "true"
	}
}

В поле event будет доступен json с всей нужной информацией об изменениях Apple ID пользователя.

Доступные event.type:

  • «email-disabled» — юзер включил использование скрытой почты, скрытая почта будет внутри json
  • «email-enabled» — юзер открыл доступ к реальной почте, реальная почта будет внутри json.
  • «consent-revoked» — юзер приостановил использование Sign in with Apple для вашего приложения
  • «account-delete» — если юзер сделал запрос на удаление apple id.

Заключение

Apple беспокоиться о секьюрности своих пользователей что за собой несет некоторые сложности в реализации в итоге мы имеем:

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

— возможность скрыть реальную почту

— остановить получение назойливых писем в настройках appleid.

По моему мнению это the most user friendly way для авторизации.

👍НравитсяПонравилось7
В избранноеВ избранном2
Подписаться на тему «iOS»
LinkedIn

Похожие статьи

Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

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

Подписаться на комментарии