Android One-tap SMS verification with the SMS User Consent API

The SMS User Consent API allows the app to prompt the user to grant access to the content of a single SMS message. When a user gives consent, the app will then have access to the entire message body to automatically complete SMS verification.

Sometimes you don’t have control over the contents of the message for example if your app works with a financial institution that might want to verify the user’s phone number before approving a payment transaction inside your app, then you can use the SMS User Consent API. The API requires the user to approve your app’s request to access the message containing the verification code. To minimize the chances of surfacing the wrong message to the user, SMS User Consent will check if the message contains a 4-10 character alphanumeric code containing at least one number. It will also filter out messages from senders in the user’s contacts list.

User Flow for SMS User Consent API

The whole working of the SMS User Consent API can be divided into 3 steps: Start, Prompt, Read

Step 1: Start
To use it, you need to start SMS User Consent API before sending the message.

Step 2: Prompt
Prompt phase means to request consent to read the message. Google Play Services is notified about the message and makes your application to show a prompt to the user for granting permission about reading the message that contains verification code.

Note: A second method, e.g. using a keyboard, for entering verification code should be implemented to handle the situation where the user receives the message on a different device.

Step 3: Read
Read phase means to read the message to parse the verification code and complete the process. If the user gave prompt to read message, the message is shared with the app and the API automatically completes the verification phase. If the user did not give the prompt, the phase will be waiting to be completed by the user manually.

Note: If the sender of the message is in the user’s contact list, the message will be never shown.

Message Criteria

The SMS User Consent API will be activated only if the below criteria’s are matched.

  1. The message should contain some verification code.
  2. The verification code should be 4–10 digit alphanumeric and 1 digit must be a number.
  3. The message containing the verification code should not be received from any of the user’s contacts.
  4. The API will look for the verification code for a maximum time of 5 minutes.

Step by Step Development Flow

1. The SMS User Consent API depends on Google Play Services.

implementation ''
implementation ''

2. Basically, to send a message to the user, you need to know the user’s phone number. So somehow you should get it application’s UX flow. But if you do not have the phone number, as Google suggested, you can consider using Smart Lock for Passwords hint selector.

private val CREDENTIAL_PICKER_REQUEST = 1  // Set to an unused request code

// Construct a request for phone numbers and show the picker
private fun requestHint() {
    val hintRequest = HintRequest.Builder()
    val credentialsClient = Credentials.getClient(this)
    val intent = credentialsClient.getHintPickerIntent(hintRequest)
        null, 0, 0, 0

public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
            // Obtain the phone number from the result
            if (resultCode == Activity.RESULT_OK && data != null) {
                val credential = data.getParcelableExtra<Credential>(Credential.EXTRA_KEY)
                // credential.getId();  <-- will need to process phone number string
        // ...

3. Start listening for incoming messages.

To listen for incoming messages, just call startSmsUserConsent method of the API. If you know the sender number of the message, then pass it to the method, but if you do not know it just pass null as a parameter. If you pass null, the API listen for all messages; if you pass number, then the API listens only for that number.

As you call the startSmsUserConsent, the 5 minute-length listening for the message starts. When the API matches a messages, in that listening time, Play services will broadcast to the app an intent to prompt.

// 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.
val task = SmsRetriever.getClient(context).startSmsUserConsent(senderPhoneNumber /* or null */)

4. Listen for the SMS_RETRIEVED_ACTION intent.

Handle the broadcasts with a broadcast receiver that responds to SMS_RETRIEVED_ACTION intents. By starting an activity for EXTRA_CONSENT_INTENT, you prompt the user for one-time permission to read the contents of the message.

To create and register the broadcast receiver:

private val SMS_CONSENT_REQUEST = 2  // Set to an unused request code
private val smsVerificationReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
            val extras = intent.extras
            val smsRetrieverStatus = extras?.get(SmsRetriever.EXTRA_STATUS) as Status

            when (smsRetrieverStatus.statusCode) {
                CommonStatusCodes.SUCCESS -> {
                    // Get consent intent
                    val consentIntent = extras.getParcelable<Intent>(SmsRetriever.EXTRA_CONSENT_INTENT)
                    try {
                        // Start activity to show consent dialog to user, activity must be started in
                        // 5 minutes, otherwise you'll receive another TIMEOUT intent
                        startActivityForResult(consentIntent, SMS_CONSENT_REQUEST)
                    } catch (e: ActivityNotFoundException) {
                        // Handle the exception ...
                CommonStatusCodes.TIMEOUT -> {
                    // Time out occurred, handle the error.

override fun onCreate(savedInstanceState: Bundle?) {
    // ...

    val intentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
    registerReceiver(smsVerificationReceiver, intentFilter)

5. Parse the verification code from a message.

Handle the user’s response to your request for permission within the onActivityResult() method. If you get a result code of RESULT_OK, the user granted permission to read the message, and you can parse the message text.

public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
        // ...
            // Obtain the phone number from the result
            if (resultCode == Activity.RESULT_OK && data != null) {
                // Get SMS message content
                val message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
                // Extract one-time code from the message and complete verification
                // `message` contains the entire text of the SMS message, so you will need
                // to parse the string.
                val oneTimeCode = parseOneTimeCode(message) // define this function

                // send one time code to the server
            } else {
                // Consent denied. User can type OTC manually.

6. Now you have the verification, continue the app’s flow of verification.