web-dev-qa-db-fra.com

OTP (token) devrait être lu automatiquement à partir du message

Je travaille sur une application Android, sur laquelle le serveur envoie un OTP et l'utilisateur doit entrer ce OTP dans l'application pour s'inscrire pour mon application. Ce que je veux, c'est que mon application soit capable de lire automatiquement l'OTP envoyé par le serveur. Comment puis-je atteindre cet objectif? Toute aide ou orientation à cet égard serait hautement appréciée.

31
user1903022

Je vous recommanderai de ne pas utiliser de bibliothèques tierces pour la récupération automatique d'OTP à partir de la boîte de réception SMS . Cela peut être fait facilement si vous avez une connaissance de base de Broadcast Receiver et de son fonctionnement . Essayez juste de suivre approche:

Étape 1) Créer une interface unique, à savoir SmsListner

package com.wnrcorp.reba;
public interface SmsListener{
public void messageReceived(String messageText);}

Étape 2) Créer un récepteur de radiodiffusion unique, à savoir SmsReceiver

package com.wnrcorp.reba;
import Android.content.BroadcastReceiver;
import Android.content.Context;
import Android.content.Intent;
import Android.os.Bundle;
import Android.telephony.SmsMessage;
public class SmsReceiver extends BroadcastReceiver {
private static SmsListener mListener;
Boolean b;
String abcd,xyz;
@Override
public void onReceive(Context context, Intent intent) {
Bundle data  = intent.getExtras();
Object[] pdus = (Object[]) data.get("pdus");
    for(int i=0;i<pdus.length;i++){
        SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
        String sender = smsMessage.getDisplayOriginatingAddress();
       // b=sender.endsWith("WNRCRP");  //Just to fetch otp sent from WNRCRP
        String messageBody = smsMessage.getMessageBody();
       abcd=messageBody.replaceAll("[^0-9]","");   // here abcd contains otp 
        which is in number format
        //Pass on the text to our listener.
        if(b==true) {
            mListener.messageReceived(abcd);  // attach value to interface 
  object
        }
        else
        {
        }
    }
}
public static void bindListener(SmsListener listener) {
    mListener = listener;
}
}

Étape 3) Ajouter un auditeur i.e, le récepteur de radiodiffusion dans le fichier manifeste Android

<receiver Android:name=".SmsReceiver">    
        <intent-filter>
            <action Android:name="Android.provider.Telephony.SMS_RECEIVED"/>
        </intent-filter>
</receiver>

et ajouter la permission

<uses-permission Android:name="Android.permission.RECEIVE_SMS"/>

Final Step 4) L'activité dans laquelle vous allez chercher automatiquement un otp quand il est reçu dans la boîte de réception. Dans mon cas, je vais chercher otp et mets le champ edittext.

public class OtpVerificationActivity extends AppCompatActivity {
EditText ed;
TextView tv;
String otp_generated,contactNo,id1;
GlobalData Gd = new GlobalData();
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_otp_verification);
    ed=(EditText)findViewById(R.id.otp);
    tv=(TextView) findViewById(R.id.verify_otp); 
    /*This is important because this will be called every time you receive 
     any sms */            
 SmsReceiver.bindListener(new SmsListener() {
        @Override
        public void messageReceived(String messageText) {
            ed.setText(messageText);     
        }
    });
    tv.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            try
            {
                InputMethodManager imm=
  (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);                    
  imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
            }
            catch(Exception e)
            {}           
            if (ed.getText().toString().equals(otp_generated))
            {
                Toast.makeText(OtpVerificationActivity.this, "OTP Verified 
       Successfully !", Toast.LENGTH_SHORT).show();           
             }
    });
   }
}

Fichier de mise en forme pour OtpVerificationActivity 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:id="@+id/activity_otp_verification"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:paddingBottom="@dimen/activity_vertical_margin"
Android:paddingLeft="@dimen/activity_horizontal_margin"
Android:paddingRight="@dimen/activity_horizontal_margin"
Android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.wnrcorp.reba.OtpVerificationActivity">
<Android.support.v7.widget.CardView
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:id="@+id/firstcard"
    xmlns:card_view="http://schemas.Android.com/apk/res-auto"
    card_view:cardCornerRadius="10dp"
    >
   <LinearLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:orientation="vertical"
        Android:background="@Android:color/white">
        <TextView
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="OTP Confirmation"
            Android:textSize="18sp"
            Android:textStyle="bold"
            Android:id="@+id/dialogTitle"
            Android:layout_margin="5dp"
            Android:layout_gravity="center"
            />
        <EditText
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:id="@+id/otp"
            Android:layout_margin="5dp"
            Android:hint="OTP Here"
            />
        <TextView
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="Verify"
            Android:textSize="18sp"
            Android:id="@+id/verify_otp"
            Android:gravity="center"
            Android:padding="10dp"
            Android:layout_gravity="center"
            Android:visibility="visible"
            Android:layout_margin="5dp"
            Android:background="@color/colorPrimary"
            Android:textColor="#ffffff"
            />
        </LinearLayout>
        </Android.support.v7.widget.CardView>
        </RelativeLayout>

Captures d'écran de l'activité de vérification d'OTP où vous extrayez d'OTP sous la forme de messages Reçus enter image description here

25
brijexecon

Vous pouvez essayer d’utiliser une simple bibliothèque like

Après l'installation via gradle et l'ajout d'autorisations, démarrez SmsVerifyCatcher selon une méthode similaire à l'activité onCreate

    smsVerifyCatcher = new SmsVerifyCatcher(this, new OnSmsCatchListener<String>() {
    @Override
    public void onSmsCatch(String message) {
        String code = parseCode(message);//Parse verification code
        etCode.setText(code);//set code in edit text
        //then you can send verification code to server
    }
});

En outre, substituez les méthodes de cycle de vie d'activité:

  @Override
protected void onStart() {
    super.onStart();
    smsVerifyCatcher.onStart();
}

@Override
protected void onStop() {
    super.onStop();
    smsVerifyCatcher.onStop();
}

/**
 * need for Android 6 real time permissions
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    smsVerifyCatcher.onRequestPermissionsResult(requestCode, permissions, grantResults);
}


public String parseCode(String message) {
    Pattern p = Pattern.compile("\\b\\d{4}\\b");
    Matcher m = p.matcher(message);
    String code = "";
    while (m.find()) {
        code = m.group(0);
    }
    return code;
}
17
smy

Cela m'a aidé et a également travaillé pour moi:

http://androiddhina.blogspot.in/2015/06/reading-incoming-message-automatically-to-verify-OTP.html

Veuillez aussi n'oubliez pas de faire static à votre EditText à partir de votre Activity/Fragment

6
karan

J'ai mis en œuvre quelque chose de ce genre. Mais voici ce que j'ai fait lorsque le message est arrivé: je récupère uniquement le code à six chiffres, le regroupe dans une intention et l'envoie à l'activité ou au fragment en ayant besoin et vérifie le code. L'exemple vous montre déjà comment obtenir le sms. Consultez le code ci-dessous pour savoir comment envoyer à l'aide de LocalBrodcastManager et si votre message contient plus de textes, par exemple, Salutations E.g, faites-en la normalisation pour mieux vous aider. E.g "Votre code de vérification est: 84HG73" vous pouvez créer un motif regex comme ce ([0-9]){2}([A-Z]){2}([0-9]){2} qui signifie deux ints, deux [majuscules] et deux ints. Bonne chance!

Après avoir supprimé toutes les informations inutiles du message

 Intent intent = new Intent("AddedItem");
 intent.putExtra("items", code);
 LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent); 

Et le fragment/activité qui le reçoit

@Override
public void onResume() {
    LocalBroadcastManager.getInstance(getActivity()).registerReceiver(receiver, new IntentFilter("AddedItem"));
    super.onResume();
}

@Override
public void onPause() {
    super.onDestroy();
    LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(receiver);
}

Et le code destiné à gérer la charge utile que vous avez collectée

 private BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction()) {
            final String message = intent.getStringExtra("message");
            //Do whatever you want with the code here
        }
    }
};

Est-ce que ça aide un peu? Je l'ai fait mieux en utilisant Callbacks

4
Abubakar Oladeji

Désolé pour la réponse tardive mais toujours envie de poster ma réponse si cela aide. Il fonctionne pour 6 chiffres OTP.

    @Override
    public void onOTPReceived(String messageBody)
    {
        Pattern pattern = Pattern.compile(SMSReceiver.OTP_REGEX);
        Matcher matcher = pattern.matcher(messageBody);
        String otp = HkpConstants.EMPTY;
        while (matcher.find())
        {
            otp = matcher.group();
        }
        checkAndSetOTP(otp);
    }
Adding constants here

public static final String OTP_REGEX = "[0-9]{1,6}";

Pour l'auditeur SMS, on peut suivre la classe ci-dessous

public class SMSReceiver extends BroadcastReceiver
{
    public static final String SMS_BUNDLE = "pdus";
    public static final String OTP_REGEX = "[0-9]{1,6}";
    private static final String FORMAT = "format";

    private OnOTPSMSReceivedListener otpSMSListener;

    public SMSReceiver(OnOTPSMSReceivedListener listener)
    {
        otpSMSListener = listener;
    }

    @Override
    public void onReceive(Context context, Intent intent)
    {
        Bundle intentExtras = intent.getExtras();
        if (intentExtras != null)
        {
            Object[] sms_bundle = (Object[]) intentExtras.get(SMS_BUNDLE);
            String format = intent.getStringExtra(FORMAT);
            if (sms_bundle != null)
            {
                otpSMSListener.onOTPSMSReceived(format, sms_bundle);
            }
            else {
                // do nothing
            }
        }
    }

    @FunctionalInterface
    public interface OnOTPSMSReceivedListener
    {
        void onOTPSMSReceived(@Nullable String format, Object... smsBundle);
    }
}

    @Override
    public void onOTPSMSReceived(@Nullable String format, Object... smsBundle)
    {
        for (Object aSmsBundle : smsBundle)
        {
            SmsMessage smsMessage = getIncomingMessage(format, aSmsBundle);
            String sender = smsMessage.getDisplayOriginatingAddress();
            if (sender.toLowerCase().contains(ONEMG))
            {
                getIncomingMessage(smsMessage.getMessageBody());
            } else
            {
                // do nothing
            }
        }
    }

    private SmsMessage getIncomingMessage(@Nullable String format, Object aObject)
    {
        SmsMessage currentSMS;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && format != null)
        {
            currentSMS = SmsMessage.createFromPdu((byte[]) aObject, format);
        } else
        {
            currentSMS = SmsMessage.createFromPdu((byte[]) aObject);
        }

        return currentSMS;
    }
3
Raghav Sharma

Avec l’API SMS Retriever, il est possible de lire le protocole OTP sans déclarer Android.permission.READ_SMS.

  1. Démarrer le SMS récupérateur
    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<Void> = 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. Recevoir des messages via diffusion
    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.*/       
    <receiver Android:name=".MySMSBroadcastReceiver" Android:exported="true">
        <intent-filter>
            <action Android:name="com.google.Android.gms.auth.api.phone.SMS_RETRIEVED"/>
        </intent-filter>
    </receiver>
  1. Envoyez le code à usage unique du message de vérification à votre serveur

Assurez-vous que votre format SMS est exactement comme ci-dessous:

<#> Your ExampleApp code is: 123ABC78
fBzOyyp9h6L
  1. Ne pas être plus de 140 octets
  2. Commencez par le préfixe <#>
  3. Terminez avec une chaîne de hachage de 11 caractères identifiant votre application.

    Vous pouvez calculer le hachage de l'application avec le code suivant:

    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<String> {
            val appCodes = ArrayList<String>();
    
            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;
        }
    }       
    

Niveau requis:

implementation "com.google.Android.gms:play-services-auth-api-phone:16.0.0"

Références:
https://developers.google.com/identity/sms-retriever/overview
https://developers.google.com/identity/sms-retriever/request
https://developers.google.com/identity/sms-retriever/verify

1
Dhaval Patel

Comme Google a restreint l'utilisation de l'autorisation READ_SMS, la solution est sans autorisation READ_SMS.

API SMS Retriever

La fonction de base consiste à éviter d'utiliser la permission critique Android READ_SMS et à accomplir la tâche à l'aide de cette méthode. Coup sont les étapes dont vous aviez besoin.

Post Envoi d'OTP au numéro d'utilisateur, consultez SMS API Retriever capable de recevoir un message ou non

SmsRetrieverClient client = SmsRetriever.getClient(SignupSetResetPasswordActivity.this);
Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        // Android will provide message once receive. Start your broadcast receiver.
        IntentFilter filter = new IntentFilter();
        filter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION);
        registerReceiver(new SmsReceiver(), filter);
    }
});
task.addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        // Failed to start retriever, inspect Exception for more details
    }
});

Code du récepteur de radiodiffusion

import Android.content.BroadcastReceiver;
import Android.content.Context;
import Android.content.Intent;
import Android.content.SharedPreferences;
import Android.os.Bundle;

import com.google.Android.gms.auth.api.phone.SmsRetriever;
import com.google.Android.gms.common.api.CommonStatusCodes;
import com.google.Android.gms.common.api.Status;

public class SmsReceiver 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 otp;
                    String msgs = (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;
            }
        }
    }
}

Dernière étape. Enregistrez ce récepteur dans votre manifeste

<receiver Android:name=".service.SmsReceiver" Android:exported="true">
    <intent-filter>
        <action Android:name="com.google.Android.gms.auth.api.phone.SMS_RETRIEVED"/>
    </intent-filter>
</receiver>

Votre SMS doit être comme ci-dessous.

<#> Your OTP code is: 6789
QWsa8754qw2 

QWsa8754qw2 représente ici votre propre code de hachage à 11 caractères. Suivez ceci link

  • Ne pas être plus de 140 octets
  • Commencez par le préfixe <#>
  • Terminez avec une chaîne de hachage de 11 caractères identifiant votre application.

Pour importer com.google.Android.gms.auth.api.phone.SmsRetriever, n'oubliez pas d'ajouter cette ligne à votre application build.gradle:

implementation "com.google.Android.gms:play-services-auth-api-phone:16.0.0"
1
Shabbir Dhangot