Thursday, July 25, 2019

Verify OTP Automatically In Android Without SMS Read Permission Using Xamarin.Forms

Google is taking user privacy seriously. It has updated its Google Play Developer Policywhich restricts SMS and CALL_LOG access. If your app does not require access to Call Log or SMS permissions, you must remove the permissions from your app's manifest.
 
For apps requesting access to the SMS or Call Log permissions, the intended and permitted uses include default SMS handling, default phone handling, or Assistant handling capability. You should only access Call Log or SMS permissions when your app falls within permitted uses and only to enable your app’s critical core functionality.
 
An Android App should read the SMS for some specific needs like to verify OTP. The Google Play Service has enabled Android SMS Retriever API that allows you to access the SMS messages and verify it via SMS without device permissions.
 
Verify OTP Automatically In Android Without SMS Read Permission Using Xamarin.Forms 
 

Verification Message Format

 
You need to follow as per Google Policy to construct the verification message and send to the user device, the message should be in the following format.
  • No longer than 140 bytes
  • Begin with the prefix <#>
  • One-time Verification code
  • A special 11-character hash for your app. That Hash can be generated by the application (will explain in the following steps).

    Verify OTP Automatically In Android Without SMS Read Permission Using Xamarin.Forms
Now, we have a very good understanding of Google's new policy and message format. In this article, I will explain the following steps. It will help you to integrate Android OTP verification code auto-read using Xamarin.Forms.
  • Create New Xamarin Forms Application.
  • Add Google Play Services Auth
  • Create UI Design
  • Dependency Service
  • Messaging Center subscribe
  • ListenToSms Dependency
  • Broadcast Receiver
  • Generate Application Hash Key Helper
  • Demo Application

Create New Xamarin.Forms Application

 
In order to implement Auto-Read OTP message, let’s start creating a new Xamarin.Forms project using Visual Studio 2019 or VS for Mac. When accessing Visual Studio 2019 for Mac for the first time, you will come across a new interface for opening and creating the projects.
 
Open Visual Studio Mac >> Create New Project or select Open recent application.
 
Verify OTP Automatically In Android Without SMS Read Permission Using Xamarin.Forms
 
The available templates will appear on a Mac, like below. Select Xamarin.Forms application with different mobile platforms.
 
Verify OTP Automatically In Android Without SMS Read Permission Using Xamarin.Forms
 

Add Google Play Service NuGet Package

 
After clicking on the Next button, the Xamarin Project template will generate and load the solutions into the local system. We need to install Xamarin.GooglePlayServices.Auth NuGet package to our Android project for SMS Retriever API.
 
 Right click on Android Project >> Add Nuget Package >> Select or Search “Xamarin.GooglePlayServices.Auth”>> Install the Latest version of Google play service.
 
Verify OTP Automatically In Android Without SMS Read Permission Using Xamarin.Forms
 

Create UI Design

 
Create a simple UI Design with one Entry and Button Control using Xaml in Xamarin Forms library.
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:ReadOTPXamarinAndroid" x:Class="ReadOTPXamarinAndroid.MainPage">  
  3.    <StackLayout Padding="15" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" BackgroundColor="White">  
  4.         <Entry  PlaceholderColor="Black" Placeholder="Enter OTP" x:Name="smSEntry"></Entry>  
  5.         <Button Text="Wait for sms"  Clicked="ImageButton_OnClicked"></Button>  
  6.     </StackLayout>  
  7. </ContentPage>  
The design looks like below,
 
Verify OTP Automatically In Android Without SMS Read Permission Using Xamarin.Forms
 

Dependency Service

 
DependencyService allows apps to call into platform-specific functionality from shared code. This functionality enables Xamarin.Forms apps to do anything that a native app can do. We need to create an interface design that will define how you interact with platform-specific functionality. Here, reading OTP will support only Android Platform and IOS will support with Autofill so create a DependencyService interface for listening to SMS retriever. Create a new class and interface for CommonServices.
  1. using Xamarin.Forms;  
  2.   
  3. namespace ReadOTPXamarinAndroid  
  4. {  
  5.     public static class CommonServices  
  6.     {  
  7.         public static void ListenToSmsRetriever()  
  8.         {  
  9.             DependencyService.Get<IListenToSmsRetriever>()?.ListenToSmsRetriever();  
  10.         }  
  11.     }  
  12.     public interface IListenToSmsRetriever  
  13.     {  
  14.         void ListenToSmsRetriever();  
  15.     }  

Subscribe To Messaging Center 

 
Xamarin.Forms MessagingCenter enables different components to communicate without having to know anything about each other besides a simple Message contract. The MessagingCenter is a static class with Subscribe and Send methods that are used throughout the solution.
 
Subscribe 
 
Listen for messages with a certain signature and perform some action when they are received. Multiple subscribers can be listening to the same message.
 
Send 
 
Publish a message for listeners to act upon. If no listeners have subscribed then the message is ignored.
 
Here, we are creating a utility for subscribing to message listener. It will help when a new message is received.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using Xamarin.Forms;  
  5.   
  6. namespace ReadOTPXamarinAndroid  
  7. {  
  8.    public static class Utilities  
  9.     {  
  10.         private static readonly object cc = new object();  
  11.         public static void Subscribe<TArgs>(this object subscriber, Events eventSubscribed, Action<TArgs> callBack)  
  12.         {  
  13.             MessagingCenter.Subscribe(subscriber, eventSubscribed.ToString(), new Action<object, TArgs>((e, a) => { callBack(a); }));  
  14.         }  
  15.         public static void Notify<TArgs>(Events eventNotified, TArgs argument)  
  16.         {  
  17.             MessagingCenter.Send(cc, eventNotified.ToString(), argument);  
  18.         }  
  19.     }  
  20. }  
Create Enum for message Event type
  1. using System;  
  2. namespace ReadOTPXamarinAndroid  
  3. {  
  4.     public enum Events  
  5.     {  
  6.         SmsRecieved,  
  7.     }  
  8. }  

Code Behind Design View

 
The Code Behind adds the Subscribe message listener and if any message is received from message app with a specific format, it will get notified and read the message and assign the OTP value into the Entry Box.
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Linq;  
  5. using System.Text;  
  6. using System.Threading.Tasks;  
  7. using Xamarin.Forms;  
  8. using Xamarin.Forms.Xaml;  
  9. namespace ReadOTPXamarinAndroid  
  10. {  
  11.     public partial class MainPage : ContentPage  
  12.     {  
  13.         public MainPage()  
  14.         {  
  15.             InitializeComponent();  
  16.             this.Subscribe<string>(Events.SmsRecieved, code =>  
  17.             {  
  18.                 smSEntry.Text = code;  
  19.             });  
  20.         }  
  21.         private void ImageButton_OnClicked(object sender, EventArgs e)  
  22.         {  
  23.             CommonServices.ListenToSmsRetriever();  
  24.         }  
  25.     }  
  26. }  

ListenToSms Dependency

 
Let us start creating a dependency on the Android project. If you have not added Xamarin.GooglePlayServices.Auth NuGet package, try to add it before going to create the instance.
 
Get an instance of SmsRetrieverClient, used to start listening for a matching SMS message.
  1. SmsRetrieverClient client = SmsRetriever.GetClient(Application.Context); 
Starts SmsRetriever, which waits for ONE matching SMS message until timeout (5 minutes). The matching SMS message will be sent via a Broadcast Intent .
  1. var task = client.StartSmsRetriever(); 
Listen to the success/failure of the Start task.
  1. using Java.Lang;  
  2. using ReadOTPXamarinAndroid.Droid;  
  3. using Application = Android.App.Application;  
  4.   
  5. [assembly: Dependency(typeof(ListenToSms))]  
  6.   
  7. namespace ReadOTPXamarinAndroid.Droid  
  8. {  
  9.     public class ListenToSms : IListenToSmsRetriever  
  10.     {  
  11.         public void ListenToSmsRetriever()  
  12.         {  
  13.              
  14.             SmsRetrieverClient client = SmsRetriever.GetClient(Application.Context);  
  15.             var task = client.StartSmsRetriever();  
  16.             task.AddOnSuccessListener(new SuccessListener());  
  17.             task.AddOnFailureListener(new FailureListener());  
  18.         }  
  19.         private class SuccessListener : Object, IOnSuccessListener  
  20.         {  
  21.             public void OnSuccess(Object result)  
  22.             {  
  23.             }  
  24.         }  
  25.         private class FailureListener : Object, IOnFailureListener  
  26.         {  
  27.             public void OnFailure(Exception e)  
  28.             {  
  29.             }  
  30.         }  
  31.     }  
  32. }  

Broadcast Receiver

 
The BroadcastReceiver that will be listening to the broadcasts of the above SmsRetrieverClient.SMS Retriever API has provided us with an intent filter SmsRetriever.SmsRetrievedAction which we will use to register our BroadcastReceiver, which we’re going to name SMSBroadcastReceiver, and implement as below.
  1. using System.Linq;  
  2. using System.Text.RegularExpressions;  
  3. using Android.App;  
  4. using Android.Content;  
  5. using Android.Gms.Common.Apis;  
  6. using Com.Google.Android.Gms.Auth.Api.Phone;  
  7. using ReadOTPXamarinAndroid;  
  8.   
  9. namespace ReadOTPXamarinAndroid.Droid  
  10. {  
  11.     [BroadcastReceiver(Enabled = true, Exported = true)]  
  12.     [IntentFilter(new[] { SmsRetriever.SmsRetrievedAction })]  
  13.     public class SmsReceiver : BroadcastReceiver  
  14.     {  
  15.         private static readonly string[] OtpMessageBodyKeywordSet = { "DevEnvExe Generated OTP" }; //You must define your own Keywords  
  16.         public override void OnReceive(Context context, Intent intent)  
  17.         {  
  18.             try  
  19.             {  
  20.   
  21.                 if (intent.Action != SmsRetriever.SmsRetrievedAction) return;  
  22.                 var bundle = intent.Extras;  
  23.                 if (bundle == nullreturn;  
  24.                 var status = (Statuses)bundle.Get(SmsRetriever.ExtraStatus);  
  25.                 switch (status.StatusCode)  
  26.                 {  
  27.                     case CommonStatusCodes.Success:  
  28.                         var message = (string)bundle.Get(SmsRetriever.ExtraSmsMessage);  
  29.                         var foundKeyword = OtpMessageBodyKeywordSet.Any(k => message.Contains(k));  
  30.                         if (!foundKeyword) return;  
  31.                         var code = ExtractNumber(message);  
  32.                         Utilities.Notify(Events.SmsRecieved, code);  
  33.                         break;  
  34.                     case CommonStatusCodes.Timeout:  
  35.                         break;  
  36.                 }  
  37.   
  38.             }  
  39.             catch (System.Exception)  
  40.             {  
  41.                 // ignored  
  42.             }  
  43.         }  
  44.         private static string ExtractNumber(string text)  
  45.         {  
  46.             if (string.IsNullOrEmpty(text)) return "";  
  47.             var number = Regex.Match(text, @"\d+").Value;  
  48.             return number;  
  49.         }  
  50.     }  
  51. }  
We have done design and coding, you can follow the below steps for generating Application hash key as per SMS Format rule.
 

Generate Application Hash Key Helper

 
The hash string is made of your app’s package name and your app’s public key certificate. To generate the hash code, just run the following C# Method to generate a hash to be included in your SMS message.
 
You need to make sure you generate a hash key and append to the OTP message. Without the correct hash, your app won't receive the message callback.
 
The hash key will generate once per app and stored. Then, you can remove this helper class from your code and create a new class into the native Android project.
  1. using System;  
  2. using System.Linq;  
  3. using System.Text;  
  4. using Android.Content;  
  5. using Android.Content.PM;  
  6. using Android.Util;  
  7. using Java.Security;  
  8. using Java.Util;  
  9.   
  10. namespace ReadOTPXamarinAndroid.Droid.Helper  
  11. {  
  12.     public class AppHashKeyHelper  
  13.     {  
  14.         private static string HASH_TYPE = "SHA-256";  
  15.         private static int NUM_HASHED_BYTES = 9;  
  16.         private static int NUM_BASE64_CHAR = 11;  
  17.   
  18.         /// <summary>  
  19.         /// Retrieve the app signed package signature  
  20.         /// known as signed keystore file hex string  
  21.         /// </summary>  
  22.         /// <param name="context"></param>  
  23.         /// <returns></returns>  
  24.         private static string GetPackageSignature(Context context)  
  25.         {  
  26.             PackageManager packageManager = context.PackageManager;  
  27.             var signatures = packageManager.GetPackageInfo(context.PackageName, PackageInfoFlags.Signatures).Signatures;  
  28.             return signatures.First().ToCharsString();  
  29.         }  
  30.   
  31.         /// <summary>  
  32.         /// Gets the app hash key.  
  33.         /// </summary>  
  34.         /// <returns>The app hash key.</returns>  
  35.         /// <param name="context">Android app Context.</param>  
  36.         public static string GetAppHashKey(Context context)  
  37.         {  
  38.             string keystoreHexSignature = GetPackageSignature(context);  
  39.   
  40.             String appInfo = context.PackageName + " " + keystoreHexSignature;  
  41.             try  
  42.             {  
  43.                 MessageDigest messageDigest = MessageDigest.GetInstance(HASH_TYPE);  
  44.                 messageDigest.Update(Encoding.UTF8.GetBytes(appInfo));  
  45.                 byte[] hashSignature = messageDigest.Digest();  
  46.   
  47.                 hashSignature = Arrays.CopyOfRange(hashSignature, 0, NUM_HASHED_BYTES);  
  48.                 String base64Hash = Android.Util.Base64.EncodeToString(hashSignature, Base64Flags.NoPadding | Base64Flags.NoWrap);  
  49.                 base64Hash = base64Hash.Substring(0, NUM_BASE64_CHAR);  
  50.   
  51.                 return base64Hash;  
  52.             }  
  53.             catch (NoSuchAlgorithmException e)  
  54.             {  
  55.                 return null;  
  56.             }  
  57.         }  
  58.     }  
  59. }  
You can call the GetAppHashkey method into MainActivity and debug the solution and get the hash key value.
 
Verify OTP Automatically In Android Without SMS Read Permission Using Xamarin.Forms
 

Demo and Download Source Code

 
The Application is ready now and you can also download the source code from GitHub, start the application using Android Emulator and click Phone icon from extended controls and add the message as per follow format with the hash key.
 
  

No comments:

Post a Comment

Lab 09: Publish and subscribe to Event Grid events

  Microsoft Azure user interface Given the dynamic nature of Microsoft cloud tools, you might experience Azure UI changes that occur after t...