Unity에서 Apple로 로그인 구현

jinwook4217·2020년 8월 11일
2
post-thumbnail

iOS 빌드 실행 에러로 한동안 iOS 앱 업데이트를 못하고 있다가 에러를 해결하고 오랜만에 iOS 업데이트 신청을 했다. 앱에 큰 문제가 없어 바로 통과할 줄 알았는데 다음과 같은 이유로 거절당했다.

Guideline 4.8 - Design - Sign in with Apple

We noticed that your app uses a third-party login service but does not offer Sign in with Apple. Apps that use a third-party login service for account authentication must offer Sign in with Apple to users as an equivalent option.

Next Steps

To resolve this issue, please revise your app to offer Sign in with Apple as an equivalent login option.

Resources

  • Review the

sample code

on Apple Developer Support to learn more about implementing Sign in with Apple in your app.

  • Read the Sign in with Apple

Overview

to learn more about the benefits Sign in with Apple offers users and developers.

Please see attached screenshot for details.

이게 무엇인가 하고 찾아보니 2020년 4월 이후로는 다른 소셜 로그인을 제공하는데 애플 로그인을 같이 제공하지 않으면 기존 앱도 업데이트 거절 사유가 된다고 되어있다. 만약 다른 소셜 로그인을 제공하지 않는다면 애플 로그인이 필수는 아니다. 서비스 중인 앱에서 구글 로그인을 제공하고 있어서 애플 로그인을 적용해야 했다.

Apple로 로그인 버튼 추가


기존에 사용하던 버튼 디자인에 애플의 애플 로그인 디자인 가이드에 벗어나지 않도록 버튼을 추가하고 iOS 에서만 애플 로그인이 가능하도록 만들었다. 안드로이드에서 애플 로그인을 사용하려면 좀 더 복잡한 개발 과정을 거쳐야 하고 안드로이드에서 애플 로그인을 사용할 사용자를 고려하기엔 가성비가 좋지 않았다.

애플 로그인 플러그인


구글 로그인 플러그인 처럼 애플 로그인도 플러그인이 있을거라고 생각하고 여러 자료를 찾아보다가 한 블로그에서 좋은 플러그인 두 가지를 찾게 되었다.

Unity Technology에서 제공하는 SignInWithApple

https://assetstore.unity.com/packages/tools/sign-in-with-apple-154202

GitHub에 오픈 소스로 올라와있는 apple-signin-unity

https://github.com/lupidan/apple-signin-unity

서비스에서 Firebase Auth를 사용하고 있기 때문에 Firebase Auth와의 사용법이 나와있는 GitHub 오픈 소스 플러그인을 사용하기로 했다.

apple-signin-unity 설치 및 세팅


플러그인 설치 방법은 GitHub에 여러가지 방법으로 자세하게 나와있다.

https://github.com/lupidan/apple-signin-unity#installation

Apple로 로그인을 사용하려면 Apple Developer에서 Apple ID Configuration을 수정하거나 XCode에서 수동으로 설정할 수 있다.

XCode에서 수동으로 Sign in with Apple을 설정하는 방법

https://github.com/lupidan/apple-signin-unity#manual-entitlements-setup

Firebase Auth와 연동하려면 Firebase Authentication의 Sign-in method에서 Apple을 사용 설정한다. iOS에서 사용하려면 추가적인 설정은 필요없다.

Apple로 로그인 구현


일단 간단하게 사용중인 페이지에 Apple로 로그인을 GitHub에서 설명하는 그대로 구현했다.

SignInView.cs

using UnityEngine;
using System.Collections.Generic;
using System;
using Firebase.Auth;
using AppleAuth;
using AppleAuth.Native;
using AppleAuth.Enums;
using AppleAuth.Extensions;
using System.Text;
using System.Security.Cryptography;
using AppleAuth.Interfaces;

public class SignInView : MonoBehaviour {
    private IAppleAuthManager _appleAuthManager;

    public override void OnClose() {
    }

    public override void OnOpen() {
#if UNITY_IOS
        // Apple로 로그인 켜기

        if (AppleAuthManager.IsCurrentPlatformSupported) {
            var deserializer = new PayloadDeserializer();
            _appleAuthManager = new AppleAuthManager(deserializer);
        }
#else
        // Apple로 로그인 끄기
#endif
    }

    private void Update() {
        _appleAuthManager?.Update();
    }

    private void OnClickSignInWithApple() {
        // 로더 켜기

        var rawNonce = GenerateRandomString(32);
        var nonce = GenerateSHA256NonceFromRawNonce(rawNonce);

        var loginArgs = new AppleAuthLoginArgs(LoginOptions.IncludeEmail | LoginOptions.IncludeFullName, nonce);

        _appleAuthManager.LoginWithAppleId(
            loginArgs,
            async credential => {
                try {
                    var appleIdCredential = credential as IAppleIDCredential;
                    var identityToken = Encoding.UTF8.GetString(appleIdCredential.IdentityToken);
                    var authorizationCode = Encoding.UTF8.GetString(appleIdCredential.AuthorizationCode);
                    var firebaseCredential = OAuthProvider.GetCredential(
                        "apple.com",
                        identityToken,
                        rawNonce,
                        authorizationCode);

                    // 파이어베이스와 계정 연동
                    await FirebaseManager.Instance.Auth.SignInWithCredentialAsync(firebaseCredential);

                    // 처음 Apple로 로그인시 이름이 있으면 파이어베이스 유저 업데이트
                    if (appleIdCredential.FullName != null) {
                        var userName = appleIdCredential.FullName.ToLocalizedString();

                        var profile = new UserProfile();
                        profile.DisplayName = userName;

                        await FirebaseManager.Instance.Auth.CurrentUser.UpdateUserProfileAsync(profile);
                    }
                } catch (AggregateException ex) {
                    // 로그인 실패 토스트 메세지
                } catch (Exception ex) {
                    // 로그인 실패 토스트 메세지
                } finally {
                    // 로더 끄기
                }
            },
            error => {
                var authorizationErrorCode = error.GetAuthorizationErrorCode();
                switch (authorizationErrorCode) {
                    case AuthorizationErrorCode.Canceled:
                        break;
                    case AuthorizationErrorCode.Unknown:
                    case AuthorizationErrorCode.InvalidResponse:
                    case AuthorizationErrorCode.NotHandled:
                    case AuthorizationErrorCode.Failed:
                        // 로그인 실패 토스트 메세지
                        break;
                }
                // 로더 끄기
            });
    }

    private string GenerateRandomString(int length) {
        const string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._";
        var cryptographicallySecureRandomNumberGenerator = new RNGCryptoServiceProvider();
        var result = string.Empty;
        var remainingLength = length;

        var randomNumberHolder = new byte[1];
        while (remainingLength > 0) {
            var randomNumbers = new List<int>(16);
            for (var randomNumberCount = 0; randomNumberCount < 16; randomNumberCount++) {
                cryptographicallySecureRandomNumberGenerator.GetBytes(randomNumberHolder);
                randomNumbers.Add(randomNumberHolder[0]);
            }

            for (var randomNumberIndex = 0; randomNumberIndex < randomNumbers.Count; randomNumberIndex++) {
                if (remainingLength == 0) {
                    break;
                }

                var randomNumber = randomNumbers[randomNumberIndex];
                if (randomNumber < charset.Length) {
                    result += charset[randomNumber];
                    remainingLength--;
                }
            }
        }

        return result;
    }

    private string GenerateSHA256NonceFromRawNonce(string rawNonce) {
        var sha = new SHA256Managed();
        var utf8RawNonce = Encoding.UTF8.GetBytes(rawNonce);
        var hash = sha.ComputeHash(utf8RawNonce);

        var result = string.Empty;
        for (var i = 0; i < hash.Length; i++) {
            result += hash[i].ToString("x2");
        }

        return result;
    }
}

위 코드는 사용중인 코드에서 Apple로 로그인 부분만 가져온 코드라서 그대로 사용하면 컴파일 에러가 발생할 수 있습니다.

추가 내용


플러그인을 사용하니 Apple로 로그인 구현은 생각보다 어렵지 않았다.
Quick login도 있었지만 테스트 과정에서 Quick login은 필요하지 않다고 판단해서 구현하지 않았다.
Apple로 로그인은 있지만 로그아웃은 개발자가 구현할 수 없었다. Apple ID 사용 중단은 유저가 설정에서 Apple ID를 사용하지 않을 앱을 정해 로그아웃해야한다.
그리고 유저의 FullName은 처음으로 Apple로 로그인 할 때에만 가져오고 나머지는 null 값을 가져온다.
만약에 유저가 이메일 숨기기를 통해 Apple로 로그인을 한다면 유저의 이메일은 <unique-alphanumeric-string>@privaterelay.appleid.com과 같은 형식을 따르게 된다.

참고 자료


Sign in with Apple | Tools | Unity Asset Store

lupidan/apple-signin-unity

Authenticate Using Apple and Unity | Firebase

lupidan/apple-signin-unity

[Unity] Apple Login과 Firebase 인증

profile
유니티 개발을 조금씩 해왔습니다.

1개의 댓글

comment-user-thumbnail
2023년 2월 14일

안녕하세요. 찾아헤메이다 발견하게 되었습니다. 다름이 아니라 제가 유니티로 ios앱을 만들려고 하는데요. ios에서 구글로그인과 애플로그인 기능을 사용하고자 하였으나, 엄청 오래 찾아도 방법을 도저히 모르겠더라고요...구글로그인은 안드로이드 빌드 기준으로만 대부분 나와있어서 ios용으로는 어떻게 하는지 도저히 모르겠습니다. 이것만 벌써 3주째 붙잡고 있는데요...혹시 괜찮으시다면 관련 문서라도 링크를 걸어주실 수 있으신가요?...정말 미칠것같네요ㅠㅠ

답글 달기