Browse Source
* Initial work on windows hello support * Switch to use windows.security.credentials.ui UserConsentVerifier * Fix linting warnings * Remove unessesary supportsBiometric from lock screen * Rename biometric.main to windows.biometric.main. Add abstraction for biometric. * Add support for dynamic biometric text. * Add untested darwin implementation * Rename fingerprintUnlock to biometric * Add new functions to cliPlatformUtils.service.ts. * Hide login if biometric is not supported * Export default for biometric.*.main.ts * Remove @nodert-win10-rs4/windows.security.credentials * Add build requirements to readme * Auto prompt biometric when starting the application. * Ensure we support biometric before trying to auto prompt. * Fix review comments and linting errorspull/135/head
19 changed files with 300 additions and 6 deletions
@ -0,0 +1,5 @@ |
|||||||
|
export abstract class BiometricMain { |
||||||
|
init: () => Promise<void>; |
||||||
|
supportsBiometric: () => Promise<boolean>; |
||||||
|
requestCreate: () => Promise<boolean>; |
||||||
|
} |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
import { I18nService, StorageService } from '../abstractions'; |
||||||
|
|
||||||
|
import { ipcMain, systemPreferences } from 'electron'; |
||||||
|
import { BiometricMain } from '../abstractions/biometric.main'; |
||||||
|
import { ConstantsService } from '../services'; |
||||||
|
import { ElectronConstants } from './electronConstants'; |
||||||
|
|
||||||
|
export default class BiometricDarwinMain implements BiometricMain { |
||||||
|
constructor(private storageService: StorageService, private i18nservice: I18nService) {} |
||||||
|
|
||||||
|
async init() { |
||||||
|
this.storageService.save(ElectronConstants.enableBiometric, await this.supportsBiometric()); |
||||||
|
this.storageService.save(ConstantsService.biometricText, 'unlockWithTouchId'); |
||||||
|
|
||||||
|
ipcMain.on('biometric', async (event: any, message: any) => { |
||||||
|
event.returnValue = await this.requestCreate(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
supportsBiometric(): Promise<boolean> { |
||||||
|
return Promise.resolve(systemPreferences.canPromptTouchID()); |
||||||
|
} |
||||||
|
|
||||||
|
async requestCreate(): Promise<boolean> { |
||||||
|
try { |
||||||
|
await systemPreferences.promptTouchID(this.i18nservice.t('touchIdConsentMessage')); |
||||||
|
return true; |
||||||
|
} catch { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,46 @@ |
|||||||
|
import * as util from 'util'; |
||||||
|
|
||||||
|
import { |
||||||
|
UserConsentVerificationResult, |
||||||
|
UserConsentVerifier, |
||||||
|
UserConsentVerifierAvailability, |
||||||
|
} from '@nodert-win10-rs4/windows.security.credentials.ui'; |
||||||
|
import { I18nService, StorageService } from '../abstractions'; |
||||||
|
|
||||||
|
import { ipcMain } from 'electron'; |
||||||
|
import { BiometricMain } from '../abstractions/biometric.main'; |
||||||
|
import { ConstantsService } from '../services'; |
||||||
|
import { ElectronConstants } from './electronConstants'; |
||||||
|
|
||||||
|
const requestVerification: any = util.promisify(UserConsentVerifier.requestVerificationAsync); |
||||||
|
const checkAvailability: any = util.promisify(UserConsentVerifier.checkAvailabilityAsync); |
||||||
|
|
||||||
|
const AllowedAvailabilities = [ |
||||||
|
UserConsentVerifierAvailability.available, |
||||||
|
UserConsentVerifierAvailability.deviceBusy, |
||||||
|
]; |
||||||
|
|
||||||
|
export default class BiometricWindowsMain implements BiometricMain { |
||||||
|
constructor(private storageService: StorageService, private i18nservice: I18nService) {} |
||||||
|
|
||||||
|
async init() { |
||||||
|
this.storageService.save(ElectronConstants.enableBiometric, await this.supportsBiometric()); |
||||||
|
this.storageService.save(ConstantsService.biometricText, 'unlockWithWindowsHello'); |
||||||
|
|
||||||
|
ipcMain.on('biometric', async (event: any, message: any) => { |
||||||
|
event.returnValue = await this.requestCreate(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
async supportsBiometric(): Promise<boolean> { |
||||||
|
const availability = await checkAvailability(); |
||||||
|
|
||||||
|
return AllowedAvailabilities.includes(availability); |
||||||
|
} |
||||||
|
|
||||||
|
async requestCreate(): Promise<boolean> { |
||||||
|
const verification = await requestVerification(this.i18nservice.t('windowsHelloConsentMessage')); |
||||||
|
|
||||||
|
return verification === UserConsentVerificationResult.verified; |
||||||
|
} |
||||||
|
} |
||||||
@ -1,3 +1,104 @@ |
|||||||
declare function escape(s: string): string; |
declare function escape(s: string): string; |
||||||
declare function unescape(s: string): string; |
declare function unescape(s: string): string; |
||||||
declare module 'duo_web_sdk'; |
declare module 'duo_web_sdk'; |
||||||
|
|
||||||
|
/* tslint:disable */ |
||||||
|
declare module '@nodert-win10-rs4/windows.security.credentials.ui' { |
||||||
|
export enum AuthenticationProtocol { |
||||||
|
basic, |
||||||
|
digest, |
||||||
|
ntlm, |
||||||
|
kerberos, |
||||||
|
negotiate, |
||||||
|
credSsp, |
||||||
|
custom, |
||||||
|
} |
||||||
|
|
||||||
|
export enum CredentialSaveOption { |
||||||
|
unselected, |
||||||
|
selected, |
||||||
|
hidden, |
||||||
|
} |
||||||
|
|
||||||
|
export enum UserConsentVerifierAvailability { |
||||||
|
available, |
||||||
|
deviceNotPresent, |
||||||
|
notConfiguredForUser, |
||||||
|
disabledByPolicy, |
||||||
|
deviceBusy, |
||||||
|
} |
||||||
|
|
||||||
|
export enum UserConsentVerificationResult { |
||||||
|
verified, |
||||||
|
deviceNotPresent, |
||||||
|
notConfiguredForUser, |
||||||
|
disabledByPolicy, |
||||||
|
deviceBusy, |
||||||
|
retriesExhausted, |
||||||
|
canceled, |
||||||
|
} |
||||||
|
|
||||||
|
export class CredentialPickerOptions { |
||||||
|
targetName: String; |
||||||
|
previousCredential: Object; |
||||||
|
message: String; |
||||||
|
errorCode: Number; |
||||||
|
customAuthenticationProtocol: String; |
||||||
|
credentialSaveOption: CredentialSaveOption; |
||||||
|
caption: String; |
||||||
|
callerSavesCredential: Boolean; |
||||||
|
authenticationProtocol: AuthenticationProtocol; |
||||||
|
alwaysDisplayDialog: Boolean; |
||||||
|
constructor(); |
||||||
|
} |
||||||
|
|
||||||
|
export class CredentialPickerResults { |
||||||
|
credential: Object; |
||||||
|
credentialDomainName: String; |
||||||
|
credentialPassword: String; |
||||||
|
credentialSaveOption: CredentialSaveOption; |
||||||
|
credentialSaved: Boolean; |
||||||
|
credentialUserName: String; |
||||||
|
errorCode: Number; |
||||||
|
constructor(); |
||||||
|
} |
||||||
|
|
||||||
|
export class CredentialPicker { |
||||||
|
constructor(); |
||||||
|
|
||||||
|
static pickAsync( |
||||||
|
options: CredentialPickerOptions, |
||||||
|
callback: (error: Error, result: CredentialPickerResults) => void |
||||||
|
): void; |
||||||
|
static pickAsync( |
||||||
|
targetName: String, |
||||||
|
message: String, |
||||||
|
callback: (error: Error, result: CredentialPickerResults) => void |
||||||
|
): void; |
||||||
|
static pickAsync( |
||||||
|
targetName: String, |
||||||
|
message: String, |
||||||
|
caption: String, |
||||||
|
callback: (error: Error, result: CredentialPickerResults) => void |
||||||
|
): void; |
||||||
|
} |
||||||
|
|
||||||
|
export class UserConsentVerifier { |
||||||
|
constructor(); |
||||||
|
|
||||||
|
static checkAvailabilityAsync( |
||||||
|
callback: ( |
||||||
|
error: Error, |
||||||
|
result: UserConsentVerifierAvailability |
||||||
|
) => void |
||||||
|
): void; |
||||||
|
|
||||||
|
static requestVerificationAsync( |
||||||
|
message: String, |
||||||
|
callback: ( |
||||||
|
error: Error, |
||||||
|
result: UserConsentVerificationResult |
||||||
|
) => void |
||||||
|
): void; |
||||||
|
} |
||||||
|
} |
||||||
|
|||||||
Loading…
Reference in new issue