Share via


Iniciar sessão com a Apple em Xamarin.iOS

Entrar com a Apple é um novo serviço que fornece proteção de identidade para usuários de serviços de autenticação de terceiros. A partir do iOS 13, a Apple exige que qualquer novo aplicativo que use serviços de autenticação de terceiros também forneça login com a Apple. Os aplicativos existentes que estão sendo atualizados não precisam adicionar login com a Apple até abril de 2020.

Este documento apresenta como pode adicionar Iniciar sessão com a Apple a aplicações iOS 13.

Configuração do desenvolvedor da Apple

Antes de criar e executar um aplicativo usando o Login com a Apple, você precisa concluir estas etapas. No portal Apple Developer Certificates, Identifiers & Profiles :

  1. Crie um novo Identificador de IDs de aplicativo.
  2. Defina uma descrição no campo Descrição .
  3. Escolha um ID de pacote explícito e defina com.xamarin.AddingTheSignInWithAppleFlowToYourApp no campo.
  4. Ative o login com o recurso Apple e registre a nova identidade.
  5. Crie um novo Perfil de Provisionamento com a nova Identidade.
  6. Baixe-o e instale-o no seu dispositivo.
  7. No Visual Studio, habilite o recurso Entrar com a Apple no arquivo Entitlements.plist .

Verificar o status de entrada

Quando seu aplicativo for iniciado ou quando você precisar verificar pela primeira vez o status de autenticação de um usuário, instancie um ASAuthorizationAppleIdProvider e verifique o estado atual:

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;
    }
});

Nesse código, chamado durante FinishedLaunching o AppDelegate.cs, o aplicativo manipulará quando um estado estiver NotFound e apresentará ao LoginViewController usuário. Se o estado teve retorno Authorized ou Revoked, uma ação diferente pode ser apresentada ao usuário.

Um LoginViewController para iniciar sessão com a Apple

O UIViewController que implementa a lógica de login e oferece login com a Apple precisa implementar IASAuthorizationControllerDelegate e IASAuthorizationControllerPresentationContextProviding como no LoginViewController exemplo abaixo.

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 ();
    }
}

Animação de aplicativo de exemplo usando o Login com a Apple

Este código de exemplo verifica o status de logon atual e PerformExistingAccountSetupFlows se conecta ao modo de exibição atual como um representante. Se for encontrada uma credencial existente do Porta-chaves iCloud ou do ID Apple, o usuário será solicitado a usá-la.

A Apple fornece ASAuthorizationAppleIdButtonum botão especificamente para este fim. Quando tocado, o botão acionará o fluxo de trabalho manipulado no método HandleAuthorizationAppleIDButtonPress.

Autorização de manuseio

Na implementação de IASAuthorizationController qualquer lógica personalizada para armazenar a conta do usuário. O exemplo abaixo armazena a conta do usuário no Keychain, o próprio serviço de armazenamento da 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

Controlador de autorização

A peça final nessa implementação é a ASAuthorizationController que gerencia solicitações de autorização para o provedor.

#region IASAuthorizationControllerPresentation Context Providing

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

#endregion