Hello everyone.
In my last tutorial, we learned about the Jetpack Compose introduction and about applying the app theme for light and dark mode in Jetpack Compose and how to use layouts, rows, columns, modifiers and Constraint layouts. I recommended reading all these articles to better understand the Jetpack Compose.
In this tutorial, we are going to learn something about the SMS User Consent API and SMS Retriever API, which means the app can verify the OTP code which is received from the server for the user and allow the permission to read the OTP by SMS Retriever API to enter OTP automatically in the OTP field to verify from the server. Now you have to get confused here why are these two APIs SMS User Consent API and SMS Retriever API, and what is the basic difference between reading the OTP and verifying. So let’s get started to understand the basic difference in both APIs.
Difference Between SMS Retriever API and SMS User Consent API
Both the APIs are so similar. Both the API is used to read the SMS received on phone, but there are some important points. For example, if you are not the sender or you don’t want to add anything to the SMS you should use SMS User Consent API.
But if you are a sender and you accepting the adding anything to the SMS, then you should use SMS Retriever API. SMS Retriever API’s positive aspect is you don’t need any permission for reading the SMS. It just gives the SMS to you. So it means this API reduced the one step for the user to tap to allow the permission to read and use the OTP for verification.
Implement SMS User Consent API
Step 1: add the dependency
First of all, we need to add the corresponding dependency to our app.gradle
file.
implementation 'com.google.android.gms:play-services-auth:17.0.0' implementation 'com.google.android.gms:play-services-auth-api-phone:17.4.0'
Step 2: Start listening SMS user Consent
Now in your fragment or activity class on onViewCreated()/onCreate(),
we need to start listening for user Consent of SMS Retriever API.
private fun startListenSmsMessages() { // Start listening for SMS User Consent broadcasts from senderPhoneNumber // The Task<Void> will be successful if SmsRetriever was able to start // SMS User Consent, and will error if there was an error starting. context?.let { SmsRetriever.getClient(it).startSmsUserConsent(null) } }
Step 3: Add the broadcast to read the incoming SMS on your device,
We need to add the broadcast receive dynamically to register and unregister when the life cycle change.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) registerOTPSmsReceiver() }
override fun onDestroyView() { super.onDestroyView() unRegisterOTPSmsReceiver() }
private fun registerOTPSmsReceiver() { val smsIntentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION) context?.registerReceiver(smsReceiver, smsIntentFilter) } private fun unRegisterOTPSmsReceiver() { context?.unregisterReceiver(smsReceiver) }
private val SMS_CONSENT_REQUEST = 3 private val smsReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent?) { if (SmsRetriever.SMS_RETRIEVED_ACTION == intent?.action) { val smsRetrieverStatus = intent.extras?.get(SmsRetriever.EXTRA_STATUS) as com.google.android.gms.common.api.Status when (smsRetrieverStatus.statusCode) { CommonStatusCodes.SUCCESS -> { val consentIntent = intent.extras?.getParcelable<Intent>(SmsRetriever.EXTRA_CONSENT_INTENT) try { startActivityForResult(consentIntent, SMS_CONSENT_REQUEST) } catch (e: ActivityNotFoundException) { // Record the exception ... } } CommonStatusCodes.TIMEOUT -> { // don't need to do anything } } } } }
Step 4: Get the code from the Intent
Now we are ready for reading the SMS. If you get a result code of RESULT_OK
, the user is granted permission to read the contents of the message, and we can get the message text from the intent.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { SMS_CONSENT_REQUEST -> { if (resultCode == Activity.RESULT_OK && data != null) { val message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE) val otp = parseOTPFromMessage(message.orEmpty()) // set the otp code. binding.otpTextView.setText(otp.toString()) } else { Log.v("OTP Screen", "OTP Consent denied. User can type OTC manually.") } } } } fun parseOTPFromMessage(message: String): String { val pattern = Pattern.compile("(\\d{6})") //to prevent username has 6 digits,need to extract otp message val otpMessage = message.substringAfter("otp code") val matcher: Matcher = pattern.matcher(otpMessage) var otp = "" if (matcher.find()) { otp = matcher.group(0) // 6 digit number } return otp }
Implement SMS Retriever API
With the SMS Retriever API, you can perform SMS-based user verification in your Android app automatically, without requiring the user to manually type verification codes, and without requiring any extra app permissions.
To implement the SMS Retriever API, all the above steps are required, Only we need to add additional String in the User SMS that is hash code
, So that the Google Play services understand that we need to read this SMS without user permission for this app.
According to Android SMS Retriever API, every message should follow a specific format which must include “One time code” and “11 character hash string” as shown in the below.
Your verification code : 123456 FA+9qCX9VSu
Your app’s hash string is the first 11 characters of the base64-encoded hash. Which may be different for different build variant. It depends on you if you want to user separate key store to sign the apk or use in key store file to sign the apk file for debug, release and production. Let’s assume we have separate keystore file to sign the app.
To generate hash keys, Google has provided a script to get the hash key from key-store file.
To generate hash keys from Sever:
SMS_RETRIEVER_HASH_KEY_GENERATOR
Save this to a sms_retriever_hash_v9.sh
file.
1. For Debug Builds
First of all find the application’s debug.keystore
file and make a copy and place it where you saved the sms_retriever_hash_v9.sh
file. Then use the following command to retrieve the hash key for debug releases of your application.
./sms_retriever_hash_v9.sh — package “com.your.packagename” — keystore /path/to/debug.keystore
This will provide the base64
encode hash code.
First 8 bytes encoded by base64: FA+9qCX9VSu SMS Retriever hash code: FA+9qCX9VSu
2. For release Builds
I am assuming that we have already generated the release.keystore
file. Now your release.keystore
file is ready and you can proceed the same in the method 1 to generate the 11 character hash code.
./sms_retriever_hash_v9.sh — package “com.your.packagename” — keystore /path/to/release.keystore
NOTE — By using this you can retrieve the hash key. You can use this in release builds, but when you upload your
.aab
file orapk
files to playstore this hash code won’t be work and One Time Code doesn’t pick from your application. In this case you have to follow different set of approach to achieve this.
3. For production Builds
Login to your play console and go to your app. Then select Release->Setup->App Signing
Then you can see Download Certificate link in the right corner. This will download a file named ‘
deployment_cert.der
’
and you cannot use this directly to generate hash code. At first you must generate .jks
file using the following command.
keytool -importcert -alias <ALIAS USED TO GENERATE JKS FILE> -file deployment_cert.der -keystore certificate.jks -storepass <PASSWORD FOR JKS FILE>
It will create temp certificate file which can be used for generating the hash key. Keytool
command can be run from java bin path. For window -> Go to C:\Program Files\Java\jdkx.x.x_x\bin and for Mac cd /your/jdk/path
Then this will prompt “Trust this certificate? [no]:” message and provide ‘Yes’ as the answer. Then your certificate will be added to the keystore and you can find the file generated “certificate.jks”.
Now as the final step you can use the certificate.jks
file and the following command.
keytool -exportcert -alias <ALIAS USED TO GENERATE JKS FILE> -keystore certificate.jks | xxd -p | tr -d "[:space:]" | echo -n <PACKAGE NAME> `cat` | shasum -a 256 | tr -d "[:space:]-" | xxd -r -p | base64 | cut -c1-11
Then this will prompt to enter the store password used for generating .jks
file. Enter the password to get the hash code.
Now finally you can get the hash code as follows FA+9qCX9VSu
That is all, now server can add this hash key in SMS So the google pay service can understand that I can read the SMS without user permission. Now I hope you got the basic difference of both API use-case. I prefer to use SMS Retriever API which provide the great experience for the user.
Here are few resources that I used for this article resource1 and resource2.
I am a very enthusiastic Android developer to build solid Android apps. I have a keen interest in developing for Android and have published apps to the Google Play Store. I always open to learning new technologies. For any help drop us a line anytime at contact@mobologicplus.com