본문 바로가기

✏️/iOS&Swift

[iOS] 내 프로젝트에 파이어베이스를 이용해서 Apple Login 붙이기

728x90

 

이전 글의 이어서,
오늘은 애플 로그인을 붙여보고 정리한 글을 포스팅 해보겠다.

 

 

[iOS] 내 프로젝트에 파이어베이스를 이용해서 구글로그인 붙이기 (GoogleSignIn v. 6.xx)

Demo 오늘은 구글 로그인 연결과 관련해서 만들어본 내용을 정리해본다. (서버 없이) 파이어베이스를 이용해서 구글 로그인을 하고자 한다면, 파이어베이스 콘솔을 등록하고, 1. GoogleService-Info.plis

doitduri.me


Demo

 

 

애플 로그인도 파이어베이스에서 OAuth 기반으로 로그인을 지원하고 있다.

그렇기 때문에, 저번 구글 로그인 때처럼 파이어베이스를 이용하면 비교적 간단하게 구현이 가능하지만, 애플 로그인은 좀 까다롭게(?) 몇 가직 더 설정 해줘야하는 부분이 있다.

(( 코딩하는 부분보다 이거 설정하는게 더 많다 ㅋㅋㅋ)

 

Signing 추가 및 Apple Login 설정

프로젝트에 애플 로그인을 추가하고자 한다면, 사이닝을 추가해야한다.

사이닝의 경우 Capability에 간단하게 추가가 가능하다.

 

추가를 했으면, 그 다음  apple developer  > certificates .. > identifiers 탭으로 이동하여
로그인을 위한 전반적인 셋팅을 해주자.

 

** serviceID : Apple login과 같은 서비스를 등록 할 때 필요한 키

 

identifier는 알아보기 쉽고, 고유해야한다. (보통 번들ID.service를 사용한다고 한다.)

꼭!
- description : 알아보기 쉬운 걸로
- identifier : 고유 값이면 되는데, 보통 project bundle ID 로 함

 

 

여기까지 완료하면, Identifiers에 추가된 것을 확인 할 수 있다.

생성 완료~~~

 

생성된 identifier를 누르고, SIgn In with Apple 을 포함해준다. (체크박스 확인!)

Configure 창

 

그러면 OAuth를 사용하기 위한 승인된 도메인을 입력하라고 한다.
우리는 이번에 파이어베이스를 통해서 OAuth 인증을 해주고 있으니, 빈 칸을 채우기 위해 파이어베이스 콘솔로 가주자.

 

 

Firebase Console

구글 설정한 것처럼, 애플 로그인도 활성해준다.

 

Authentication 페이지로 가면, 승인된 도메인을 확인 할 수 있다.

이 값을 복사해서 아까 configure에 붙여넣기 하면 끝!

 

 

이렇게 하면 애플 로그인을 위한 셋팅은 끝난다.
이제 다시 프로젝트로 돌아와 애플 로그인을 코드로 설정해주자!

 

애플 로그인을 하고 있는 LoginViewController에 라이브러리를 산뜻하게 import하고 시작해보자.

import AuthenticationServices // apple login 관련 라이브러리
import CryptoKit // 해시 값 추가

 

애플의 경우, 로그인하는 사용자의 데이터를 익명처리 할 수 있는 옵션을 제공하고 있다. 그래서, 이를 익명처리 하는 과정과 애플 인증까지 함께 확인해야한다.

애플 로그인과 관련해서는, 파이어베이스 문서에서 자세히 나와있다. 읽어보고 필요한 코드를 가져다 써볼 예정이다.

 

 

애플 로그인을 연결시켜줄 액션에 startSignInWithAppleFlow() 메소드를 연결해주자.

    @IBAction func tapAppleLoginButton(_ sender: UIButton) {
        startSignInWithAppleFlow()
    }

 

그리고, 다음의 코드를 통해 인증을 요청해보자.

extension LoginViewController {
    func startSignInWithAppleFlow() {
        let nonce = randomNonceString()
        currentNonce = nonce
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        let request = appleIDProvider.createRequest()
        // request 요청을 했을 때 none가 포함되어서 릴레이 공격을 방지
        // 추후 파베에서도 무결성 확인을 할 수 있게끔 함
        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
    }
}

 

로그인 요청 할 때마다 nonce를 포함해준다. 이는 릴레이 공격을 방지하기 위해 사용되는데,  앱의 인증 요청에 보안을 위해(?) nonce로 해싱하여 전달해주자.

이 값은, 파이어베이스로 인증 정보 값을 전달하게 되고, 정상적으로 인증을 인가 받으면, 파이어베이스를 통해 로그인을 시도해준다.

 

extension LoginViewController: ASAuthorizationControllerDelegate {
    
    // controller로 인증 정보 값을 받게 되면은, idToken 값을 받음
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
            
            // nonce : 암호화된 임의의 난수, 단 한번만 사용 가능
            // 동일한 요청을 짧은 시간에 여러번 보내는 릴레이 공격 방지
            // 정보 탈취 없이 안전하게 인증 정보 전달을 위한 안전장치
            
            guard let nonce = currentNonce else {
                fatalError("Invalid state: A login callback was received, but no login request was sent.")
                // 안전하게 인증 정보를 전달하기 위해 nonce 사용
            
            }
            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
            }
            
            // token들로 credential을 구성해서 auth signin 구성 (google과 동일)
            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)
            }
        }
    }
}

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

 

 

이렇게 애플 로그인에 대한 인증 연결 작업을 완료하였다.
나중에 앱 서비스를 개발하면서 OAuth로 인증 받고 회원가입 한 회원들의 정보를 가져오는 등 할 떄 한번 더 다시 공부해야겠다.

 

 

--

https://github.com/doitduri/UIKit-Training/tree/main/SopotifyLoginSampleApp

 

GitHub - doitduri/UIKit-Training: UIKit 관련 공부를 정리한 레포 (기초부터 ~)

UIKit 관련 공부를 정리한 레포 (기초부터 ~). Contribute to doitduri/UIKit-Training development by creating an account on GitHub.

github.com

https://firebase.google.com/docs/auth/ios/apple

 

iOS에서 Apple을 통해 인증  |  Firebase

Google은 흑인 공동체를 위한 인종적 평등을 추구하기 위해 노력하고 있습니다. 자세히 알아보기 의견 보내기 iOS에서 Apple을 통해 인증 Firebase SDK를 통해 엔드 투 엔드 OAuth 2.0 로그인 과정을 실행

firebase.google.com