Content posted here with the permission of the author Chandrashekhar Sahu, who is currently employed at Josh Software. Original post available here.
The Android app needs SMS receive/read permission to retrieve SMS content.
Imagine an application where the use case is to get the SMS only for validating the user using OTP. And rest of the app does not use SMS reading feature again. Then in this case, it is a waste of the resources & time and of course code to check the SMS permissions.
To solve this problem, Google has introduced SMS Retriever API, this API allows to retrieve the OTP without needing of the SMS permission in your application.

Image Credit: Google
Dependency for SMS Retriever API
implementation 'com.google.android.gms:play-services-base:16.0.1' implementation 'com.google.android.gms:play-services-identity:16.0.0' implementation 'com.google.android.gms:play-services-auth:16.0.1' implementation 'com.google.android.gms:play-services-auth-api-phone:16.0.0'
Obtain the user’s phone number (Phone Selector API)
First, we need the number of the user on which the OTP will be received. We create a hint request object and set the phone number identifier supported field to true.
HintRequest hintRequest = new HintRequest.Builder() .setHintPickerConfig(newCredentialPickerConfig.Builder().setShowCancelButton(true).build()) .setPhoneNumberIdentifierSupported(true) .build();
Then, we get a pending intent from that hint request for the phone number selector dialogue.
GoogleApiClient apiClient = new GoogleApiClient.Builder(getContext()).addApi(Auth.CREDENTIALS_API).enableAutoManage(getActivity(), GoogleApiHelper.getSafeAutoManageId(), new GoogleApiClient.OnConnectionFailedListener() { @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { Log.e(TAG, "Client connection failed: " + connectionResult.getErrorMessage()); }).build(); PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent(apiClent, hintRequest); startIntentSenderForResult(intent.getIntentSender(),RESOLVE_HINT, null,0,0,0);
Once the user selects the phone number, that phone number is returned to our app in the onActivityResult()
.
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == RC_PHONE_HINT) { if (data != null) { Credential cred = data.getParcelableExtra(Credential.EXTRA_KEY); if (cred != null) { final String unformattedPhone = cred.getId(); } } } }
Start the SMS retriever
When we are ready to verify the user’s phone number, get an instance of the SmsRetrieverClient
object. Will call startSmsRetriever
and attach success and failure listeners to the SMS retrieval task:
SmsRetrieverClient client = SmsRetriever.getClient(mContext); // Starts SmsRetriever, waits for ONE matching SMS message until timeout // (5 minutes). Task<Void> task = client.startSmsRetriever(); // Listen for success/failure of the start Task. task.addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { // Log.d(TAG,"Successfully started retriever"); } }); task.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Log.e(TAG, "Failed to start retriever"); }); );
Our server can then send the message to the phone using existing SMS infrastructure or service. When this message is received, Google Play services broadcasts an intent which contains the text of the message.
public class MySMSBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) { Bundle extras = intent.getExtras(); Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS); switch (status.getStatusCode()) { case CommonStatusCodes.SUCCESS: // Get SMS message contents String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE); // Extract one-time code from the message and complete verification // by sending the code back to your server for SMS authenticity. break; case CommonStatusCodes.TIMEOUT: // Waiting for SMS timed out (5 minutes) // Handle the error ... break; } } } }
We need to register this BroadcastReceiver in our Manifest file as follows
<receiver android:name=".MySMSBroadcastReceiver" android:exported="true"> <intent-filter> <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" /> </intent-filter> </receiver>
Construct a verification message:
When our server receives a request to verify a phone number, first construct the verification message that you will send to the user’s device. This message must:
- Be no longer than 140 bytes
- Begin with the prefix
<#>
- Contain a one-time code that the client sends back to your server to complete the verification flow (see Generating a one-time code)
- End with an 11-character hash string that identifies your app (see Computing your app’s hash string)
Otherwise, the contents of the verification message can be whatever you choose. It is helpful to create a message from which you can easily extract the one-time code later on. For example, a valid verification message might look like the following:
<#> Use 123456 as your verification code FC+7qAH5AZu
Optional: Save the phone number with Smart Lock for Passwords
Optionally, after the user has verified their phone number, We can prompt the user to save this phone number account with Smart Lock for Passwords so it will be available automatically in other apps and on other devices without having to type or select the phone number again.
Click here to get the source code.
Happy Coding 🙂
what kind of side server are you using for this SMS API ?