Accedere con Apple in Xamarin.iOS

Scaricare l'esempio Scaricare l'esempio

L'accesso con Apple è un nuovo servizio che fornisce la protezione delle identità per gli utenti di servizi di autenticazione di terze parti. A partire da iOS 13, Apple richiede che anche le nuove app che usano servizi di autenticazione di terze parti forniranno l'accesso con Apple. Le app esistenti in fase di aggiornamento non devono aggiungere l'accesso con Apple fino ad aprile 2020.

Questo documento illustra come aggiungere l'accesso con Apple alle applicazioni iOS 13.

Configurazione per sviluppatori Apple

Prima di compilare ed eseguire un'app usando Accedi con Apple, è necessario completare questi passaggi. Nel portale Dei certificati per sviluppatori Apple, profili degli identificatori:

  1. Creare un nuovo identificatore id app.
  2. Impostare una descrizione nel campo Descrizione.
  3. Scegliere un ID bundle esplicito e impostare nel campo .
  4. Abilitare l'accesso con la funzionalità Apple e registrare la nuova identità.
  5. Creare un nuovo profilo di provisioning con la nuova identità.
  6. Scaricarlo e installarlo nel dispositivo.
  7. In Visual Studio abilitare la funzionalità Accedi con Apple nel file Entitlements.plist.

Controllare lo stato di accesso

Quando l'app inizia o quando è necessario controllare per la prima volta lo stato di autenticazione di un utente, creare un'istanza di ASAuthorizationAppleIdProvider e controllare lo stato corrente:

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

In questo codice, chiamato durante in , l'app gestirà quando uno stato è FinishedLaunching e presenta AppDelegate.csNotFoundLoginViewController l'oggetto all'utente. Se lo stato ha restituito o , all'utente può AuthorizedRevoked essere presentata un'azione diversa.

LoginViewController per l'accesso con Apple

Che UIViewController implementa la logica di accesso e offre l'accesso con Apple deve IASAuthorizationControllerDelegate implementare e come IASAuthorizationControllerPresentationContextProvidingLoginViewController nell'esempio seguente.

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

Animazione dell'app di esempio con l'accesso con Apple

Questo codice di esempio controlla lo stato di accesso corrente in e PerformExistingAccountSetupFlows si connette alla visualizzazione corrente come delegato. Se viene trovata una credenziale keychain iCloud esistente o una credenziale ID Apple, all'utente verrà richiesto di usarla.

Apple fornisce ASAuthorizationAppleIdButton , un pulsante specifico per questo scopo. Quando viene toccato, il pulsante attiverà il flusso di lavoro gestito nel metodo HandleAuthorizationAppleIDButtonPress .

Gestione dell'autorizzazione

In IASAuthorizationController implementare qualsiasi logica personalizzata per archiviare l'account dell'utente. L'esempio seguente archivia l'account dell'utente in Keychain, il servizio di archiviazione di 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

Controller di autorizzazione

L'ultima parte di questa implementazione è ASAuthorizationController l'oggetto che gestisce le richieste di autorizzazione per il provider.

#region IASAuthorizationControllerPresentation Context Providing

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

#endregion