ASAuthorizationAppleIDRequest с именем и областью почты возвращает нулевые значения

Я реализую вход с помощью Apple и заметил, что свойства email и fullName возвращенного ASAuthorizationAppleIDCredential заполняются только при самом первом входе в систему для этого Apple ID. При всех последующих входах эти свойства равны нулю.

Это ошибка iOS 13 или ожидаемое поведение?

Вот код, который я использую для запуска запроса:

@available(iOS 13.0, *)
dynamic private func signInWithAppleClicked() {
    let request = ASAuthorizationAppleIDProvider().createRequest()
    request.requestedScopes = [.fullName, .email]

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

Я получаю учетные данные в этом методе делегата:

public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential else { return }

    let userIdentifier = credential.user
    let token = credential.identityToken
    let authCode = credential.authorizationCode
    let realUserStatus = credential.realUserStatus
    let mail = credential.email // nil
    let name = credential.fullName // nil
}

person mhaddl    schedule 21.08.2019    source источник
comment
Я потратил на это почти 3 часа, и похоже, что это ошибка. В первый раз, когда пользователь создает учетную запись - только тогда я могу получить полное имя и адрес электронной почты. При последующих попытках входа он возвращает мне только .user (id), а все остальное возвращает ноль.   -  person Osama Naeem    schedule 21.08.2019
comment
Согласно этой теме (forum.developer.apple.com/message/377782#377437) это кажется ожидаемым поведением.   -  person mhaddl    schedule 22.08.2019
comment
Аналогичный вопрос stackoverflow.com/q/57714339/2595805   -  person Eugene Berdnikov    schedule 12.09.2019
comment
Это ожидаемое поведение, как описано в документации Apple - developer.   -  person spig    schedule 22.03.2020
comment
Таким образом, похоже, что если пользователь снова входит в систему на другом устройстве, в том же приложении (или на том же устройстве, но приложение было удалено), информация для пользователя (например, полное имя) должна распространяться до приложение с сервера. (Или, как указано ниже, возможно, из-за локального декодирования JWT?)   -  person Chris Prince    schedule 11.01.2021
comment
Привет, не могли бы вы сказать мне, будет ли userIdentifier для конкретного пользователя одинаковым для каждого входа в систему с использованием Apple ID или нет?   -  person Ankit    schedule 05.07.2021


Ответы (8)


Похоже на ошибку, но после прочтения разных сообщений на форумах Apple, похоже, это ожидаемое поведение.

Итак, некоторые выводы.

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

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

Чтобы отозвать доступ на вашем устройстве с iOS 13.

iPhone Settings > Apple Id > Password & Security > Apple ID logins > {YOUR APP} > Stop using Apple ID
person Bilal    schedule 12.09.2019
comment
Это заставит экран с сенсорным идентификатором или любой другой экран аутентифицировать его, больше не звонить ... просто больше ничего не работает ... какие-то мысли ?? - person Daniel Arantes Loverde; 08.02.2020
comment
Если я могу получить электронную почту и полное имя только при первом звонке, могу ли я получить уведомление, если после этого полное имя изменилось? - person mdonati; 25.03.2020
comment
@mdonati: Начиная с iOS 14, вы можете получать эту информацию, реализуя уведомления S2S. На данный момент у нас нет дополнительной информации об этих уведомлениях. - person vmeyer; 07.08.2020

Если вам интересно, как получить электронную почту второй и последующие разы, вот подсказка: используйте identityToken, который содержит закодированные в JWT данные авторизации пользователя, включая электронную почту.

  1. Импортируйте эту библиотеку для декодирования JWT: https://github.com/auth0/JWTDecode.swift
  2. попробуйте этот код
    import JWTDecode
    ...
    if let identityTokenData = appleIDCredential.identityToken,
    let identityTokenString = String(data: identityTokenData, encoding: .utf8) {
    print("Identity Token \(identityTokenString)")
    do {
       let jwt = try decode(jwt: identityTokenString)
       let decodedBody = jwt.body as Dictionary
       print(decodedBody)
       print("Decoded email: "+(decodedBody["email"] as? String ?? "n/a")   )
    } catch {
       print("decoding failed")
    }

Или расшифруйте его на PHP-сервере следующим образом:

    print_r(json_decode(base64_decode(str_replace('_', '/', str_replace('-','+',explode('.', $identityTokenString)[1])))));
person Ilya Shevyryaev    schedule 24.04.2020
comment
Только при первом входе в систему JWT включает электронную почту. И ни в коем случае не имя или другая информация о пользователе. developer.apple.com/documentation/sign_in_with_apple/ - person Hamoonist; 18.05.2020
comment
Неправильно. Я мог постоянно иметь электронную почту в identityToken в закодированном представлении. - person Ilya Shevyryaev; 19.05.2020
comment
Я копирую вставленный ваш код, только в первый раз, когда он включал электронное письмо. - person Hamoonist; 20.05.2020
comment
Когда я попробовал это, письмо также было отправлено в последующих ответах identityToken. Возможно, стоит отметить, что я решил скрыть свой адрес электронной почты и что токен содержит адрес электронной почты ретранслятора Apple ... - person user2549803; 27.05.2020
comment
Что ж, спасибо ребятам вроде тебя, у меня все еще есть работа :). Скопировать вставку часто не получается, придется немного покопаться. Этот токен теперь отлично работает в производственном приложении с библиотекой php JWKFactory на бэкэнде. - person Ilya Shevyryaev; 08.06.2020
comment
Я воспроизвел логику php в javascript, и она отлично сработала. я смог получить адрес электронной почты. - person Rajesh; 07.07.2020
comment
Я дал свой ответ выше, и он работает, но, @IlyaShevyryaev, вы его прибили. Если вы хотите выполнить проверку на стороне сервера, я должен предложить этот ответ. - person Hitesh Surani; 08.07.2020
comment
Это хорошо, но не дает вам полного имени. Я думаю, что в верхней части сообщения человек спрашивает о получении адреса электронной почты и полного имени. - person LilMoke; 21.09.2020

Это кажется ожидаемым поведением:

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

Источник https://forums.developer.apple.com/thread/121496#379297

person Juan de la Torre    schedule 11.09.2019

Это правильное поведение при реализации входа в систему с Apple.

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

Чтобы решить эту проблему, мы можем сохранить всю необходимую информацию в Keychain. Я создал Singleton класс для входа в систему Apple. Я уверен, что это вам поможет.

Источник Git: https://github.com/IMHitesh/HSAppleSignIn

Вам необходимо выполнить следующие шаги:

Шаг 1

Добавьте в свой проект папку AppleSignIn.

Шаг 2

Включите вход через Apple в Capabilities.

Шаг: 3 -> ВАЖНО

Перейдите в свой UIViewController и добавьте представление контейнера для входа в систему с помощью Apple.

if #available(iOS 13.0, *) {
    appleSignIn.loginWithApple(view:viewAppleButton, completionBlock: { (userInfo, message) in
        if let userInfo = userInfo{
            print(userInfo.email)
            print(userInfo.userid)
            print(userInfo.firstName)
            print(userInfo.lastName)
            print(userInfo.fullName)
        }else if let message = message{
            print("Error Message: \(message)")
        }else{
            print("Unexpected error!")
        }
    })
}else{
    viewAppleButton.isHidden = true
}
person Hitesh Surani    schedule 22.04.2020


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

Лучше всего оценивать значение ASAuthorizationAppleIDProvider.getCredentialState, пока ваше приложение активно, для синхронизации состояния входа с внутренним сервером во времени.

См .: Как выйти из Apple после аутентификации

person Shrdi    schedule 30.10.2020

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

import Foundation
import SwiftKeychainWrapper

/// A Helper class which abstract Keychain API related calls.
final class KeyChainService {
    // MARK: - Properties
    static let shared = KeyChainService()
    
    /// Returns previous saved user name if available.
    var appleUserName: String? {
        return KeychainWrapper
            .standard
            .string(forKey: .appAppleUserName)
    }
    
    /// Returns previous saved user appleId/email  if available.
    var appleUserEmail: String? {
        return KeychainWrapper
            .standard
            .string(forKey: .appAppleEmailId)
    }
    
    
    /// Saves the apple user name into keychain.
    /// - Parameter name: Apple user name retrieved form AppleLogin.
    /// - Returns: true if succeed otherwise false.
    @discardableResult
    func saveAppleUserName(name: String?) -> Bool {
        guard let name = name else {return false}
        return KeychainWrapper.standard.set(
            name,
            forKey: KeychainWrapper.Key.appAppleUserName.rawValue
        )
    }
    
    /// Saves the apple user email into keychain.
    /// - Parameter email: Apple userId/email  retrieved form AppleLogin.
    /// - Returns: true if succeed otherwise false.
    @discardableResult
    func saveAppleEmail(email: String?) -> Bool {
        guard let email = email else {return false}
        return KeychainWrapper.standard.set(
            email,
            forKey: KeychainWrapper.Key.appAppleEmailId.rawValue
        )
    }
    

    /// Deletes both apple user name and saved Id from keyChain.
    func deleteSavedAppleUserInfo(){
        KeychainWrapper.standard.remove(forKey: .appAppleUserName)
        KeychainWrapper.standard.remove(forKey: .appAppleEmailId)
    }
}

// MARK: - KeychainWrapper + Extensions
extension KeychainWrapper.Key {
    /// A random string used to identify saved user apple name from keychain.
    static let appAppleUserName: KeychainWrapper.Key = "appAppleUserName"
    
    /// A random string used to identify saved user apple email /Id from keychain.
    static let appAppleEmailId:KeychainWrapper.Key = "appAppleEmailId"
}

person Mussa Charles    schedule 06.12.2020

Это не ошибка, но будет означать, что ваша аутентификация успешно сохранена в настройках вашего устройства.

если вы хотите получить всю эту информацию снова, вам необходимо следовать этим положениям.

  1. перейдите на свое устройство - ›Настройки -› Apple ID - ›Пароль и безопасность -› Приложения Используя свой Apple ID - ›вы получите список приложений, которые использовались для входа с помощью Apple {найдите свое приложение} -› проведите пальцем влево от строки с вашими приложениями {show Параметр «Удалить»} - ›нажмите« Удалить »

  2. перезапустите приложение или повторно войдите с помощью кнопки Apple

Теперь вы можете получить всю информацию

person Rahul Patel    schedule 09.07.2021