Old_SWIFT(221012)/라이브러리이야기

Firebase Auth 다루기 (3/3) (Apple 로그인)

KataRN 2022. 3. 21. 01:45
반응형

안녕하세요. KataRN입니다.

 

드디어 마지막 애플로그인, 로그아웃, 회원정보 수정 입니다.

더보기

애플은 2019년 애플 로그인 기능을 발표했습니다. 동시에 앱 내에 다른 소셜 로그인 서비스를 사용하고 있다면...

반드시! 애플 로그인을 제공해야 한다는 심사지침도 함께 내놓았습니다.

 

FirebaseAuth를 이용하기 위한 사전지식이 필요하신 분들은 아래 링크 참고 부탁드립니다.

구글로그인도 있어요~

 

https://katarnios.tistory.com/45

 

Firebase Auth 다루기(기본, 이메일로그인) (1/2)

안녕하세요 KataRN입니다. 오늘은 Firebase 인증에 대해 설명해드리려고 합니다. Firebase Auth 로 무엇을 할 수 있는가? -> 이것을 이용해서 로그인을 구현 할 수 있습니다. 로그인구현에 필요한것...은

katarnios.tistory.com

https://katarnios.tistory.com/46

 

Firebase Auth 다루기 (2/3) (구글로그인)

안녕하세요. KataRN입니다. 저번에 알아봤던 이메일 로그인 구글로그인을 알아보겠습니다. 우선 저번시간에 알려드림 기본적인 세팅과 이메일 로그인은 아래 링크를 참고해주세요. https://katarnios.

katarnios.tistory.com

 

참고사항

애플개발자 멤버십 가입이 되어있어야 가능합니다.

 

파이어베이스(https://console.firebase.google.com/project/_/authentication/users)에 접속합니다.

이미지대로 따라서 설정해주세요.

 

이제 앱으로 돌아가서 설정을 해줍시다.

 

 

 

이제 애플개발자 페이지로 이동합시다.

위에서 말씀드렸듯이 애플 개발자 멤버십 가입이 되어있어야 진행가능합니다.

 

 

자 이렇게 누르면 3가지를 써줘야합니다.

 

1번은 App ID를 선택.

2번과 3번은 다시 파이어베이스로 가서 가져옵시다.

 

후... 길다..

1. 애플 로그인

이제 앱으로 돌아가 애플로그인을 만들어줍시다.

더보기

잠깐!

애플 로그인버튼 규격 준수해야합니다. 링크 참고해주세요.(https://bebesoft.tistory.com/35)

import AuthenticationServices
import CryptoKit

fileprivate var currentNonce: String?

extension ViewController: ASAuthorizationControllerDelegate {
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {       
            guard let nonce = currentNonce else {
                fatalError("Invalid state: A login callback was received, but no login request was sent.")
            }
            guard let appleIDToken = appleIDCredential.identityToken else {
                print("Unable to fetch identity token")
                return
            }
            guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
                print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
                return
            }
            
            let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce)
            
            Auth.auth().signIn(with: credential) { authResult, error in
                if let error = error {
                    print ("Error Apple sign in: %@", error)
                    return
                }
                // User is signed in to Firebase with Apple.
                // ...
                ///Main 화면으로 보내기
                let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
                let mainViewController = storyboard.instantiateViewController(identifier: "MainViewController")
                mainViewController.modalPresentationStyle = .fullScreen
                self.navigationController?.show(mainViewController, sender: nil)
            }
        }
    }
}

//Apple Sign in
extension ViewController {
    func startSignInWithAppleFlow() {
        let nonce = randomNonceString()
        currentNonce = nonce
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        let request = appleIDProvider.createRequest()
        request.requestedScopes = [.fullName, .email]
        request.nonce = sha256(nonce)
        
        let authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController.delegate = self
        authorizationController.presentationContextProvider = self
        authorizationController.performRequests()
    }
    
    private func sha256(_ input: String) -> String {
        let inputData = Data(input.utf8)
        let hashedData = SHA256.hash(data: inputData)
        let hashString = hashedData.compactMap {
            return String(format: "%02x", $0)
        }.joined()
        
        return hashString
    }
    
    // Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
    private func randomNonceString(length: Int = 32) -> String {
        precondition(length > 0)
        let charset: Array<Character> =
            Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
        var result = ""
        var remainingLength = length
        
        while remainingLength > 0 {
            let randoms: [UInt8] = (0 ..< 16).map { _ in
                var random: UInt8 = 0
                let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
                if errorCode != errSecSuccess {
                    fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)")
                }
                return random
            }
            
            randoms.forEach { random in
                if remainingLength == 0 {
                    return
                }
                
                if random < charset.count {
                    result.append(charset[Int(random)])
                    remainingLength -= 1
                }
            }
        }
        
        return result
    }
}

extension ViewController : ASAuthorizationControllerPresentationContextProviding {
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        return self.view.window!
    }
}

 

????? 갑자기 뭐가 많죠?

 

제가 임의로 만든것들은 아닙니다.

우선 Nonce에 대해 설명드릴게요.

더보기

             Nonce 란?

             - 암호화된 임의의 난수

             - 단 한번만 사용할 수 있는 값

             - 주로 암호화 통신을 할 때 사용

             - 동일한 요청을 짧은 시간에 여러번 보내는 릴레이 공격 방지

             - 정보 탈취 없이 안전하게 인증 정보 전달을 위한 안전장치

randomNonceString()은 암호화된 Nonce를 만듭니다.

startSigInWithAppleFlow()에서 애플ID 인증값을 요청할때 request를 생성해서 전달하는데요.

이때 Nonce를 포함해서 전달합니다.

이걸 통해 나중에 Firebase에서 무결성 검사를합니다.

 

Firebase와 Apple에서 제공하는 가이드라인대로 붙여넣었습니다.

제가 직접 손댄부분이 없다보니... 설명이 부족하죠..

참고링크 : https://auth0.com/docs/get-started/authentication-and-authorization-flow/mitigate-replay-attacks-when-using-the-implicit-flow#generate-a-cryptographically-random-nonce

 

참고로 시뮬레이터로는 작동안합니다.

테스트 기기를 이용해서 빌드해보세요.

 

2. 로그아웃

예외처리 (throws, do-catch, try) 를 이용했습니다.

그리고 Auth.auth().signOut()을 이용하여 로그아웃 처리 했습니다.

let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
  self.navigationController?.popToRootViewController(animated: true)
} catch let signOutError as NSError {
  print ("Error signing out: %@", signOutError)
}

 

3. 회원정보 수정

간단하게 만들기 위해 변하게될 프로필을 코드에 포함시켰습니다.

let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
  changeRequest?.displayName = "KataRN"
  changeRequest?.commitChanges { _ in
  let displayName = Auth.auth().currentUser?.displayName ?? Auth.auth().currentUser?.email ?? ""
  self.welcomeLabel.text = "환영합니다.\n\(displayName)님"
}

 

사실 2, 3번의 내용은 이전글에 같은내용이 있습니다. 참고부탁드립니다.ㅎㅎ

https://katarnios.tistory.com/45

 

Firebase Auth 다루기(기본, 이메일로그인) (1/2)

안녕하세요 KataRN입니다. 오늘은 Firebase 인증에 대해 설명해드리려고 합니다. Firebase Auth 로 무엇을 할 수 있는가? -> 이것을 이용해서 로그인을 구현 할 수 있습니다. 로그인구현에 필요한것...은

katarnios.tistory.com

 

오늘도 긴글 읽어주셔서 감사합니다.

 

 

반응형