OTP (token) should be automatically read from the message

前端 未结 7 854
栀梦
栀梦 2020-12-04 10:50

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

7条回答
  •  长情又很酷
    2020-12-04 11:34

    With the SMS Retriever API, one can Read OTP without declaring android.permission.READ_SMS.

    1. 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")
            }
        }
    
    1. 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.*/       
        
            
                
            
        
    
    1. 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
    
    1. Be no longer than 140 bytes
    2. Begin with the prefix <#>
    3. 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

提交回复
热议问题