본문 바로가기

react-native

[RN] react-native 생체인증 구현, biometrics와 touch-id

앱의 지문, FACE ID 로그인을 위해 생체인증을 적용 했다.

react-native-touch-id로 작업을 했었지만 react-native-biometrics도 있어 2개를 같이 구현했다.

react-native-touch-id는 업데이트가 된지 오래 됐다고 하는데 별 차이 없어 보임..

 


● 설치 및 권한 설정

npm install

npm install --save react-native-biometrics
npm install --save react-native-touch-id

Android(AndroidManifest.xml)

<uses-permission android:name="android.permission.USE_FINGERPRINT" />

IOS(info.plist)

<key>NSFaceIDUsageDescription</key>
<string>RoyalFund Authentication with TouchId or FaceID </string>

 

● react-native-biometrics

Authentication.js

import ReactNativeBiometrics, {BiometryTypes} from 'react-native-biometrics';
import * as keyStore from '/lib/secure-key-store';

const rnBiometrics = new ReactNativeBiometrics();

//생체 인식 가능 여부
export const booleanBiometricsCheck = () => {
    return rnBiometrics
        .isSensorAvailable()
        .then(resultObject => {
            const {available, biometryType, error} = resultObject;

            if(!available){ // 인식실패(lock)
                console.log('isSensorAvailable error', error);
            }else if(available && biometryType === BiometryTypes.TouchID){
                return {result: true, type: biometryType};
            }else if(available && biometryType === BiometryTypes.FaceID){
                return {result: true, type: biometryType};
            }else if(available && biometryType === BiometryTypes.Biometrics){
                return {result: true, type: biometryType};
            }else{
                
                return {result: false, type: null};
            }
        })
        .catch(error => {
            console.log('booleanBiometricsCheck error ', error);
            return {result: false, type: null};
        });
};

//키생성
export const createKey = () => {
    return rnBiometrics
        .createKeys()
        .then(resultObject => {
            const {publicKey} = resultObject;
            return {result: true, key: publicKey};
        })
        .catch(error => {
            console.log('createKey error ', error);
            return {result: false, key: null};    
        });
};

//키존재여부
export const checkKey = () => {
    return rnBiometrics
        .biometricKeysExist()
        .then(resultObject => {
            const {keysExist} = resultObject;
            if(keysExist){
                return true;
            }else{
                return false;
            }
        })
        .catch(error => {
            console.log('checkKey error ', error);
            return false;
        });
}

export const deleteKey = () => {
    return rnBiometrics
        .deleteKeys()
        .then(resultObject => {
            const {keysDeleted} = resultObject;

            if(keysDeleted){
                return true;
            }else{
                return false;
            }
        })
        .catch(error => {
            console.log('deleteKey error ', error);
            return false;
        });
};

//값 확인
export const biometricsLogin = (
    userID: string = '',
    msg: string = '로그인',
) => {
    return rnBiometrics
        .createSignature({
            promptMessage: msg,
            payload: userID,
        })
        .then(resultObject => {
            const { success, signature} = resultObject;
            if(success){
                keyStore.setItem('biometricsKey', signature);
                return {result: true, key: signature};
            }else{
                return {result: false, key: null};
            }
        })
        .catch(error => {
            console.log('biometricsLogin error ', error);
            return {result: false, key: null, msg: error};
        });
};

 

secure-key-store.js는 아래 참고

2023.08.09 - [react-native] - [RN] react-native-keychain 구현

 

Home.js

import { Alert } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import * as authentication from "/lib/Authentication";

function Home (props){
    registerBiometrics = async() => {
      const token = await messaging().getToken();

      const keyCreate = await authentication.createKey();
      if(keyCreate?.result){
        const biometricsCheck = await authentication.booleanBiometricsCheck();

        if(biometricsCheck?.result){
          const bioKey = await authentication.biometricsLogin('유저아이디' + token, '등록');

          //성공 로직
          if(bioKey?.key){
            Alert.alert('알림', '성공');
            console.log('bioKey Success');
          }else{
            Alert.alert(
              '알림',
              '생체인식에 실패했습니다.'
            );
          }
        }
      }else{
        Alert.alert('알림', '생체인식을 사용할 수 없습니다.');
      }
    };
}

 

 

참고 사이트 : http://www.dmonster.co.kr/bbs/board.php?bo_table=b0902&wr_id=11237&sst=wr_hit&sod=asc&sop=and&page=1 

 

디몬스터,앱개발,어플제작,앱제작,어플개발,앱광고,어플광고,홈페이지제작

디몬스터,앱개발,어플제작,앱제작,어플개발,앱광고,어플광고,홈페이지제작,홈페이지제작업체,반응형홈페이지, 070-7621-0572

www.dmonster.co.kr

 


● react-native-touch-id

Authentication.js

import { Linking, Platform, Alert } from 'react-native';
import TouchID from 'react-native-touch-id';
import * as keyStore from '/lib/secure-key-store';

// 생체 인증 등록
export const registerBiometrics = async () => {
    try {
        const optionalConfigObject = {
            unifiedErrors: false, // use unified error messages (default false)
            passcodeFallback: false // if true is passed, itwill allow isSupported to return an error if the device is not enrolled in touch id/face id etc. Otherwise, it will just tell you what method is supported, even if the user is not enrolled.  (default false)
        };

        const isSupported = await TouchID.isSupported(optionalConfigObject)
            .then(biometryType => {
                // Success code
                if (biometryType === 'FaceID') {
                    console.log('FaceID is supported.');
                } else {
                    console.log('TouchID is supported.');
                }

                const registerConfig = {
                    title: '생체 정보 등록',
                    imageColor: '#4388D6',
                    imageErrorColor: '#FF0000',
                    sensorDescription: '지문을 등록해주세요.',
                    sensorErrorDescription: '지문 인식에 실패하였습니다.',
                    cancelText: '취소',
                };
    
                const registerResult = TouchID.authenticate('생체 정보를 등록해주세요.', registerConfig)
                    .then(success => {
                        console.log('생체 정보 확인이 완료되었습니다.');
                        keyStore.setItem('biometricYn', 'Y');
                    })
                    .catch(error => {
                        if (Platform.OS === 'android') {
                            console.log('생체 정보 확인에 실패하였습니다.');
                        }else if (Platform.OS === 'ios') {
                            if(error.name === 'LAErrorAuthenticationFailed' || error.name === 'RCTTouchIDUnknownError'){
                                Alert.alert('생체인증에 실패했습니다. 잠시 후 다시 시도해주세요.');
                            }else{
                                console.log('생체 정보 확인에 실패하였습니다.');
                            }
                        }
                    });
            })
            .catch(error => {
                console.log('error name ::: ' + error.name);
                // Failure code
                if (Platform.OS === 'android') {
                    if(error.code === 'NOT_ENROLLED'){
                        Alert.alert('생체인증이 등록되어 있지 않습니다. 설정으로 이동합니다.', '', [{text: '확인', onPress: () => openSettings() }]);
                    }else{
                        console.log('생체인증을 지원하지 않는 디바이스입니다.');
                    }
                }else if (Platform.OS === 'ios') {
                    if(error.name === 'LAErrorTouchIDNotEnrolled'){
                        Alert.alert('생체인증이 등록되어 있지 않습니다. 설정으로 이동합니다.', '', [{text: '확인', onPress: () => openSettings() }]);
                    }else if(error.name === 'LAErrorAuthenticationFailed' || error.name === 'RCTTouchIDUnknownError'){
                        Alert.alert('생체인증에 실패했습니다. 생체인증 설정 정보를 확인해주세요.', '', [{text: '확인', onPress: () => openSettings() }]);
                    }else{
                        console.log('생체인증을 지원하지 않는 디바이스입니다.');
                    }
                }
            });
    } catch (error) {
        // 오류 처리
        console.log('생체 정보 등록 중 오류가 발생하였습니다:', error);
    }
};

// 디바이스 생체인증 등록화면 이동
const openSettings = () => {
    if (Platform.OS === 'android') {
        Linking.openSettings();
    }else if (Platform.OS === 'ios') {
        Linking.openURL('App-Prefs:root=TOUCHID_PASSCODE');
    }
};

생체인증 인증 횟수 초과로 인한 lock 오류로 디바이스 설정으로 가는 기능까지 구현함.

 

Home.js

import * as authentication from "/lib/Authentication";

function Home (props){
    registerBiometrics = (msgData)=> {
      authentication.registerBiometrics();
    }
}

 

 

참고 사이트 : https://www.npmjs.com/package/react-native-touch-id

 

react-native-touch-id

React Native authentication with the native Touch ID popup.. Latest version: 4.4.1, last published: 4 years ago. Start using react-native-touch-id in your project by running `npm i react-native-touch-id`. There are 20 other projects in the npm registry usi

www.npmjs.com


 

2개 모두 구현을 하고 무엇을 사용할지는 각 프로젝트의 버전, 용도에 맞춰서 고르면 될 것 같다.

내가 느낀 가장 큰 특징은 아래와 같았음.

  • react-native-biometrics - 개인키를 만들어 사용자 인증을 하는데 용이함, 비교적 최근에 나와 최근까지 업데이트 되고 있음.
  • react-native-touch-id     - 공식 문서에 FINGERPRINT_ERROR_LOCKOUT과 같은 오류 코드들이 잘 나와있어 인증 횟수 초과로 인한 LOCK 같은 것도 쉽게 캐치할 수 있음.