ㄷㅣㅆㅣ's Amusement

[iOS/Objective-c] Touch Id 사용하여 인증하기. (passcode도 동작) 본문

Programming/iOS

[iOS/Objective-c] Touch Id 사용하여 인증하기. (passcode도 동작)

ㄷㅣㅆㅣ 2015. 11. 25. 21:04

[iOS/Objective-c] Touch Id 사용하여 인증하기. (passcode도 동작)

오늘은... iOS에서 지문인식을 통한 인증방법에대해 포스팅을 해봅니다.



우선... 지문인식 다이얼로그는 LAContext로 띄울 수 있는데.... 이것을 쓰면 "enter passcode"를 눌렀을 때 "User fallback"에러메시지가 떨어진 후 passcode입력할 수 있는 방법이 없기 때문에.. 나는 이를 우회할 수 있는 다른 방법을 쓰기로 한다.


1. flow

  1) 키체인을 설정하되, 읽을 때 touch id(지문인식)을 요구하도록 설정한다.

  2) 키체인을 읽어들이는 쿼리(query)를 실행한다. 


1) keychain 설정.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
- (void)setKeychainForTouchIdVerify {
    LAContext *context = [[LAContext allocinit];
    NSError *error = nil;
    if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error] == NO) {
        NSLog(@"This device don't support touch id verification.");
        return;
    }
    
    // LAContext만으로 TouchId verify를 할 수는 있으나, 실패시 passcode넣는 동선이 동작하지 않음.
    // 따라서 키체인을 이용하여 모두 지원할 수 있도록 workaround한다.
    
    // TODO : 키체인 설정. 이 부분은 한번만 실행되면 되기 때문에.. 추후 프로젝트 골격이 완성되면 적당한 곳으로 옮겨야 한다.
    
    // 패스워드 컨텐트는 어떤 문자든지 중요하지 않으나 꼭 넣어야 함. (애플 기본사항)
    NSData * pwData = [@"the password itself does not matter" dataUsingEncoding:NSUTF8StringEncoding];
    
    // 키체인 엔트리가 읽히면 touch id나 passcode를 요구하도록 만든다.
    // 사용자가 passcode를 지웠을 때 키체인도 날리는 옵션을 제공하지만, 사용하는 동선이 없을 것으로 판단됨. 필요시 추후 개발
    CFErrorRef accessControlError = NULL;
    SecAccessControlRef accessControlRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
                                                                           kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
                                                                           kSecAccessControlUserPresence,
                                                                           &accessControlError);
    
    if (accessControlRef == NULL || accessControlError != NULL) {
        NSLog(@"Cannot create SecAccessControlRef to store a password with identifier “%@” in the key chain: %@.", keychainItemIdentifier, accessControlError);
        return;
    }
    
    // Create the keychain entry attributes. identifier와 서비스 네임으로 유니크한 키체인 엔트리를 만듬.
    NSDictionary *attributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
                                (__bridge id)(kSecClassGenericPassword), kSecClass,
                                keychainItemIdentifier, kSecAttrAccount,
                                keychainItemServiceName, kSecAttrService,
                                (__bridge id)accessControlRef, (__bridge id)kSecAttrAccessControl,
                                @YES, (__bridge id)kSecUseNoAuthenticationUI,
                                pwData, (__bridge id)kSecValueData, nil];
    
    CFTypeRef result;
    OSStatus osStatus = SecItemAdd((__bridge CFDictionaryRef)attributes, &result);
    if (osStatus != noErr) {
        NSError * error = [[NSError alloc] initWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
        NSLog(@"Adding generic password with identifier “%@” to keychain failed with OSError %d: %@.", keychainItemIdentifier, (int)osStatus, error);
    }
}
 
cs


2) query 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- (void)touchIdVerify:(void (^)(BOOL bVerified))verified {
    LAContext *context = [[LAContext allocinit];
    NSError *error = nil;
    if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error] == NO) {
        NSLog(@"This device don't support touch id verification.");
        return;
    }
    
    // touch id scan UI가 뜨도록 하는부분.
    // touch id scan할 때 타이틀
    NSString * secUseOperationPrompt = @"Authenticate for server login";
    
    // 키체인관련 기능은 global_queue에서 해야한다. (다른 큐에서는 동작 안함)
    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
        NSMutableDictionary * query = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
                                       (__bridge id)(kSecClassGenericPassword), kSecClass,
                                       keychainItemIdentifier, kSecAttrAccount,
                                       keychainItemServiceName, kSecAttrService,
                                       secUseOperationPrompt, kSecUseOperationPrompt,
                                       nil];
        
        // 쿼리를 실행해서 touch id scan 시작
        CFTypeRef result = nil;
        OSStatus userPresenceStatus = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
        if (userPresenceStatus == noErr) {
            NSLog(@"Fingerprint or device passcode validated.");
            verified(YES);
        } else {
            NSLog(@"Fingerprint or device passcode could not be validated. Status %d.", (int) userPresenceStatus);
            verified(NO);
        }
    });
}
 
cs


3) 마무리.

  - 지금까지 GUI에 관련된 그 어떤 작업도 하지 않았으나, 기본 지문인식 다이얼로그로 매우 원활히 인증이 가능하다.

    이와 관련된 GUI는 할 필요가 없을 듯.... 물론 까탈스러운 UX디자이너가 요구해올 수 있겠지만... GUI의 수준이 기본 다이얼로그보다 좋지 않을것임이 뻔하다.


0 Comments
댓글쓰기 폼