Android One-Tap SMS Verification With the SMS User Consent API
Get started with the SMS User Consent API.
Join the DZone community and get the full member experience.
Join For FreeThe 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 three steps: start, prompt, and 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 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 a prompt to read the 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.
- The message should contain some verification code.
- The verification code should be 4–10 digit alphanumeric and 1 digit must be a number.
- The message containing the verification code should not be received from any of the user’s contacts.
- The API will look for the verification code for a maximum time of 5 minutes.
Step-by-Step Development Flow
The SMS User Consent API depends on Google Play Services.
implementation 'com.google.android.gms:play-services-auth:17.0.0'
implementation 'com.google.android.gms:play-services-auth-api-phone:17.1.0'
Basically, to send a message to the user, you need to know the user’s phone number. So, somehow you should get its 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()
.setPhoneNumberIdentifierSupported(true)
.build()
val credentialsClient = Credentials.getClient(this)
val intent = credentialsClient.getHintPickerIntent(hintRequest)
startIntentSenderForResult(
intent.intentSender,
CREDENTIAL_PICKER_REQUEST,
null, 0, 0, 0
)
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
CREDENTIAL_PICKER_REQUEST ->
// 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
}
// ...
}
}
Start Listening for Incoming Messages.
To listen for incoming messages, just call the startSmsUserConsent method of the API. If you know the sender number of the message, then pass it to the method; 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 five 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 */)
Listen to 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)
}
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) {
// ...
SMS_CONSENT_REQUEST ->
// 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.
}
}
}
Now, you have the verification, and you can continue the app’s flow of verification. That's all folks.
References
- https://developers.google.com/identity/sms-retriever
Further Reading
Published at DZone with permission of Alaattin KAYRAK. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments