Accedere con Apple in Xamarin.iOS

Download Sample 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 qualsiasi nuova app che usi servizi di autenticazione di terze parti debba fornire anche l'accesso con Apple. Le app esistenti da aggiornare non devono aggiungere Accedi 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 Certificati per sviluppatori Apple, Identificatori e profili :

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

Controllare lo stato di accesso

All'avvio dell'app o quando è prima necessario controllare lo stato di autenticazione di un utente, creare un'istanza ASAuthorizationAppleIdProvider di 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 FinishedLaunching in AppDelegate.cs, l'app gestirà quando uno stato è NotFound e presente all'utente LoginViewController . Se lo stato ha restituito Authorized o Revoked, è possibile che all'utente venga presentata un'azione diversa.

LoginViewController per l'accesso con Apple

Che UIViewController implementa la logica di accesso e offre l'accesso con Apple deve implementare IASAuthorizationControllerDelegate e IASAuthorizationControllerPresentationContextProviding come nell'esempio LoginViewController 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 ();
    }
}

Animation of sample app using Sign In with Apple

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

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

Gestione dell'autorizzazione

Nell'implementazione IASAuthorizationController di 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

La parte finale di questa implementazione è quella ASAuthorizationController che gestisce le richieste di autorizzazione per il provider.

#region IASAuthorizationControllerPresentation Context Providing

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

#endregion