안녕하세요. iOS 개발자 도미닉입니다.

오늘은 페이워치코리아에서 생체 인증을 처음으로 도입해 본 경험을 기록으로 남겨보고자 합니다.

페이워치코리아에 처음 입사해서 iOS 쪽 일을 찾던 와중에 생체 인증이 필요하다는 것을 알게 되었습니다.

저희 앱에 인출하기 버튼을 누르면 패스워드를 입력하는데요.

이 때 비밀번호 입력을 생체 인증으로 대신하면 어떨까? 하는 사항이였습니다.

생체 인증...? 사용은 해봤는데...

생체 인증은 편하게 금융 앱 등에서 사용했었습니다.

하지만 구현을 해보려고 하니 알고 있는 내용들이 많이 없었습니다.

위처럼 궁금한 점이 많았습니다.

먼저 간단히 생체 인증 예제 코드를 작성해봤습니다.

//
//  ViewController.swift
//  ExFaceTouchId
//
//
import UIKit
import LocalAuthentication

class ViewController: UIViewController {
    @IBOutlet weak var loginButton: UIButton!
    enum AuthenticationState {
        case loggedin, loggedout
    }

    // ✅ 현재 로그인 상태에 따른 UI 변화
    var state = AuthenticationState.loggedout {
        didSet {
            if state == .loggedin {
                loginButton.setTitle("Logout", for: .normal)
            } else {
                loginButton.setTitle("Login", for: .normal)
            }
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        
    }

    @IBAction func didTapButton(_ sender: Any) {
        if state == .loggedin {
            state = .loggedout
        } else {
            // ✅ LAContext 로 Secure Enclave 와 앱과의 상호작용을 중개.
            let context = LAContext()
            
            // ✅ alert view 에서 cancel button 메시지.
            context.localizedCancelTitle = "Enter Username/Password"
            
            var error: NSError?
            
            // ✅ 지정한 policy 로 biometrics 인증이 가능한지 테스트.
            if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
                
                let reason = "Log in to your account"
                
                // ✅ 지정한 policy 로 biometrics 인증 시작.
                context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason ) { success, error in
                    if success {
                        // ✅ state 업데이트는 UI 변화를 일으켜서 main thread 에서 처리해야한다.
                        DispatchQueue.main.async { [unowned self] in
                            self.state = .loggedin
                            print("state : \(self.state)")
                        }
                    } else {
                        print(error?.localizedDescription ?? "Failed to authenticate")
                        
                        // Fall back to a asking for username and password.
                        // ...
                    }
                }
            }
        }
    }
}

이 코드에서 생체 인증을 시도해보고 성공, 실패에 따른 분기 처리를 확인할 수 있었습니다.

아래처럼 의문을 해결해가면서 기획된 내용들을 대부분 구현할 수 있었습니다.

모바일에 생체 정보가 등록되어 있는지 여부를 알 수 있을까?

참고에 첫번째 링크에 있는 에러 메시지들을 통해 LAError.biometryNotEnrolled 에러일 경우 기기에 등록된 생체 인식 ID가 없다는 것을 알 수 있었습니다.

이 에러가 발생할 경우에는 생체 인증이 진행되지 않고 휴대폰의 "설정" 앱에서 생체 인증을 우선 등록하라는 간단한 오류창을 띄웠습니다.

앱에 생체 인증 등록이 필요할까?

기획에는 앱에 생체 인증을 등록해야 앱 내에서 생체 인증 기능을 사용할 수 있었습니다.

하지만 제가 개발을 하고 예제를 만들어보니 앱을 위한 등록을 하지 않아도 바로 기기에 등록한 생체 인증 정보를 사용할 수 있었습니다.

이 부분은 빼야하나 고민을 하고 의논해보니 다른 핸드폰에서 생체 인증을 하면 재등록을 하도록 해야하기 때문에 앱 내에 생체 인증 등록이 필요했습니다.

생체 데이터는 아니지만 아래 코드로 생체 정보 ID 값을 가져올 수 있습니다.

let context = LAContext()
context.evaluatedPolicyDomainState

이 생체 정보 ID 값을 UserDefaults 로 휴대폰 내부에 저장할 수 있습니다.

또한 DB 에 저장하여 생체 정보가 바뀐 것을 확인할 수 있습니다.

여러 핸드폰에서 각자의 생체 인증 ID로 생체 인증을 진행하면 안 되기 때문에 이 값을 DB 에 저장하도록 API 를 요청하였습니다.

이렇게 DB 에 저장하는 것을 앱에 생체 인증 등록하는 것으로 처리하였습니다.

생체 인증 시도 횟수 초과 시 처리를 컨트롤할 수 있을까?

별도로 인증 시도에 실패했을 때 어떤 코드를 실행하도록 컨트롤 할 수는 없는 것으로 파악했습니다.

예제 코드에서는 생체 인증에 여러 번 실패 시 아이폰의 비밀번호로 생체 인증을 대신할 수 있었습니다.

아이폰을 취득해서 아이폰의 비밀번호만 알고 있는 사람이 저희 서비스에 비밀번호를 모르고도 진행을 할 수 있었습니다.

이러면 안되기 때문에 LAContext 의 정책을 deviceOwnerAuthentication 가 아닌 deviceOwnerAuthenticationWithBiometrics 로 변경하여 기기의 비밀번호를 알더라도 생체 인증을 하지 않으면 진행할 수 없도록 막았습니다.

생체 인증 정보가 변경된 것을 캐치할 수 있을까?

만약 사용자의 휴대폰을 누군가 주워서 생체 인증에 자신의 지문이나 FaceID 를 추가하게 되서 우리 서비스의 인증을 진행하게 되면 안될 것입니다.

생체 인증 정보가 변경된 것을 캐치해서 생체 인증을 못하게 막고 재등록을 해야만 사용할 수 있도록 구성을 하였습니다.

생체 인증 정보가 바뀐 것은 생체 인증을 진행하기 전에 위에 등록처럼 context.evaluatedPolicyDomainState 값을 활용합니다.

이 값은 생체에 대한 정보가 담긴 데이터는 아니며 생체의 고유한 ID 값으로 이해했으며 생체 정보가 바뀐다면 이 값이 바뀌게 되었습니다.

이 값을 DB 에 저장하고 조회하여 현재 값과 다르다면 생체 인증이 진행되지 않게 막았습니다.

위에 질문과 조사한 내용들로 저희가 원하는 대로 생체 인증을 구성할 수 있었습니다.

생체 인증이란?

진행한 내용들을 통해서 생체 인증을 정리해보고자 합니다.

생체 인증을 사용하고자 한다면 우선 LocalAuthentication 을 import 해야합니다.

import LocalAuthentication

그 뒤에 LAContext 라는 클래스를 생성하고 사용하며 생체 인증을 개발할 수 있습니다.

let context = LAContext()

기존에 저장된 생체 ID 와 다른 것을 체크하기 위해서 아래처럼 context.evaluatedPolicyDomainState 값을 사용할 수 있었습니다.

let biometricsData = context.evaluatedPolicyDomainState

생체 인증 alert view 에서 cancel button 메시지를 아래처럼 작성할 수 있습니다.

context.localizedCancelTitle = "취소"

그러고는 보통 아래처럼 지정한 policy(위에서 적어놓았던 deviceOwnerAuthentication 와 deviceOwnerAuthenticationWithBiometrics 중 선택)에서 인증이 가능한지 확인합니다.

if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {

if 문 안으로 이동되어서 인증이 가능하다면 지정한 policy로 생체 인증을 진행합니다.

context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason ) { success, error in
	if success {
    	// 생체 인증에 성공했을 때의 동작
    } else {
       	// 실패했을 때의 동작
    }
}

정리

이렇게 해서 생체 인증 기능을 저희 앱에 추가하여 어제 배포 완료하였습니다.

저희 회사에서는 여러 공고를 통해 개발자 분들을 모집하고 있습니다.

https://www.wanted.co.kr/company/11912

위 링크로 많은 지원 부탁드립니다.

질문이 있거나 잘못된 점 있으면 편하게 댓글 달아주세요.

이상으로 긴 글 읽어주셔서 감사합니다.

참고

https://kkh0977.tistory.com/1608

https://dev.classmethod.jp/articles/ios-biometrics-authentication-kr/

https://dev.classmethod.jp/articles/ios-biometry-authentication-domain-change-kr/

profile
페이워치코리아 개발블로그

2개의 댓글

comment-user-thumbnail
2022년 9월 20일

저도 해당 글 참고해서 생체인증 사용해서 개발할수있겠네요 ㅎㅎ
유익한 글 작성해주셔서 감사합니다. 다음 글도 기대할게요 도미닉 개발자님~

1개의 답글