Вход с помощью Apple в Xamarin.iOS

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

В этом документе описано, как добавить вход с помощью приложений Apple в iOS 13.

Настройка разработчика Apple

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

  1. Создайте новый идентификатор идентификаторов приложений .
  2. Задайте описание в поле "Описание ".
  3. Выберите явный идентификатор пакета и задайте com.xamarin.AddingTheSignInWithAppleFlowToYourApp в поле.
  4. Включите вход с помощью возможностей Apple и зарегистрируйте новое удостоверение.
  5. Создайте новый профиль подготовки с новым удостоверением.
  6. Скачайте и установите его на устройстве.
  7. В Visual Studio включите функцию входа с помощью Apple в файле Entitlements.plist .

Проверка состояния входа

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

var appleIdProvider = new ASAuthorizationAppleIdProvider ();
appleIdProvider.GetCredentialState (KeychainItem.CurrentUserIdentifier, (credentialState, error) => {
    switch (credentialState) {
    case ASAuthorizationAppleIdProviderCredentialState.Authorized:
        // The Apple ID credential is valid.
        break;
    case ASAuthorizationAppleIdProviderCredentialState.Revoked:
        // The Apple ID credential is revoked.
        break;
    case ASAuthorizationAppleIdProviderCredentialState.NotFound:
        // No credential was found, so show the sign-in UI.
        InvokeOnMainThread (() => {
            var storyboard = UIStoryboard.FromName ("Main", null);

            if (!(storyboard.InstantiateViewController (nameof (LoginViewController)) is LoginViewController viewController))
                return;

            viewController.ModalPresentationStyle = UIModalPresentationStyle.FormSheet;
            viewController.ModalInPresentation = true;
            Window?.RootViewController?.PresentViewController (viewController, true, null);
        });
        break;
    }
});

В этом коде, вызываемом во время FinishedLaunching работы AppDelegate.cs, приложение будет обрабатывать состояние NotFound и представлять LoginViewController пользователю. Если состояние возвращено Authorized или Revokedдругое действие может быть представлено пользователю.

LoginViewController для входа с помощью Apple

Эта UIViewController функция реализует логику входа и предлагает вход с помощью Apple IASAuthorizationControllerDelegate , IASAuthorizationControllerPresentationContextProviding как показано в приведенном LoginViewController ниже примере.

public partial class LoginViewController : UIViewController, IASAuthorizationControllerDelegate, IASAuthorizationControllerPresentationContextProviding {
    public LoginViewController (IntPtr handle) : base (handle)
    {
    }

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
        // Perform any additional setup after loading the view, typically from a nib.

        SetupProviderLoginView ();
    }

    public override void ViewDidAppear (bool animated)
    {
        base.ViewDidAppear (animated);

        PerformExistingAccountSetupFlows ();
    }

    void SetupProviderLoginView ()
    {
        var authorizationButton = new ASAuthorizationAppleIdButton (ASAuthorizationAppleIdButtonType.Default, ASAuthorizationAppleIdButtonStyle.White);
        authorizationButton.TouchUpInside += HandleAuthorizationAppleIDButtonPress;
        loginProviderStackView.AddArrangedSubview (authorizationButton);
    }

    // Prompts the user if an existing iCloud Keychain credential or Apple ID credential is found.
    void PerformExistingAccountSetupFlows ()
    {
        // Prepare requests for both Apple ID and password providers.
        ASAuthorizationRequest [] requests = {
            new ASAuthorizationAppleIdProvider ().CreateRequest (),
            new ASAuthorizationPasswordProvider ().CreateRequest ()
        };

        // Create an authorization controller with the given requests.
        var authorizationController = new ASAuthorizationController (requests);
        authorizationController.Delegate = this;
        authorizationController.PresentationContextProvider = this;
        authorizationController.PerformRequests ();
    }

    private void HandleAuthorizationAppleIDButtonPress (object sender, EventArgs e)
    {
        var appleIdProvider = new ASAuthorizationAppleIdProvider ();
        var request = appleIdProvider.CreateRequest ();
        request.RequestedScopes = new [] { ASAuthorizationScope.Email, ASAuthorizationScope.FullName };

        var authorizationController = new ASAuthorizationController (new [] { request });
        authorizationController.Delegate = this;
        authorizationController.PresentationContextProvider = this;
        authorizationController.PerformRequests ();
    }
}

Анимация примера приложения с помощью входа в Apple

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

Apple предоставляет ASAuthorizationAppleIdButtonкнопку специально для этой цели. При касании кнопка активирует рабочий процесс, обрабатываемый в методе HandleAuthorizationAppleIDButtonPress.

Обработка авторизации

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

#region IASAuthorizationController Delegate

[Export ("authorizationController:didCompleteWithAuthorization:")]
public void DidComplete (ASAuthorizationController controller, ASAuthorization authorization)
{
    if (authorization.GetCredential<ASAuthorizationAppleIdCredential> () is ASAuthorizationAppleIdCredential appleIdCredential) {
        var userIdentifier = appleIdCredential.User;
        var fullName = appleIdCredential.FullName;
        var email = appleIdCredential.Email;

        // Create an account in your system.
        // For the purpose of this demo app, store the userIdentifier in the keychain.
        try {
            new KeychainItem ("com.example.apple-samplecode.juice", "userIdentifier").SaveItem (userIdentifier);
        } catch (Exception) {
            Console.WriteLine ("Unable to save userIdentifier to keychain.");
        }

        // For the purpose of this demo app, show the Apple ID credential information in the ResultViewController.
        if (!(PresentingViewController is ResultViewController viewController))
            return;

        InvokeOnMainThread (() => {
            viewController.UserIdentifierText = userIdentifier;
            viewController.GivenNameText = fullName?.GivenName ?? "";
            viewController.FamilyNameText = fullName?.FamilyName ?? "";
            viewController.EmailText = email ?? "";

            DismissViewController (true, null);
        });
    } else if (authorization.GetCredential<ASPasswordCredential> () is ASPasswordCredential passwordCredential) {
        // Sign in using an existing iCloud Keychain credential.
        var username = passwordCredential.User;
        var password = passwordCredential.Password;

        // For the purpose of this demo app, show the password credential as an alert.
        InvokeOnMainThread (() => {
            var message = $"The app has received your selected credential from the keychain. \n\n Username: {username}\n Password: {password}";
            var alertController = UIAlertController.Create ("Keychain Credential Received", message, UIAlertControllerStyle.Alert);
            alertController.AddAction (UIAlertAction.Create ("Dismiss", UIAlertActionStyle.Cancel, null));

            PresentViewController (alertController, true, null);
        });
    }
}

[Export ("authorizationController:didCompleteWithError:")]
public void DidComplete (ASAuthorizationController controller, NSError error)
{
    Console.WriteLine (error);
}

#endregion

Контроллер авторизации

Последний элемент этой реализации — это ASAuthorizationController управление запросами авторизации для поставщика.

#region IASAuthorizationControllerPresentation Context Providing

public UIWindow GetPresentationAnchor (ASAuthorizationController controller) => View.Window;

#endregion