Implement the SMS User Consent API and SMS Retriever API in Android

Sharing is caring!

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 from Sever:

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 or apk 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

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.

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.

0 0 votes
Article Rating
Implement the SMS User Consent API and SMS Retriever API in Android
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Scroll to top
0
Would love your thoughts, please comment.x
()
x