Secure Android OTP & Account Verification using the SMS Retriever API

Wilder Pereira
Nagoya Foundation
Published in
4 min readJan 7, 2019

In October, Google has announced updates to improve the user's privacy that will limit the use of the Call Log permission group and SMS permission group. Such permissions allows developers to manage phone calls, read and write SMS, among other things.

But after January 9, 2019, only approved apps that have as a critical core app functionality to manage SMS messages or phone calls will be able to do it. The data retrieved from it cannot be sold, transferred or used for marketing purposes. From now, almost every app that declares any of the permissions that belongs to those groups in the Manifest will need to be updated to remove the permissions and migrate to an alternative implementation.

A common use case of the RECEIVE_SMS and READ_SMS permissions is for One Time Password and Account Verification. The use of those permissions for this purpose is not secure and could expose the user's data. But relax, there are safer alternatives.

One alternative, is to use the SMS Retriever API.

SMS Retriever API

With the SMS Retriever API, you can perform the account verification without requiring the user to type the verification code and without the need of any special app permissions.

Common Verification Flow — Google: https://developers.google.com/identity/sms-retriever/overview

This can be done in a few steps, and it require both server and client side implementations.

To keep the article clean, for now, it will cover only the client side implementation. (The server side implementation will be available here soon!).

The verification flow

Once the app initializes the Account Verification flow, it must get the user's phone number, send it to the server and listen for the SMS. If it meets all of your use case's requirements on the server, the server must send an SMS with the generated code and a hash code generated from your APK. The code will be retrieved automatically and the client must send it to the server to complete the flow.

Implementing the SMS Verification

As mentioned before, we wont cover the backend implementation of the Sms Verification. But one important thing to keep in mind is that to use the SMS Verification API, the SMS message sent from the server must have a specific structure.

SMS Message Structure

The SMS message must begin with one of the following prefixes:

  • <#>
  • \u200b\u200b

The first one is visible and will appear in the message contents, and the latter is an invisible unicode string, but requires unicode support for the message.

The message must end with a string derived from the app's package name and certificate and be at most 140 bytes.

A valid message should look like this:

<#> Your code is: 401947
FA+9qCX9VSu

The hash string is made of your app’s package name and your app’s public key certificate. To generate the hash code, just run the following command:

keytool -exportcert -alias MyAndroidKeyStoreAlias -keystore MyKeys.keystore | xxd -p | tr -d "[:space:]" | echo -n com.example.myapp `cat` | sha256sum | tr -d "[:space:]-" | xxd -r -p | base64 | cut -c1-11

Make sure to replace MyAndroidKeyStoreAlias with the alias of your app's keystore, MyKeys.keystore with your keystore and com.example.myapp with your app's package name.

OBS:
- If you're running the app on debug mode, you might sign the debug apk and get the certificate from KeyStore used; (more details here).
- If you're on Mac OS, you can use shasum -a 256 instead of sha256sum.

Prerequisite

The SMS Retriever API is available only on Android devices with Play services version 10.2 and newer.

Dependencies

Let's start by adding the SmsRetriever API dependency to the app's build.gradle file:

Start the SMS retriever

After getting the user’s phone number (won’t be covered in this post, but checkout the demo app), we need to create an instance of the SmsRetrieverClient, start the retriever, and listen for success and failures.

The startSmsRetriever method waits for the matching SMS message with a timeout of 5 minutes.The matching SMS message will be sent via a Broadcast Intent with the SMS_RETRIEVED_ACTION.

Now, we must create the BroadcastReceiver to wait for SMS messages.

Let's not forget to register the BroadcastReceiver in the Manifest.xml:

After extracting the code, the client app should send the extracted code to the server so it can complete the verification.

References

https://stackoverflow.com/questions/16965058/where-is-debug-keystore-in-android-studio

The full working code can be found here: https://github.com/wilder/SmsRetrieverApiExample

--

--