[Unity] Apple Login과 Firebase 인증

Apple이 익명 로그인 기능만 사용할게 아니라면 Apple Login을 의무화 하였기 때문에 Apple Login을 지원하지 않으면 리젝을 당하게 됩니다.

그래서 Apple Login을 프로젝트에 추가하는 과정을 간략하게 남깁니다.

우선 Unity에서 애플 로그인을 지원하는 플러그인이 두 가지가 있습니다.
첫번째는 Unity Technology에서 제공하는 SignInWithApple 입니다.
https://assetstore.unity.com/packages/tools/sign-in-with-apple-154202

두번째는 GitHub에 올라와 있는 AppleAuth 입니다.
https://github.com/lupidan/apple-signin-unity

당연히 처음에는 Unity에서 제공해 주는 플러그인을 사용하여 구현을 진행하였고 Apple Login이 문제없이 동작하는 것을 확인하였으나 Firebase 인증 단계에서 벽에 부딪혔습니다.
Firebase 인증 단계에서 Apple Login의 검증을 위해 nonce를 요구하는데, SignInWithApple은 Nonce을 얻어오는 API가 제공되지 않았습니다.
(현재 시점에 제가 발견하지 못했을 수 있습니다.)
그래서 눈물을 머금고 코드를 날린 뒤 AppleAuth를 통해 구현을 진행했습니다.

AppleAuth 설치

GitHub의 설명대로 진행하면 됩니다.
저는 2018.4 버전을 사용하고 있기 때문에 Packages/manifest.json 파일을 열어 아래의 패키지를 추가해 줍니다.

"dependencies": {
    "com.lupidan.apple-signin-unity": "https://github.com/lupidan/apple-signin-unity.git#v1.1.0",
}

AppleAuthorizer 구현

using AppleAuth;

public class AppleAuthorizer {
    // 취향 상 싱글턴을 사용합니다.
    public static AppleAuthorizer instance {get; private set;}

    public static string IdToken {get; private set;}
    public static string AuthCode {get; private set;}
    public static string RawNonce {get; private set;}
    public static string Nonce {get; private set;}

    private IAppleAuthManager appleAuthManager;
    public bool IsLoginSuccess = false;
    public bool IsLoginDone = false;

    private void Awake() {
        if( _instance == null && _instance != this) {
            Destroy(gameObject);
            return;
        }
        _instance = this;
        DontDestroyOnLoad(gameObject);
    }
    private void Start() {
        if(AppleAuthManager.IsCurrentPlatformSupported) {
            var deserializer = new PayloadDeserializer();
            appleAuthManager = new AppleAuthManager(deserializer);
        }
    }
    private void Update() {
        appleAuthManager?.Update();
    }

    // Nonce는 SHA256으로 만들어서 전달해야함
    private static string GenerateNonce(string _rawNonce) {
        SHA256 sha = new SHA256Managed();
        var sh = new StringBuilder();
        // Encoding은 반드시 ASCII여야 함
        byte[] hash = sha.ComputeHash(Encoding.ASCII.GetBytes(_rawNonce));
        // ToString에서 "x2"로 소문자 변환해야 함. 대문자면 실패함. ㅠㅠ
        foreach (var b in hash) sb.Append(b.ToString("x2"));
        return sb.ToString();
    }
    // 내부적으로 사용하는 인터페이스 통일을 위해 Coroutine으로 구현
    // 기존 인터페이스가 아니었다면 Async를 사용했을 듯
    public IEnumerator LoginProcess() {
        IsLoginSuccess = false;
        IsLoginDone = false;

        // Nonce 초기화
        // Nonce는 Apple로그인 시 접속 세션마다 새로 생성
        RawNonce = System.Guid.NewGuid().ToString();
        Nonce = GenerateNonce(RawNonce);

        // QuickLogin을 먼저 수행
        // 이전 로그인 기록이 없다면 실패 처리됨
        var quickLoginArgs = new AppleAuthQuickLoginArgs(Nonce);
        var isQuickLoginDone = false;
        appleAuthManager.QuickLogin(
            quickLoginArgs,
            credential => {
                try {
                    var appleIdCredential = credential as IAppleIDCredential;
                    AuthCode = Encoding.UTF8.GetString(appleIdCredential.AuthorizationCode);
                    IdToken = Encoding.UTF8.GetString(appleIdCredential.IdentityToken);
                    IsLoginSuccess = true;
                }
                catch(System.Exception e) {
                    Debug.LogException(e);
                    IsLoginSuccess = false;
                }
                isQuickLoginDone = true;
            },
            error => {
                IsLoginSuccess = false;
                isQuickLoginDone = true;
            });
        yield return new WaitUntil(() => isQuickLoginDone);
        // QuickLogin이 성공했다는 것은 이전 로그인 정보가 있었다는 의미
        // 일반 Login 과정을 진행할 필요가 없어짐.
        if(IsLoginSuccess) {
            IsLoginDone = true;
            yield break;
        }

        var loginArgs = new AppleAuthLoginArgs(LoginOptions.IncludeEmail, Nonce);
        appleAuthManager.LoginWithAppleId(
            loginArgs,
            credential => {
                try {
                    var appleIdCredential = credential as IAppleIDCredential;
                    AuthCode = Encoding.UTF8.GetString(appleIdCredential.AuthorizationCode);
                    IdToken = Encoding.UTF8.GetString(appleIdCredential.IdentityToken);
                    IsLoginSuccess = true;
                }
                catch(System.Exception e) {
                    Debug.LogException(e);
                    IsLoginSuccess = false;
                }
                IsLoginDone = true;
            },
            error => {
                IsLoginSuccess = false;
                IsLoginDone = true;
            });
        yield return new WaitUntil(() => IsLoginDone);
    }
}

위의 코드는 프로젝트에서 사용하는 인증 코드의 일부를 떼어낸 것이라 컴파일 오류가 날 수 있으나 기본적인 로직은 동일하기 때문에 사용 가능할 것이라고 생각됩니다.
QuickLogin에 대한 처리는 기존 로그인 정보가 있는지 여부를 별도로 파악해야할지 그냥 QuickLogin을 실패처리하면 될지 테스트가 필요한 상태.

Firebase에서 AppleLogin 사용

Firebase 쪽 코드는 잡코드가 많아서 Apple Login에 사용되는 부분만 추렸다.

// Firebase는 Google이나 Apple GameCenter와 달리 AppleLoginProvider를 제공하지 않고 있다. 그래서 OAuthProvider를 사용해야한다.
// RawNonce는 SHA256으로 변환하기 전 문자열을 의미한다.
var credential = Firebase.Auth.OAuthProvider.GetCredential("apple.com", AppleAuthorizer.IdToken, AppleAuthorizer.RawNonce, AppleAuthorizer.AuthCode);

var task = auth.SignInWithCredentialAsync(credential);
yield return new WaitUntil(() => task.IsCompleted || task.IsFaulted || task.IsCanceled);

FirebaseUser user = task.Result;

주의할 것은 AppleLogin 과정에서는 SHA256으로 만들어진 Nonce를 전달해야하고 Firebase 인증 시에는 SHA256으로 변환하기 전의 문자열을 전달해야 한다.

XCode 설정

AppleLogin을 위한 XCode 설정은 Unity Blog에서 영상으로 친절하게 설명하고 있으니 해당 내용을 참고하면 된다.
https://blogs.unity3d.com/kr/2019/09/19/support-for-apple-sign-in/

Reference

https://firebase.google.com/docs/auth/ios/apple?authuser=0
https://github.com/lupidan/apple-signin-unity
https://firebase.google.com/docs/auth/unity/apple

기타 삽질

UIApplicationExitsOnSuspend가 plist에 포함되어 있으면 ipa 업로드 중 에러남.
plist 열어서 삭제해 줘야함.

2 thoughts on “[Unity] Apple Login과 Firebase 인증”

  1. Hello I tried to follow your step to make an Apple sign in, But still doesn’t work, I wanna know which one you use to make an Apple sign in? Unity Technology SignInWithApple or GitHub AppleAuth? And whats AppleAuthorizer you meant? Cos my Apple sign-in working well in client-side but it doesn’t work to sign in for my firebase. Thanks for advance, If you can show me your sample that would be better. 🙂

    1. Hello.
      I used github AppleAuth. because there is no Nonce in Unity SignInWithApple.
      You need to check your xcode setting.
      If you build for android, I don’t provide apple auth to android version.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다