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 :
- Crie um novo Identificador de IDs de aplicativo.
- Defina uma descrição no campo Descrição .
- Escolha um ID de pacote explícito e defina
com.xamarin.AddingTheSignInWithAppleFlowToYourApp
no campo. - Ative o login com o recurso Apple e registre a nova identidade.
- Crie um novo Perfil de Provisionamento com a nova Identidade.
- Baixe-o e instale-o no seu dispositivo.
- 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 ();
}
}
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 ASAuthorizationAppleIdButton
um 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