ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • iOS 애플 로그인
    Language/iOS,AOS 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>

     

    짝짝짝

     

    댓글

Designed by Tistory.