Language/iOS,AOS

iOS 애플 로그인

건담아빠 2023. 4. 11. 18:53

 

웹뷰를 사용하는 환경에서 웹 소스에서 bridge를 통해 iOS에서 로그인 인증을 받는 부분을 정리하려고 합니다.

 

1. 애플 개발자 사이트 설정

https://developer.apple.com/

 

Apple Developer

There’s never been a better time to develop for Apple platforms.

developer.apple.com

 

아래와 같이 Account -> Identifiers -> Sign In with Apple 추가 후 프로비저닝 프로파일 다시 내려받아서 적용합니다.

Account -> Identifiers -> Sign In with Apple

 

2. Capablility 추가

`+` 를 눌러서 Sign In with Apple를 추가합니다.

Sign In with Apple

 

3. 코드

3.1. Swift 코드

import AuthenticationServices

...

// MARK: - iOS Bridge
// 웹 액션 정의 : WebAction을 구분하는데 사용되는 타입
enum WebAction: String {
    ...
    case loginWithKakao
    case loginWithNaver
    case loginWithFacebook
    case loginWithApple
    ...
}

...

extension MainViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        guard message.name == IOS_BRIDGE_NAME,
              let messages = message.body as? [String: Any],
              let action = messages["action"] as? String else { return }
        
        let webAction = WebAction(rawValue: action)
        
        switch webAction {
        ...
        case .loginWithApple:
            guard let params: [String: Any] = messages["params"] as? [String: Any],
                  let callback: String = params["callback"] as? String else {
                return
            }

            loginWithApple(callback: callback)
            
        ...
        
        default:
            Utils.Log("undefined action")
        }
    }
}

...

// MARK: - 애플 로그인 - 확장
extension MainViewController: ASAuthorizationControllerDelegate {
    /**
     * 로그인 및 체크
     */
    func loginWithApple(callback: String) -> Void {
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        let request = appleIDProvider.createRequest()
        request.requestedScopes = [.fullName, .email]

        let authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController.delegate = self
//        authorizationController.presentationContextProvider = self
        authorizationController.performRequests()
    }

    // 성공 후 동작
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        
        if let credential = authorization.credential as? ASAuthorizationAppleIDCredential {
            let idToken = credential.identityToken!
            
            guard let idTokenStr: String =  String(data: idToken, encoding: .utf8) else {
                return
            }
            
            guard let code = credential.authorizationCode else { return }
            let codeStr = String(data: code, encoding: .utf8)
            let user = credential.user
            let callback = "window.setAccessToken"
            
            self.executeJavasScript(wKWeb: self.getActiveWkWebView(), callback: "\(callback)('\(idTokenStr)')")
        }
    }

    // 실패 후 동작
    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        Utils.Log("##################### didCompleteWithError : \(error)")
    }
}

 

3.2. 일반웹

const loginByWeb = function () {
    ...
}

sessionStorage.setItem(Common.POST_LOGIN_URI, location.pathname + location.search);

if (Util.Browser.isAndroidApp()) {
    ...
} else if (Util.Browser.isIosApp()) {
    window.setAccessToken = function (idToken) {
        if (idToken === 'cancel') {
        } else if (idToken === 'error') {
            loginByWeb();
        } else if (idToken) {
            Common.login({
                SNS_TYPE_CD: 'SNSTP00004',
                id_token: encodeURIComponent(idToken)
            })
        } else {
            loginByWeb();
        }
    }

    const message = {
        action: 'loginWithApple',
        params: {callback: 'window.setAccessToken'}
    };
    window.webkit.messageHandlers.IosBridge1.postMessage(message);
} else {
    loginByWeb();
}

 

3.3. 리엑트

export const useLogin = () => {
  const context = useContext(BaseContext);

  const kakao = () => {
    ...
  };

  const apple = () => {
    ...

    const loginByWeb = function () {
      ...
    };

    if (Util.Browser.isIosApp()) {
      window.setAccessToken = async function (idToken) {
        context.showLoading(false);
        if (idToken === 'cancel') {
        } else if (idToken === 'error') {
          loginByWeb();
        } else if (idToken) {
          await Common.login({ SNS_TYPE_CD: 'SNSTP00004', id_token: encodeURIComponent(idToken) });
        } else {
          loginByWeb();
        }
        context.showLoading(false);
      };

      try {
        const message = {
          action: 'loginWithApple',
          params: { callback: 'window.setAccessToken' },
        };
        window.webkit.messageHandlers.IosBridge1.postMessage(message);
      } catch (e) {
        loginByWeb();
      }
    } else {
      setTimeout(() => {
        loginByWeb();
      }, 500);
    }
    ...
  };

  return { kakao, apple };
};



<a
    className={cx('btn_apple')}
    onClick={() => {
      props.onClose();
      login.apple();
    }}
>
    Apple로 계속하기
</a>

 

짝짝짝