unit PMA.Biometric; { Fine couche au-dessus de FMX.BiometricAuth. On passe par le service FMX plutot que par BiometricPrompt en JNI direct : le rappel de BiometricPrompt exige une classe Java implementant une interface, donc un .jar a compiler et embarquer. FMX fournit deja ce pont. MODELE RETENU : la cle du Keystore est creee avec une validite APRES authentification (quelques dizaines de secondes). L'utilisateur s'authentifie, puis la cle devient utilisable pendant ce delai. C'est plus simple qu'un CryptoObject lie a chaque operation, et suffisant ici : la fenetre est courte et l'application est au premier plan pendant ce temps. L'APPEL DOIT SE FAIRE SUR LE THREAD PRINCIPAL - il affiche un dialogue systeme. Le travail lourd (dechiffrement) part ensuite dans un thread. } interface uses System.SysUtils; type TBioOutcome = (boOK, boUnavailable, boFailed, boCancelled); /// L'appareil peut-il authentifier (empreinte, visage, ou code de /// verrouillage) ? False signifie qu'aucune protection n'est configuree. function BiometricAvailable: Boolean; /// Demande l'authentification puis appelle AThen. Non bloquant : le resultat /// arrive par le rappel, sur le thread principal. /// Si l'appareil ne sait pas authentifier, AThen est appele avec boUnavailable /// SANS refuser l'acces - a l'appelant de decider. Un telephone sans /// verrouillage ne peut de toute facon pas proteger la cle. procedure AuthenticateThen(const APrompt: string; const AThen: TProc); implementation {$IF DEFINED(ANDROID)} uses FMX.BiometricAuth, FMX.Platform; {$ENDIF} function BiometricAvailable: Boolean; {$IF DEFINED(ANDROID)} var LSvc: IFMXBiometricAuthService; begin Result := TPlatformServices.Current.SupportsPlatformService( IFMXBiometricAuthService, LSvc) and LSvc.CanAuthenticate; end; {$ELSE} begin Result := False; end; {$ENDIF} procedure AuthenticateThen(const APrompt: string; const AThen: TProc); {$IF DEFINED(ANDROID)} var LSvc: IFMXBiometricAuthService; LProps: TBiometricPromptProperties; begin if not Assigned(AThen) then Exit; if not (TPlatformServices.Current.SupportsPlatformService( IFMXBiometricAuthService, LSvc) and LSvc.CanAuthenticate) then begin AThen(boUnavailable); Exit; end; LProps := Default(TBiometricPromptProperties); LProps.Title := 'Deverrouiller le coffre'; LProps.Description := APrompt; LProps.NegativeButtonText := 'Annuler'; LSvc.SetPromptProperties(LProps); // DeviceCredential inclus : un telephone sans empreinte enrolee mais avec // un code de verrouillage reste utilisable, au lieu d'etre bloque. LSvc.SetBiometricStrengths([TBiometricStrength.Strong, TBiometricStrength.DeviceCredential]); LSvc.Authenticate( procedure begin AThen(boOK); end, procedure(const AFailReason: TBiometricFailReason; const AMessage: string) begin if AFailReason = TBiometricFailReason.Cancelled then AThen(boCancelled) else AThen(boFailed); end); end; {$ELSE} begin if Assigned(AThen) then AThen(boUnavailable); end; {$ENDIF} end.