diff --git a/XDemo.Android/MainActivity.cs b/XDemo.Android/MainActivity.cs index dce5e8d..b9a84e2 100644 --- a/XDemo.Android/MainActivity.cs +++ b/XDemo.Android/MainActivity.cs @@ -5,6 +5,7 @@ using FFImageLoading.Forms.Platform; using Android.Views; using Xamarin.Forms; +using Plugin.CurrentActivity; namespace XDemo.Droid { @@ -34,6 +35,8 @@ protected override void OnCreate(Bundle savedInstanceState) * ================================================================================================*/ CachedImageRenderer.Init(true); + CrossCurrentActivity.Current.Init(this, savedInstanceState); + /* ================================================================================================== * set the app info secure in app switcher * ================================================================================================*/ diff --git a/XDemo.Android/Services/Implementations/Fingerprints/CryptoObjectHelper.cs b/XDemo.Android/Services/Implementations/Fingerprints/CryptoObjectHelper.cs deleted file mode 100644 index 96d0370..0000000 --- a/XDemo.Android/Services/Implementations/Fingerprints/CryptoObjectHelper.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using Android.Support.V4.Hardware.Fingerprint; -using Android.Security.Keystore; -using Java.Security; -using Javax.Crypto; -using Android.Hardware.Fingerprints; - -namespace XDemo.Droid.Services.Implementations.Fingerprints -{ - public class CryptoObjectHelper - { - // This can be key name you want. Should be unique for the app. - static readonly string KeyName = $"vn.quinn.XDemo.{Guid.NewGuid()}"; - - // We always use this keystore on Android. - static readonly string KeyStoreName = "AndroidKeyStore"; - - // Should be no need to change these values. - static readonly string KeyAlgorithm = KeyProperties.KeyAlgorithmAes; - static readonly string BlockMode = KeyProperties.BlockModeCbc; - static readonly string EncryptionPadding = KeyProperties.EncryptionPaddingPkcs7; - static readonly string Transformation = $"{KeyAlgorithm}/{BlockMode}/{EncryptionPadding}"; - readonly KeyStore _keystore; - - public CryptoObjectHelper() - { - _keystore = KeyStore.GetInstance(KeyStoreName); - _keystore.Load(null); - } - - public FingerprintManagerCompat.CryptoObject BuildCompatCryptoObject() - { - Cipher cipher = CreateCipher(); - return new FingerprintManagerCompat.CryptoObject(cipher); - } - public FingerprintManager.CryptoObject BuildCryptoObject() - { - Cipher cipher = CreateCipher(); - return new FingerprintManager.CryptoObject(cipher); - } - - Cipher CreateCipher(bool retry = true) - { - IKey key = GetKey(); - Cipher cipher = Cipher.GetInstance(Transformation); - try - { - cipher.Init(CipherMode.EncryptMode | CipherMode.DecryptMode, key); - } - catch (KeyPermanentlyInvalidatedException e) - { - _keystore.DeleteEntry(KeyName); - if (retry) - { - CreateCipher(false); - } - else - { - throw new System.Exception("Could not create the cipher for fingerprint authentication.", e); - } - } - return cipher; - } - - IKey GetKey() - { - IKey secretKey; - if (!_keystore.IsKeyEntry(KeyName)) - { - CreateKey(); - } - - secretKey = _keystore.GetKey(KeyName, null); - return secretKey; - } - - void CreateKey() - { - var keyGen = KeyGenerator.GetInstance(KeyProperties.KeyAlgorithmAes, KeyStoreName); - var keyGenSpec = - new KeyGenParameterSpec.Builder(KeyName, KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt) - .SetBlockModes(BlockMode) - .SetEncryptionPaddings(EncryptionPadding) - .SetUserAuthenticationRequired(true) - .Build(); - keyGen.Init(keyGenSpec); - keyGen.GenerateKey(); - } - } -} diff --git a/XDemo.Android/Services/Implementations/Fingerprints/FingerprintHandler.cs b/XDemo.Android/Services/Implementations/Fingerprints/FingerprintHandler.cs new file mode 100644 index 0000000..207531f --- /dev/null +++ b/XDemo.Android/Services/Implementations/Fingerprints/FingerprintHandler.cs @@ -0,0 +1,59 @@ +using System; +using Android; +using Android.Content; +using Android.Hardware.Fingerprints; +using Android.OS; +using Android.Runtime; +using Java.Lang; +using Plugin.CurrentActivity; +using XDemo.Core.BusinessServices.Interfaces.Hardwares.LocalAuthentications; + +namespace XDemo.Droid.Services.Implementations.Fingerprints +{ + public interface IFingerprint + { + void AuthenticationResult(FingerprintResult result); + } + + + public class FingerprintHandler : FingerprintManager.AuthenticationCallback + { + private IFingerprint fingerprint; + + public FingerprintHandler(IFingerprint fingerprint) + { + this.fingerprint = fingerprint; + } + + public CancellationSignal Start(FingerprintManager manager, FingerprintManager.CryptoObject cryptoObject) + { + CancellationSignal cancellationSignal = new CancellationSignal(); + if(CrossCurrentActivity.Current.Activity.CheckSelfPermission(Manifest.Permission.UseFingerprint) != Android.Content.PM.Permission.Granted) + { + return cancellationSignal; + } + manager.Authenticate(cryptoObject, cancellationSignal, 0, this, null); + return cancellationSignal; + } + + public override void OnAuthenticationError([GeneratedEnum] FingerprintState errorCode, ICharSequence errString) + { + fingerprint?.AuthenticationResult(FingerprintResult.Error); + } + + public override void OnAuthenticationFailed() + { + fingerprint?.AuthenticationResult(FingerprintResult.Failed); + } + + public override void OnAuthenticationHelp([GeneratedEnum] FingerprintState helpCode, ICharSequence helpString) + { + fingerprint?.AuthenticationResult(FingerprintResult.Help); + } + + public override void OnAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) + { + fingerprint?.AuthenticationResult(FingerprintResult.Succeed); + } + } +} diff --git a/XDemo.Android/Services/Implementations/Fingerprints/FingerprintService.cs b/XDemo.Android/Services/Implementations/Fingerprints/FingerprintService.cs index 8711047..d6f5bad 100644 --- a/XDemo.Android/Services/Implementations/Fingerprints/FingerprintService.cs +++ b/XDemo.Android/Services/Implementations/Fingerprints/FingerprintService.cs @@ -6,110 +6,175 @@ using Android.Runtime; using XDemo.UI; using System; +using Android.Support.V7.App; +using Xamarin.Forms; +using Plugin.CurrentActivity; +using Android; +using Java.Security; +using System.Diagnostics; +using Javax.Crypto; +using Android.Security.Keystore; +using XDemo.Core.Infrastructure.Logging; +using XDemo.UI.ViewModels.Common; +using Android.OS; namespace XDemo.Droid.Services.Implementations.Fingerprints { - public class FingerprintService : ILocalAuthenticationService + + public class FingerprintService : ILocalAuthenticationService, IFingerprint { - public bool IsEnrolled() + CancellationSignal _cancellationSignal; + AlertDialog _alertDialog; + ILocalAuthentication _localAuthentication; + KeyStore _keyStore; + string _androidKeyStore = "AndroidKeyStore"; + string _keyName = "androidHive"; + Cipher _cipher; + + public void setlocalAuthentication(ILocalAuthentication fingerprintVM) + { + this._localAuthentication = fingerprintVM; + } + + public bool IsSupported() { var context = Android.App.Application.Context; + if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M) { /* ================================================================================================== * android api 23 or higher * ================================================================================================*/ + var manager = context.GetSystemService(Context.FingerprintService) as FingerprintManager; - var rs = manager.HasEnrolledFingerprints; - return rs; + if (manager.IsHardwareDetected) + { + if (CrossCurrentActivity.Current.Activity.CheckSelfPermission(Manifest.Permission.UseFingerprint) == Android.Content.PM.Permission.Granted) + { + return true; + } + } } + return false; + } - // Using the Android Support Library v4 - var managerCompat = FingerprintManagerCompat.From(context); - var rs2 = managerCompat.HasEnrolledFingerprints; - return rs2; + private void GenerateKey() + { + try + { + if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M) + { + _keyStore = KeyStore.GetInstance(_androidKeyStore); + KeyGenerator keyGenerator = KeyGenerator.GetInstance(KeyProperties.KeyAlgorithmAes, _androidKeyStore); + _keyStore.Load(null); + keyGenerator.Init(new KeyGenParameterSpec.Builder(_keyName, + KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt) + .SetBlockModes(KeyProperties.BlockModeCbc) + .SetUserAuthenticationRequired(true) + .SetEncryptionPaddings(KeyProperties.EncryptionPaddingPkcs7) + .Build()); + keyGenerator.GenerateKey(); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + } } - public bool IsSupported() + private bool CipherInit() + { + try + { + if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M) + { + _cipher = Cipher.GetInstance($"{KeyProperties.KeyAlgorithmAes}/{KeyProperties.BlockModeCbc}/{KeyProperties.EncryptionPaddingPkcs7}"); + _keyStore.Load(null); + var key = _keyStore.GetKey(_keyName, null); + _cipher.Init(CipherMode.EncryptMode, key); + return true; + } + else + { + return false; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + return false; + } + } + + public void AuthenticFingerprint(string reason) { - var context = Android.App.Application.Context; if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M) { - /* ================================================================================================== - * android api 23 or higher - * ================================================================================================*/ - var manager = context.GetSystemService(Context.FingerprintService) as FingerprintManager; - var rs = manager.IsHardwareDetected; - return rs; + ShowDialog(); + + GenerateKey(); + + if (CipherInit()) + { + var manager = CrossCurrentActivity.Current.Activity.GetSystemService(Context.FingerprintService) as FingerprintManager; + FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(_cipher); + FingerprintHandler fingerprintHandler = new FingerprintHandler(this); + _cancellationSignal = fingerprintHandler.Start(manager, cryptoObject); + } } + } - // Using the Android Support Library v4 - var managerCompat = FingerprintManagerCompat.From(context); - var rs2 = managerCompat.IsHardwareDetected; - return rs2; + public void CancelAuthenticate() + { + _cancellationSignal?.Cancel(); + ShowDialogError(); } - public Task AuthenticateAsync(string reason) + void ShowDialog() { - // CryptoObjectHelper is described in the previous section. - var cryptoHelper = new CryptoObjectHelper(); + _alertDialog = new AlertDialog.Builder(CrossCurrentActivity.Current.Activity).Create(); + _alertDialog.SetTitle("Login"); + _alertDialog.SetMessage("Touch the fingerprint scanner to login"); + _alertDialog.SetButton(-1, "Cancel", (sender, e) => + { + _cancellationSignal?.Cancel(); + _alertDialog.Dismiss(); + }); + _alertDialog.SetCancelable(false); + _alertDialog.SetIcon(Android.Resource.Drawable.IcSecure); + _alertDialog.Show(); + } - var context = Android.App.Application.Context; - if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M) + void ShowDialogError() + { + CrossCurrentActivity.Current.Activity.RunOnUiThread(() => { - /* ================================================================================================== - * android api 23 or higher - * ================================================================================================*/ - var manager = context.GetSystemService(Context.FingerprintService) as FingerprintManager; - // cancellationSignal can be used to manually stop the fingerprint scanner. - var cancellationSignal = new Android.OS.CancellationSignal(); - var authenticationCallback = new SimpleAuthCallback(); - // Start the fingerprint scanner. - manager.Authenticate(cryptoHelper.BuildCryptoObject(), cancellationSignal, FingerprintAuthenticationFlags.None, authenticationCallback, null); - } - // Using the Android Support Library v4 - var managerCompat = FingerprintManagerCompat.From(context); - // cancellationSignal can be used to manually stop the fingerprint scanner. - var cancellationSignalCompat = new Android.Support.V4.OS.CancellationSignal(); - // AuthenticationCallback is a base class that will be covered later on in this guide. - var callbackCompat = new SimpleCompatAuthCallback(); - // Start the fingerprint scanner. - managerCompat.Authenticate(cryptoHelper.BuildCompatCryptoObject(), 0, cancellationSignalCompat, callbackCompat, null); - //return new LocalAuthResult(false); - return Task.FromResult(new LocalAuthResult(false)); + AlertDialog alertDialog = new AlertDialog.Builder(CrossCurrentActivity.Current.Activity).Create(); + alertDialog.SetTitle("Login fail"); + alertDialog.SetMessage("Can not login with fingerprints"); + alertDialog.SetButton(-1, "OK", (sender, e) => + { + alertDialog.Dismiss(); + }); + alertDialog.SetCancelable(false); + alertDialog.SetIcon(Android.Resource.Drawable.IcLockPowerOff); + alertDialog?.Show(); + }); } - public void AuthenticateAndroid(string reason) + + public void AuthenticationResult(FingerprintResult result) { - // CryptoObjectHelper is described in the previous section. - var cryptoHelper = new CryptoObjectHelper(); + if (result == FingerprintResult.Error || result == FingerprintResult.Succeed) + { + _alertDialog?.Dismiss(); + _cancellationSignal?.Cancel(); - var context = Android.App.Application.Context; - //if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.M) - //{ - // /* ================================================================================================== - // * android api 23 or higher - // * ================================================================================================*/ - // var manager = context.GetSystemService(Context.FingerprintService) as FingerprintManager; - // // cancellationSignal can be used to manually stop the fingerprint scanner. - // var cancellationSignal = new Android.OS.CancellationSignal(); - // var authenticationCallback = new SimpleAuthCallback(); - // // Start the fingerprint scanner. - // manager.Authenticate(cryptoHelper.BuildCryptoObject(), cancellationSignal, FingerprintAuthenticationFlags.None, authenticationCallback, null); - // return; - //} - - // Using the Android Support Library v4 - var managerCompat = FingerprintManagerCompat.From(context); - // cancellationSignal can be used to manually stop the fingerprint scanner. - var cancellationSignalCompat = new Android.Support.V4.OS.CancellationSignal(); - // AuthenticationCallback is a base class that will be covered later on in this guide. - var callbackCompat = new SimpleCompatAuthCallback(); - // Start the fingerprint scanner. - managerCompat.Authenticate(cryptoHelper.BuildCompatCryptoObject(), 0, cancellationSignalCompat, callbackCompat, null); - //return new LocalAuthResult(false); - //return Task.FromResult(new LocalAuthResult(false)); + } + + _localAuthentication?.AuthenticationFingerprintResult(result); + LogCommon.Info($"AuthenticationResult: {result}"); } } } diff --git a/XDemo.Android/Services/Implementations/Fingerprints/SimpleAuthCallback.cs b/XDemo.Android/Services/Implementations/Fingerprints/SimpleAuthCallback.cs deleted file mode 100644 index 7221cc0..0000000 --- a/XDemo.Android/Services/Implementations/Fingerprints/SimpleAuthCallback.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Java.Lang; -using Javax.Crypto; -using XDemo.Core.Infrastructure.Logging; -using Android.Hardware.Fingerprints; -using Android.Runtime; - -namespace XDemo.Droid.Services.Implementations.Fingerprints -{ - /// - /// API 23 or higher - /// - internal class SimpleAuthCallback : FingerprintManager.AuthenticationCallback - { - // Can be any byte array, keep unique to application. - static readonly byte[] SECRET_BYTES = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - public override void OnAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) - { - if (result.CryptoObject.Cipher != null) - { - try - { - // Calling DoFinal on the Cipher ensures that the encryption worked. - byte[] doFinalResult = result.CryptoObject.Cipher.DoFinal(SECRET_BYTES); - - // No errors occurred, trust the results. - } - catch (BadPaddingException bpe) - { - // Can't really trust the results. - LogCommon.Error(bpe); - } - catch (IllegalBlockSizeException ibse) - { - // Can't really trust the results. - LogCommon.Error(ibse); - } - } - else - { - // No cipher used, assume that everything went well and trust the results. - } - } - - public override void OnAuthenticationFailed() - { - // Tell the user that the fingerprint was not recognized. - } - public override void OnAuthenticationError([GeneratedEnum] FingerprintState errorCode, ICharSequence errString) - { - // Report the error to the user. Note that if the user canceled the scan, - // this method will be called and the errMsgId will be FingerprintState.ErrorCanceled. - } - public override void OnAuthenticationHelp([GeneratedEnum] FingerprintState helpCode, ICharSequence helpString) - { - // Notify the user that the scan failed and display the provided hint. - } - } -} diff --git a/XDemo.Android/Services/Implementations/Fingerprints/SimpleCompatAuthCallback.cs b/XDemo.Android/Services/Implementations/Fingerprints/SimpleCompatAuthCallback.cs deleted file mode 100644 index a8ab43d..0000000 --- a/XDemo.Android/Services/Implementations/Fingerprints/SimpleCompatAuthCallback.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Android.Support.V4.Hardware.Fingerprint; -using Java.Lang; -using Javax.Crypto; -using XDemo.Core.Infrastructure.Logging; - -namespace XDemo.Droid.Services.Implementations.Fingerprints -{ - internal class SimpleCompatAuthCallback : FingerprintManagerCompat.AuthenticationCallback - { - // Can be any byte array, keep unique to application. - static readonly byte[] SECRET_BYTES = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - public override void OnAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) - { - if (result.CryptoObject.Cipher != null) - { - try - { - // Calling DoFinal on the Cipher ensures that the encryption worked. - byte[] doFinalResult = result.CryptoObject.Cipher.DoFinal(SECRET_BYTES); - - // No errors occurred, trust the results. - } - catch (BadPaddingException bpe) - { - // Can't really trust the results. - LogCommon.Error(bpe); - } - catch (IllegalBlockSizeException ibse) - { - // Can't really trust the results. - LogCommon.Error(ibse); - } - } - else - { - // No cipher used, assume that everything went well and trust the results. - } - } - - public override void OnAuthenticationError(int errMsgId, ICharSequence errString) - { - // Report the error to the user. Note that if the user canceled the scan, - // this method will be called and the errMsgId will be FingerprintState.ErrorCanceled. - } - - public override void OnAuthenticationFailed() - { - // Tell the user that the fingerprint was not recognized. - } - - public override void OnAuthenticationHelp(int helpMsgId, ICharSequence helpString) - { - // Notify the user that the scan failed and display the provided hint. - } - } -} diff --git a/XDemo.Android/XDemo.Android.csproj b/XDemo.Android/XDemo.Android.csproj index 67eeb15..adcd7b7 100644 --- a/XDemo.Android/XDemo.Android.csproj +++ b/XDemo.Android/XDemo.Android.csproj @@ -72,6 +72,9 @@ 8.0.0 + + 2.1.0.4 + @@ -94,9 +97,7 @@ - - - + diff --git a/XDemo.Core/BusinessServices/Interfaces/Hardwares/LocalAuthentications/ILocalAuthenticationService.cs b/XDemo.Core/BusinessServices/Interfaces/Hardwares/LocalAuthentications/ILocalAuthenticationService.cs index c4b0a9c..459cbae 100644 --- a/XDemo.Core/BusinessServices/Interfaces/Hardwares/LocalAuthentications/ILocalAuthenticationService.cs +++ b/XDemo.Core/BusinessServices/Interfaces/Hardwares/LocalAuthentications/ILocalAuthenticationService.cs @@ -12,33 +12,67 @@ namespace XDemo.Core.BusinessServices.Interfaces.Hardwares.LocalAuthentications { /// - /// interact with device fingerprint/faceId + /// Fingerprint result. /// - public interface ILocalAuthenticationService + public enum FingerprintResult { /// - /// indicate that the device has compabity hardware + /// The succeed. /// - /// true, if supported was ised, false otherwise. - bool IsSupported(); + Succeed, + /// + /// The failed. + /// + Failed, + /// + /// The help. + /// + Help, + /// + /// The error. + /// + Error + } + + /// + /// Local authentication. + /// + public interface ILocalAuthentication + { + /// + /// Authentications the fingerprint result. + /// + /// Result. + void AuthenticationFingerprintResult(FingerprintResult result); + } + /// + /// interact with device fingerprint/faceId + /// + public interface ILocalAuthenticationService + { /// - /// if hardware supported, check if has fingerprint/faceid was configuarated + /// Setlocals the authentication. /// - /// true, if enrolled was ised, false otherwise. - bool IsEnrolled(); + /// Local authentication. + void setlocalAuthentication(ILocalAuthentication localAuthentication); /// - /// Authenticates async. + /// indicate that the device has compabity hardware /// - /// The async. - /// Reason. - Task AuthenticateAsync(string reason); + /// true, if supported was ised, false otherwise. + bool IsSupported(); + /// /// for test synchronus call. /// todo: remove /// /// Reason. - void AuthenticateAndroid(string reason); + void AuthenticFingerprint(string reason); + + /// + /// Cancels the authenticate. + /// + void CancelAuthenticate(); } } diff --git a/XDemo.UI/ViewModels/Common/LoginPageViewModel.cs b/XDemo.UI/ViewModels/Common/LoginPageViewModel.cs index 1140323..2aa0a28 100644 --- a/XDemo.UI/ViewModels/Common/LoginPageViewModel.cs +++ b/XDemo.UI/ViewModels/Common/LoginPageViewModel.cs @@ -10,16 +10,16 @@ using XDemo.UI.Models.Validations.Base; using XDemo.UI.Models.Validations.DefinedRules; using XDemo.Core.BusinessServices.Interfaces.Hardwares.LocalAuthentications; +using XDemo.Core.Infrastructure.Logging; namespace XDemo.UI.ViewModels.Common { - public class LoginPageViewModel : ViewModelBase + public class LoginPageViewModel : ViewModelBase, ILocalAuthentication { private readonly ISecurityService _securityService; private readonly IPageDialogService _pageDialogService; private readonly IPhotoService _photoService; private readonly ILocalAuthenticationService _localAuthService; - public LoginPageViewModel(ISecurityService securityService, IPageDialogService pageDialogService, INavigationService navigationService, IPhotoService photoService, ILocalAuthenticationService localAuthService) : base(navigationService) { @@ -40,7 +40,6 @@ public override void OnNavigatedTo(INavigationParameters parameters) //example using local setting UserName.Value = setting.SavedUserId; Password.Value = setting.SavedPassword; - /* ================================================================================================== * DONT USE LIKE THIS => BC THE SETTING VALUE WILL BE READ MANY TIMES (NOT GOOD) * UserName = StorageContext.Current.LoginSetting.SavedUserId; @@ -55,22 +54,26 @@ public override void OnNavigatedTo(INavigationParameters parameters) private ICommand _authCommand; - public ICommand AuthCommand => _authCommand ?? (_authCommand = new Command(async () => await AuthCommandExecute())); + public ICommand AuthCommand => _authCommand ?? (_authCommand = new Command(() => AuthCommandExecute())); - private async Task AuthCommandExecute() + private void AuthCommandExecute() { - var isEnrolled = _localAuthService.IsEnrolled(); var isSupported = _localAuthService.IsSupported(); - _localAuthService.AuthenticateAndroid("sdsd"); + _localAuthService.setlocalAuthentication(this); + _localAuthService.AuthenticFingerprint("Login with fingerprint"); - //var authRs = await _localAuthService.AuthenticateAsync("Test for touch id"); - //if (authRs.IsSuccess) - // await GoToMainPageAsync(); - //else - //await _pageDialogService.DisplayAlertAsync("Error", authRs.ErrorMessage, "Ok"); } + public async void AuthenticationFingerprintResult(FingerprintResult result) + { + if (result == FingerprintResult.Succeed) + await GoToMainPageAsync(); + else if (result == FingerprintResult.Error) + _localAuthService?.CancelAuthenticate(); + } + + #endregion #region LoginCommand diff --git a/XDemo.iOS/Services/Implementations/TouchIdService.cs b/XDemo.iOS/Services/Implementations/TouchIdService.cs index a0b4a9a..2f77fce 100644 --- a/XDemo.iOS/Services/Implementations/TouchIdService.cs +++ b/XDemo.iOS/Services/Implementations/TouchIdService.cs @@ -8,95 +8,93 @@ namespace XDemo.iOS.Services.Implementations { public class TouchIdService : ILocalAuthenticationService - { - public async Task AuthenticateAsync(string reason) - { - var context = new LAContext + { + ILocalAuthentication localAuthentication; + + public bool IsSupported() + { + using (var context = new LAContext()) { - LocalizedFallbackTitle = "Fallback" // iOS 8 + var result = context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out var error); + if (result) + return result; + var status = (LAStatus)(int)error.Code; + result = status != LAStatus.BiometryNotAvailable; + return result; + } + } + + public void AuthenticFingerprint(string reason) + { + var context = new LAContext + { + LocalizedFallbackTitle = "Fallback" // iOS 8 }; - if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0)) - { - context.LocalizedCancelTitle = "Cancel"; // iOS 10 + if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0)) + { + context.LocalizedCancelTitle = "Cancel"; // iOS 10 } - if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0)) - { - context.LocalizedReason = reason; // iOS 11 + if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0)) + { + context.LocalizedReason = reason; // iOS 11 } - var rs = await AuthOnMainThreadAsync(context, reason); - context.Dispose(); - return rs; - } - - /// - /// Auths the on main thread. - /// - /// The on main thread. - /// Context. - /// Reason can not be null or empty - private Task AuthOnMainThreadAsync(LAContext context, string reason) - { + AuthOnMainThread(context, reason); + } + + private void AuthOnMainThread(LAContext context, string reason) + { var tcs = new TaskCompletionSource(); var result = new LocalAuthResult(false); /* ================================================================================================== * indicate not allow null or empty reason * ================================================================================================*/ - if (string.IsNullOrWhiteSpace(reason)) - { - result = new LocalAuthResult(false, "Your reason can not be null or empty"); - tcs.SetResult(result); - return tcs.Task; + if (string.IsNullOrWhiteSpace(reason)) + { + localAuthentication.AuthenticationFingerprintResult(FingerprintResult.Error); } /* ================================================================================================== * indicate the hardware * ================================================================================================*/ - if (!context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out NSError authError)) - { - result = new LocalAuthResult(false, authError?.ToString()); - tcs.SetResult(result); - return tcs.Task; + if (!context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out NSError authError)) + { + localAuthentication.AuthenticationFingerprintResult(FingerprintResult.Help); } /* ================================================================================================== * begin auth - * ================================================================================================*/ + * ================================================================================================*/ var nsReason = new NSString(reason); - var evaluateTask = context.EvaluatePolicyAsync(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, nsReason); + var evaluateTask = context.EvaluatePolicyAsync(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, nsReason); evaluateTask.ContinueWith(t => { Device.BeginInvokeOnMainThread(() => - { - var rs = t.Result; - result = new LocalAuthResult(rs.Item1, rs.Item2?.ToString()); - tcs.SetResult(result); - }); + { + if(t.Result.Item1) + localAuthentication.AuthenticationFingerprintResult(FingerprintResult.Succeed); + else + localAuthentication.AuthenticationFingerprintResult(FingerprintResult.Error); + }); }); - return tcs.Task; } - public bool IsSupported() - { - using (var context = new LAContext()) - { - var result = context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out var error); - if (result) - return result; - var status = (LAStatus)(int)error.Code; - result = status != LAStatus.BiometryNotAvailable; - return result; - } - } - - public bool IsEnrolled() + public void setlocalAuthentication(ILocalAuthentication localAuthentication) { - throw new System.NotImplementedException(); + this.localAuthentication = localAuthentication; } - public void AuthenticateAndroid(string reason) + public void CancelAuthenticate() { - throw new System.NotImplementedException("Do not support on iOS"); + var alert = UIAlertController.Create("Login fail", "Can not login with fingerprint", UIAlertControllerStyle.Alert); + alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null)); + + var topController = UIApplication.SharedApplication.KeyWindow.RootViewController; + while (topController.PresentedViewController != null) + { + topController = topController.PresentedViewController; + } + topController.PresentViewController(alert, true, null); } } }