feat: add Touch ID authentication support for macOS (#16707)

This PR adds Touch ID authentication support for macOS with two new `SystemPreferences` methods.

1. `systemPreferences.promptForTouchID()` returns a Promise that resolves with `true` if successful and rejects with an error message if authentication could not be completed.
2. `systemPreferences.isTouchIDAvailable()` returns a Boolean that's `true` if this device is a Mac running a supported OS that has the necessary hardware for Touch ID and `false` otherwise.
This commit is contained in:
Shelley Vohr 2019-02-13 18:36:28 -08:00 committed by GitHub
parent 228805353f
commit 46a24c82ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 121 additions and 0 deletions

View file

@ -93,6 +93,8 @@ void SystemPreferences::BuildPrototype(
.SetMethod("setAppLevelAppearance",
&SystemPreferences::SetAppLevelAppearance)
.SetMethod("getSystemColor", &SystemPreferences::GetSystemColor)
.SetMethod("canPromptTouchID", &SystemPreferences::CanPromptTouchID)
.SetMethod("promptTouchID", &SystemPreferences::PromptTouchID)
.SetMethod("isTrustedAccessibilityClient",
&SystemPreferences::IsTrustedAccessibilityClient)
.SetMethod("getMediaAccessStatus",

View file

@ -95,6 +95,10 @@ class SystemPreferences : public mate::EventEmitter<SystemPreferences>
std::string GetSystemColor(const std::string& color, mate::Arguments* args);
bool CanPromptTouchID();
v8::Local<v8::Promise> PromptTouchID(v8::Isolate* isolate,
const std::string& reason);
static bool IsTrustedAccessibilityClient(bool prompt);
// TODO(codebytere): Write tests for these methods once we

View file

@ -8,14 +8,19 @@
#import <AVFoundation/AVFoundation.h>
#import <Cocoa/Cocoa.h>
#import <LocalAuthentication/LocalAuthentication.h>
#import <Security/Security.h>
#include "atom/browser/mac/atom_application.h"
#include "atom/browser/mac/dict_util.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/sdk_forward_declarations.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/values.h"
#include "native_mate/object_template_builder.h"
#include "net/base/mac/url_conversions.h"
@ -438,6 +443,72 @@ std::string SystemPreferences::GetSystemColor(const std::string& color,
}
}
bool SystemPreferences::CanPromptTouchID() {
if (@available(macOS 10.12.2, *)) {
base::scoped_nsobject<LAContext> context([[LAContext alloc] init]);
if (![context
canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
error:nil])
return false;
if (@available(macOS 10.13.2, *))
return [context biometryType] == LABiometryTypeTouchID;
return true;
}
return false;
}
void OnTouchIDCompleted(scoped_refptr<util::Promise> promise) {
promise->Resolve();
}
void OnTouchIDFailed(scoped_refptr<util::Promise> promise,
const std::string& reason) {
promise->RejectWithErrorMessage(reason);
}
v8::Local<v8::Promise> SystemPreferences::PromptTouchID(
v8::Isolate* isolate,
const std::string& reason) {
scoped_refptr<util::Promise> promise = new util::Promise(isolate);
if (@available(macOS 10.12.2, *)) {
base::scoped_nsobject<LAContext> context([[LAContext alloc] init]);
base::ScopedCFTypeRef<SecAccessControlRef> access_control =
base::ScopedCFTypeRef<SecAccessControlRef>(
SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAccessControlPrivateKeyUsage |
kSecAccessControlUserPresence,
nullptr));
scoped_refptr<base::SequencedTaskRunner> runner =
base::SequencedTaskRunnerHandle::Get();
[context
evaluateAccessControl:access_control
operation:LAAccessControlOperationUseKeySign
localizedReason:[NSString stringWithUTF8String:reason.c_str()]
reply:^(BOOL success, NSError* error) {
if (!success) {
runner->PostTask(
FROM_HERE,
base::BindOnce(
&OnTouchIDFailed, promise,
std::string([error.localizedDescription
UTF8String])));
} else {
runner->PostTask(
FROM_HERE,
base::BindOnce(&OnTouchIDCompleted, promise));
}
}];
} else {
promise->RejectWithErrorMessage(
"This API is not available on macOS versions older than 10.12.2");
}
return promise->GetHandle();
}
// static
bool SystemPreferences::IsTrustedAccessibilityClient(bool prompt) {
NSDictionary* options = @{(id)kAXTrustedCheckOptionPrompt : @(prompt)};