๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐ŸŽ iOS/iOS Programming

[iOS, swift] ์†Œ์…œ ๋กœ๊ทธ์ธ(social login) ๊ตฌํ˜„ํ•˜๊ธฐ - ์• ํ”Œ(Apple) ๊ณ„์ •

728x90
์†Œ์…œ ๋กœ๊ทธ์ธ - ์• ํ”Œ ๋กœ๊ทธ์ธ(Sign in with Apple)์„ ๊ตฌํ˜„ํ•ด๋ณด์ž.

 

 

 

 

์š”์ฆ˜ ์–ดํ”Œ์—์„œ๋Š” ํ•„์ˆ˜๋กœ ๋“ค์–ด๊ฐ€๋Š” ์†Œ์…œ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•œ๋‹ค๋ฉด, ์• ํ”Œ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์• ํ”Œ์€ 2019๋…„ ์• ํ”Œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๋ฐœํ‘œ์™€ ๋™์‹œ์— ์•ฑ ๋‚ด์— ๋‹ค๋ฅธ ์†Œ์…œ ๋กœ๊ทธ์ธ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉ ์‹œ ํ•„์ˆ˜๋กœ ์• ํ”Œ ๋กœ๊ทธ์ธ์„ ์ œ๊ณตํ•ด์•ผ ํ•œ๋‹ค๋Š” ์‹ฌ์‚ฌ์ง€์นจ๋„ ํ•จ๊ป˜ ๋‚ด๋†“์•˜๊ธฐ ๋•Œ๋ฌธ!!

 

Apple๋กœ ๋กœ๊ทธ์ธ์— ๋Œ€ํ•œ ์‹ ๊ทœ ๊ฐ€์ด๋“œ๋ผ์ธ - ๋‰ด์Šค - Apple Developer

์‚ฌ์šฉ์ž์˜ Apple ID๋กœ ์•ฑ๊ณผ ์›น์‚ฌ์ดํŠธ์— ๋กœ๊ทธ์ธํ•˜๋„๋ก ํ•˜์—ฌ ๋กœ๊ทธ์ธ ๊ณผ์ •์„ ๊ฐ„์†Œํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ ๋ฐ ๋ณด์•ˆ ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ Apple๋กœ ๋กœ๊ทธ์ธ์„ ํ™œ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ๊ณ„์ •์„ ์„ค์ •ํ•˜๊ณ  ๋ฐ”๋กœ ์‹œ

developer.apple.com

๋”ฐ๋ผ์„œ, ์ด์ œ๋Š” ํ•„์ˆ˜๊ฐ€ ๋œ ์• ํ”Œ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ด…์‹œ๋‹ค.

 

*์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์—์„  ์•ˆ๋˜๊ณ , ์‹ค์ œ ์•„์ดํฐ ๊ธฐ๊ธฐ๋กœ ์‹คํ–‰ ๊ฐ€๋Šฅ.

๋กœ๊ทธ์•„์›ƒ์€ ์ง€์›์ด ์•ˆ๋ผ์„œ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•จ.


Step 1. Capability ์ถ”๊ฐ€

์• ํ”Œ ๋กœ๊ทธ์ธ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์‚ฌ์ „ ์„ค์ •์ž…๋‹ˆ๋‹ค.

XCode Project > Signing & Capabilities >  + Capability > Sign In with Apple์„ ๊ฒ€์ƒ‰ ํ›„ ์ถ”๊ฐ€!

๋‹ค๋ฅธ ์†Œ์…œ ๋กœ๊ทธ์ธ ์„œ๋น„์Šค๋“ค์„ ๊ฐœ๋ฐœํ•  ๋•Œ์ฒ˜๋Ÿผ SDK๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋„ ์•Š๊ณ  ๋ณ„๋‹ค๋ฅธ ์„ค์ •๋„ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค.

๋ฐ”๋กœ Capability์— ์•„๋ž˜์™€ ๊ฐ™์ด Sign In with Apple ํ•ญ๋ชฉ์ด ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.


Step 2.  ์• ํ”Œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ์ถ”๊ฐ€

Apple ๋กœ๊ทธ์ธ Action์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.


2-1 AuthenticationServices import

๊ทธ์ „์— ์‚ฌ์šฉํ•  ํด๋ž˜์Šค์— Apple ๋กœ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ์ธ AuthenticationServices ์•„๋ž˜์™€ ๊ฐ™์ด ์ž„ํฌํŠธ ํ•ด์ฃผ์„ธ์š”.

import AuthenticationServices

 


2-2. ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding ์ƒ์†

๋จผ์ € ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding ์ƒ์†๋ฐ›์Šต๋‹ˆ๋‹ค.

์ €๋Š” extension์œผ๋กœ ํ•ด์คฌ์–ด์š”.

์ƒ์†๋ฐ›์œผ๋ฉด ํ•„์ˆ˜๋กœ presentationAnchor ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด์ค๋‹ˆ๋‹ค.

>> presentationAnchor : ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ Apple ๋กœ๊ทธ์ธ์„ ๋ชจ๋‹ฌ ์‹œํŠธ๋กœ ํ‘œ์‹œํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ, ์—ฐ๋™ ์„ฑ๊ณต ์‹œ ๋ฐ›์•„์˜ฌ ์ •๋ณด๋ฅผ ์ฒ˜๋ฆฌํ•ด์ค๋‹ˆ๋‹ค. ์ œ๊ณต๋ฐ›๋Š” ์ •๋ณด๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

user - ์œ ์ €๊ณ ์œ  ์‹๋ณ„์ž
fullName - ์œ ์ €์ด๋ฆ„
email - ์œ ์ €์ด๋ฉ”์ผ
authorizationCode - ์ธ์ฆ์ฝ”๋“œ
identityToken - ์œ ์ €์˜ ํ† ํฐ >> ์œ ์ €๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ณ ์œ ํ•œ ๊ฐ’์œผ๋กœ ํ† ํฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ•„์š”ํ•œ ๊ฐ’!
extension SignInAppleVC: ASAuthorizationControllerPresentationContextProviding, ASAuthorizationControllerDelegate{
    
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        return self.view.window!
    }
    
    // Apple ID ์—ฐ๋™ ์„ฑ๊ณต ์‹œ
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        switch authorization.credential {
            // Apple ID
        case let appleIDCredential as ASAuthorizationAppleIDCredential:
            
            // ๊ณ„์ • ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
            let userIdentifier = appleIDCredential.user
            let fullName = appleIDCredential.fullName
            let email = appleIDCredential.email
            let idToken = appleIDCredential.identityToken!
            let tokeStr = String(data: idToken, encoding: .utf8)
         
            print("User ID : \(userIdentifier)")
            print("User Email : \(email ?? "")")
            print("User Name : \((fullName?.givenName ?? "") + (fullName?.familyName ?? ""))")
            print("token : \(String(describing: tokeStr))")
            
        default:
            break
        }
    }
    
    // Apple ID ์—ฐ๋™ ์‹คํŒจ ์‹œ
    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        // Handle error.
    }
}

 

์ตœ์ดˆ ๋กœ๊ทธ์ธ ์ดํ›„์—๋Š” email๊ฐ’์ด ์ž๊พธ nil์ด ๋‚˜์˜ค๋Š”๋ฐ, ์ด๋ฏธ ๊ฐ€์ž…์ด ๋๊ธฐ ๋•Œ๋ฌธ์— email๊ฐ’์ด nil์ด ๋‚˜์˜ต๋‹ˆ๋‹ค.

์•ฑ์˜ ์• ํ”Œ ์•„์ด๋”” ์—ฐ๋™์„ ์‚ญ์ œํ•˜๊ณ  ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•˜๋ฉด ๋‹ค์‹œ ๋‚˜์˜ต๋‹ˆ๋‹ค.

*[์—ฐ๋™ ์‚ญ์ œ]

์„ค์ • > ๊ณ„์ • > ์•”ํ˜ธ > ์• ํ”Œ ์•„์ด๋”” ์‚ฌ์šฉ ์•ฑ > ํ•ด๋‹น ์•ฑ > ์•„์ด๋”” ์‚ฌ์šฉ ์ค‘๋‹จ

 


2-3. Request

๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์ˆ˜ํ–‰ํ•  ๋กœ์ง์€ ๋จผ์ € request ์ƒ์„ฑ ํ›„ request๋ฅผ ๋ณด๋‚ด์ค„ controller๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  performRequest()๋กœ ์š”์ฒญ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ๋งค์šฐ ๊ฐ„๋‹จ ^^

class SignInAppleVC: UIViewController {
//์• ํ”Œ ์†Œ์…œ ๋กœ๊ทธ์ธ
    @IBAction func appleLogin(_ sender: Any) {
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        let request = appleIDProvider.createRequest()
        request.requestedScopes = [.fullName, .email]
        
        let authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController.delegate = self
        authorizationController.presentationContextProvider = self
        authorizationController.performRequests()
    }
}

[์‹คํ–‰ ํ™”๋ฉด]

[์„ฑ๊ณต ๊ฒฐ๊ณผ]


+ ์• ํ”Œ ์ œ๊ณตํ•˜๋Š” ๋ฒ„ํŠผ ์‚ฌ์šฉํ•˜๊ธฐ(์˜ต์…˜)

์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ ,

let appleButton = ASAuthorizationAppleIDButton(type: .signIn, style: .whiteOutline)

์—์„œ ํƒ€์ž…์ด๋ž‘ ์Šคํƒ€์ผ์„ ๋ณ€๊ฒฝํ•ด์„œ ์• ํ”Œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

    func setupProviderLoginView() {
        let appleButton = ASAuthorizationAppleIDButton(type: .signIn, style: .whiteOutline)
        appleButton.addTarget(self, action: #selector(handleAuthorizationAppleIDButtonPress), for: .touchUpInside)
        self.view.addSubview(appleButton)
        appleButton.translatesAutoresizingMaskIntoConstraints = false
        appleButton.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
        appleButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
        appleButton.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
        appleButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
        
    }
    @objc func handleAuthorizationAppleIDButtonPress() {
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        let request = appleIDProvider.createRequest()
        request.requestedScopes = [.fullName, .email]
        let authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController.delegate = self
        authorizationController.presentationContextProvider = self
        authorizationController.performRequests()
    }

Step 3.  ์• ํ”Œ ๋กœ๊ทธ์ธ ์ƒํƒœ ํ™•์ธ

 

๋‹ค์Œ์€ ์•ฑ ์‹คํ–‰ ์‹œ ์• ํ”Œ ๋กœ๊ทธ์ธ ์ƒํƒœ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด, AppDelegate์—์„œ application(_:didFinishLaunchingWithOptions:)์— ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

 

[์• ํ”Œ ๋กœ๊ทธ์ธ ์ƒํƒœ๊ฐ’]

  - authorized : ํ•ด๋‹น User Identifier ๊ฐ’์ด ์•ฑ๊ณผ ์—ฐ๊ฒฐ์ด ํ—ˆ๊ฐ€๋˜์–ด์žˆ๋‹ค.

  - revoked : ํ•ด๋‹น User Identifier ๊ฐ’์ด ์•ฑ๊ณผ ์—ฐ๊ฒฐ์ด ์ทจ์†Œ๋˜์–ด์žˆ๋‹ค.

  - notFound : ํ•ด๋‹น User Identifier ๊ฐ’์ด ์•ฑ๊ณผ ์—ฐ๊ฒฐ์„ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค.

//applelogin ์ƒํƒœ ํ™•์ธ
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        //forUserID = userIdentifier
        appleIDProvider.getCredentialState(forUserID: "001281.9301aaa1f617423c9c7a64b671b6eb84.0758") { (credentialState, error) in
            switch credentialState {
            case .authorized:
                // The Apple ID credential is valid.
                print("ํ•ด๋‹น ID๋Š” ์—ฐ๋™๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.")
            case .revoked:
                // The Apple ID credential is either revoked or was not found, so show the sign-in UI.
                print("ํ•ด๋‹น ID๋Š” ์—ฐ๋™๋˜์–ด์žˆ์ง€์•Š์Šต๋‹ˆ๋‹ค.")
            case .notFound:
                // The Apple ID credential is either was not found, so show the sign-in UI.
                print("ํ•ด๋‹น ID๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
            default:
                break
            }
        }
		//์•ฑ ์‹คํ–‰ ์ค‘ ๊ฐ•์ œ๋กœ ์—ฐ๊ฒฐ ์ทจ์†Œ ์‹œ
        NotificationCenter.default.addObserver(forName: ASAuthorizationAppleIDProvider.credentialRevokedNotification, object: nil, queue: nil) { (Notification) in
            print("Revoked Notification")
            // ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™
        } 
	return true
}

 

[์‹คํ–‰ ํ™”๋ฉด]

๋ฐ˜์‘ํ˜•