Many apps use phone numbers to verify the identity of a user. The app sends an SMS message containing an OTP (One Time Passcode) to the user, who then enters the OTP from the received SMS message to prove ownership of the phone number.
In Android, to provide a streamlined UX, an app may request the SMS read permission, and retrieve the OTP automatically. This is problematic since this permission allows the app to read other SMS messages which may contain the user's private information.
The SmsRetriever API solves this problem by providing app developers a way to automatically retrieve only the SMS directed to the app without asking for the SMS read permission or gaining the ability to read any other SMS messages on the device.
The recommended process for retrieving an SMS message using this API is as follows:
CredentialsApi
or manual input)
// Get an instance of SmsRetrieverClient, used to start listening for a
// matching SMS message.
SmsRetrieverClient client = SmsRetriever.getClient(context);
// Start SmsRetriever, which waits for ONE matching SMS message until timeout
// (5 minutes). The matching SMS message will be sent via a Broadcast Intent
// with action SmsRetriever#SMS_RETRIEVED_ACTION.
Task<Void> task = client.startSmsRetriever();
// Listen for success/failure of the start Task. If in a background thread,
// this can be made blocking using Tasks.await(task, [timeout]);
task.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
// Successfully started SmsRetriever
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// Failed to start SmsRetriever, inspect Exception for more details
});
);
Send SMS containing an OTP using existing infrastructure. However, the message template must be changed to include one of the following prefixes:
The SMS must also contain an 11-character string at the end of the SMS which is a hash string derived from the app package name and certificate. This is static and should be a whitelisted precomputed value and not supplied by the client app.
Given the app package name and the certificate, the hash string can be generated by the following steps:
input = "$package_name $certificate"
output = $(printf "$input" | shasum -a 256)
output = $(printf "$output" | cut -c1-18)
base64output = $(printf "$output" | xxd -r -p | base64 | cut -c1-11)
The rest of the SMS message is flexible: it can contain any content/instructions and be localized, etc.
Example SMS message contents:
[#] Use 123456 as your code for Example App FA+9qCX9VSuWhere 123456 is the OTP and the hashed string is inserted inline at the end of the SMS message. The OTP is human readable and could be typed in manually by the user if need be.
/**
* BroadcastReceiver to wait for SMS messages. This can be registered either
* in the AndroidManifest or at runtime. Should filter Intents on
* SmsRetriever.SMS_RETRIEVED_ACTION.
*/
public class MyBroadcastReceiver 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
break;
case CommonStatusCodes.TIMEOUT:
// Waiting for SMS timed out (5 minutes)
// Handle the error ...
break;
}
}
}
}
Once the SMS hash been retrieved, use a regular expression or similar logic to extract the code and send it to the server.
On the server, check that the code matches the one sent to the phone number being verified and complete the verification process by marking the number as verified and setting up user session, for example.
SmsRetrieverApi | API interface for SmsRetriever. |
SmsRetriever | The SmsRetriever API provides access to Google services that help you retrieve the SMS message directed to your app without asking for android.permission.READ_SMS. |
SmsRetrieverClient | The main entry point for interacting with SmsRetriever. |