Xamarin.ios에서 Apple에 로그인 합니다.Sign In with Apple in Xamarin.iOS

샘플 다운로드 샘플 다운로드Download Sample Download the sample

Apple로 로그인은 타사 인증 서비스 사용자에 게 id 보호를 제공 하는 새로운 서비스입니다.Sign In with Apple is a new service that provides identity protection for users of third-party authentication services. IOS 13부터 Apple에서는 타사 인증 서비스를 사용 하는 새 앱 에서도 Apple에 로그인을 제공 해야 합니다.Beginning with iOS 13, Apple requires that any new app using a third-party authentication services should also provide Sign In with Apple. 업데이트 되는 기존 앱은 4 월 2020 일까 지 Apple에 로그인을 추가할 필요가 없습니다.Existing apps being updated do not need to add Sign In with Apple until April 2020.

이 문서에서는 Apple에서 로그인을 iOS 13 응용 프로그램에 추가할 수 있는 방법을 소개 합니다.This document introduces how you can add Sign In with Apple to iOS 13 applications.

Apple developer 설치Apple developer setup

Apple에서 로그인을 사용 하 여 앱을 빌드하고 실행 하기 전에 다음 단계를 완료 해야 합니다.Before building and running an app using Sign In with Apple, you need to complete these steps. Apple Developer certificate, 식별자 & 프로필 포털에서 다음을 수행 합니다.On Apple Developer Certificates, Identifiers & Profiles portal:

  1. 앱 id 식별자를 만듭니다.Create a new App Ids Identifier.
  2. 설명 필드에 설명을 설정 합니다.Set a description in the Description field.
  3. 명시적 번들 ID를 선택 하 고 com.xamarin.AddingTheSignInWithAppleFlowToYourApp 필드에를 설정 합니다.Choose an Explicit Bundle ID and set com.xamarin.AddingTheSignInWithAppleFlowToYourApp in the field.
  4. Apple 기능으로 로그인 을 사용 하도록 설정 하 고 새 id를 등록 합니다.Enable Sign In with Apple capability and register the new Identity.
  5. 새 Id를 사용 하 여 새 프로 비전 프로필을 만듭니다.Create a new Provisioning Profile with the new Identity.
  6. 장치에 다운로드 하 여 설치 합니다.Download and install it on your device.
  7. Visual Studio에서 info.plist 파일의 Apple 기능으로 로그인 을 사용 하도록 설정 합니다.In Visual Studio, enable the Sign In with Apple capability in Entitlements.plist file.

로그인 상태 확인Check sign in status

앱이 시작 되거나 사용자의 인증 상태를 처음 확인 해야 하는 경우를 인스턴스화하고 ASAuthorizationAppleIdProvider 현재 상태를 확인 합니다.When your app begins, or when you first need to check the authentication status of a user, instantiate an ASAuthorizationAppleIdProvider and check the current state:

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 LoginViewController 에서 실행 되는이 코드에서, 상태가 일 NotFound 때 앱이 처리 하 고 사용자에 게를 표시 합니다. AppDelegate.csIn this code, called during FinishedLaunching in the AppDelegate.cs, the app will handle when a state is NotFound and present the LoginViewController to the user. 상태에서 또는 Revoked를 반환 Authorized 하는 경우 다른 작업이 사용자에 게 표시 될 수 있습니다.If the state had return Authorized or Revoked, a different action may be presented to the user.

Apple에서 로그인에 대 한 LoginViewControllerA LoginViewController for Sign In with Apple

로그인 UIViewController 논리를 구현 하 고 Apple에서 로그인을 제공 하는는 IASAuthorizationControllerDelegate 아래 IASAuthorizationControllerPresentationContextProviding LoginViewController 예제와 같이 및를 구현 해야 합니다.The UIViewController that implements login logic and offers Sign In with Apple needs to implement IASAuthorizationControllerDelegate and IASAuthorizationControllerPresentationContextProviding as in the LoginViewController example below.

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 현재 로그인 상태를 확인 하 고 현재 뷰에 대리자로 연결 합니다.This example code checks the current login status in PerformExistingAccountSetupFlows and connects to the current view as a delegate. 기존 iCloud 키 집합 자격 증명 또는 Apple ID 자격 증명이 있는 경우 사용자에 게 해당 자격 증명을 사용 하 라는 메시지가 표시 됩니다.If an existing iCloud Keychain credential or Apple ID credential is found, the user will be prompted to use that.

Apple은 ASAuthorizationAppleIdButton이러한 용도로만 사용할 단추를 제공 합니다.Apple provides ASAuthorizationAppleIdButton, a button specifically for this purpose. 작업을 수행 하는 경우 단추는 메서드에서 HandleAuthorizationAppleIDButtonPress처리 된 워크플로를 트리거합니다.When touched, the button will trigger the workflow handled in the method HandleAuthorizationAppleIDButtonPress.

권한 부여 처리Handling authorization

에서 사용자 계정을 저장 하는 사용자 지정 논리를 구현합니다.IASAuthorizationControllerIn the IASAuthorizationController implement any custom logic to store the user's account. 아래 예제에서는 사용자의 계정을 키 집합 Apple의 자체 저장소 서비스에 저장 합니다.The example below stores the user's account in Keychain, Apple's own storage service.

#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

권한 부여 컨트롤러Authorization Controller

이 구현 ASAuthorizationController 에서 최종 조각은 공급자에 대 한 권한 부여 요청을 관리 하는입니다.The final piece in this implementation is the ASAuthorizationController which manages authorization requests for the provider.

#region IASAuthorizationControllerPresentation Context Providing

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

#endregion