I am working on an Android App, in which the server sends an OTP and the user needs to enter this OTP in the App, to SignUp for my App. What I want is, that my App should be
With the SMS Retriever API, one can Read OTP without declaring android.permission.READ_SMS.
- Start the SMS retriever
private fun startSMSRetriever() {
// Get an instance of SmsRetrieverClient, used to start listening for a matching SMS message.
val client = SmsRetriever.getClient(this /* context */);
// Starts 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.
val task: 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 {
Log.d("SmsRetriever", "SmsRetriever Start Success")
}
task.addOnFailureListener {
Log.d("SmsRetriever", "SmsRetriever Start Failed")
}
}
- Receive messages via Broadcast
public class MySMSBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (SmsRetriever.SMS_RETRIEVED_ACTION == intent?.action && intent.extras!=null) {
val extras = intent.extras
val status = extras.get(SmsRetriever.EXTRA_STATUS) as Status
when (status.statusCode) {
CommonStatusCodes.SUCCESS -> {
// Get SMS message contents
val message = extras.get(SmsRetriever.EXTRA_SMS_MESSAGE) as String
Log.e("Message", message);
// Extract one-time code from the message and complete verification
// by sending the code back to your server.
}
CommonStatusCodes.TIMEOUT -> {
// Waiting for SMS timed out (5 minutes)
// Handle the error ...
}
}
}
}
}
/**Don't forgot to define BroadcastReceiver in AndroidManifest.xml.*/
- Send the one-time code from the verification message to your server
Make sure your SMS format is exactly as below:
<#> Your ExampleApp code is: 123ABC78
fBzOyyp9h6L
End with an 11-character hash string that identifies your app
You can compute app hash with following code:
import android.content.Context
import android.content.ContextWrapper
import android.content.pm.PackageManager
import android.util.Base64
import android.util.Log
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*
/**
* This is a helper class to generate your message hash to be included in your SMS message.
*
* Without the correct hash, your app won't recieve the message callback. This only needs to be
* generated once per app and stored. Then you can remove this helper class from your code.
*
* For More Detail: https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string
*
*/
public class AppSignatureHelper(private val context: Context) : ContextWrapper(context) {
companion object {
val TAG = AppSignatureHelper::class.java.simpleName;
private const val HASH_TYPE = "SHA-256";
const val NUM_HASHED_BYTES = 9;
const val NUM_BASE64_CHAR = 11;
}
/**
* Get all the app signatures for the current package
* @return
*/
public fun getAppSignatures(): ArrayList {
val appCodes = ArrayList();
try {
// Get all package signatures for the current package
val signatures = packageManager.getPackageInfo(
packageName,
PackageManager.GET_SIGNATURES
).signatures;
// For each signature create a compatible hash
for (signature in signatures) {
val hash = hash(packageName, signature.toCharsString());
if (hash != null) {
appCodes.add(String.format("%s", hash));
}
}
} catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "Unable to find package to obtain hash.", e);
}
return appCodes;
}
private fun hash(packageName: String, signature: String): String? {
val appInfo = "$packageName $signature";
try {
val messageDigest = MessageDigest.getInstance(HASH_TYPE);
messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8));
var hashSignature = messageDigest.digest();
// truncated into NUM_HASHED_BYTES
hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
// encode into Base64
var base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING or Base64.NO_WRAP);
base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);
Log.e(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash));
return base64Hash;
} catch (e: NoSuchAlgorithmException) {
Log.e(TAG, "hash:NoSuchAlgorithm", e);
}
return null;
}
}
Required Gradle :
implementation "com.google.android.gms:play-services-auth-api-phone:16.0.0"
References:
https://developers.google.com/identity/sms-retriever/overview
https://developers.google.com/identity/sms-retriever/request
https://developers.google.com/identity/sms-retriever/verify