web-dev-qa-db-fra.com

Base de feu SMS Vérification/Authentification

Pour un projet client, je crée une application hybride simple qui remplit une fonction très simple tout en générant un trafic important. L’application n’aurait normalement pas besoin d’un backend, car c’est très simple, et firebase apparaît comme une solution parfaite pour le projet. 

La seule partie où je suis bloqué est SMS Vérification/Authentification avec Firebase. Cependant, après des recherches intenses sur Google et sur le doc, je me suis rendu compte qu'il n'y avait pas de moyen facile de le faire. Voici ce que j'ai examiné jusqu'à présent: 

  1. Fabric.io Digits possède une excellente API JS. Toutefois, pour une raison quelconque, firebase et les chiffres ne fonctionneront pas parfaitement ensemble: https://groups.google.com/forum/#!topic/firebase-talk/ sB7lPuyCVBQ
  2. Kit de compte Facebook - Il y a à peine une semaine, Facebook a publié un nouveau kit pour SMS Vérification et authentification, même s'il a toujours le même problème que les chiffres fabric.io, du moins jusqu'à preuve du contraire.
  3. Twilio/Nexmo via NodeJS - Ce sont deux services épiques dotés d'excellentes API JS, mais d'après ce que j'ai compris, cela nécessiterait un serveur dorsal distinct pour gérer l'échange de jetons JWT. Et cela représente à lui seul un autre serveur, qui deviendrait le goulot d'étranglement lors d'un trafic intense, et un autre point de vulnérabilité pour la sécurité, que l'équipe client devrait gérer séparément. Pas le plus agréable. 
  4. Twilio/Nexmo & Auth0 - Jusqu’à présent, cela semblait être la meilleure option, où l’authentification et la gestion des utilisateurs sont gérées par Auth0, mais cette solution peut rapidement coûter cher étant donné que twilio ou nexmo et auth0 sont des solutions payantes. Non pas que je sois un bon à rien d’espérer que les choses marchent gratuitement - mais me semble être une étape supplémentaire très coûteuse, étant donné que c’est juste pour envoyer des jetons. [voir: clients d'enfer]
  5. Je me souviens avoir lu quelque part une suggestion consistant à utiliser des numéros de téléphone comme adresses électroniques sur firebase comme: [email protected] et à utiliser les codes de sécurité envoyés par sms en tant que mot de passe, ce qui semble très fragmentaire pour de nombreuses raisons. 

Généralement, avec les applications mobiles hybrides, c'est leur nature non native ou les API JS qui sont à blâmer, mais pour la première fois (pour moi du moins), on a l'impression que ce n'est pas le cas. À ce stade, je suppose que Firebase n’est pas une option valable, mais je voulais demander une dernière fois aux membres aimants et attentionnés de la communauté avant de commencer à examiner AWS et à mettre en place un backend complet pour le client. 

Existe-t-il un autre moyen de gérer ce type d'authentification sans le service intermédiaire/sans serveur principal? Quelqu'un a-t-il déjà utilisé ces solutions? 


MISE À JOUR: MAI 2017

La vérification et l'authentification du téléphone sont désormais disponibles de manière native dans Firebase. Voir ma réponse affichée ci-dessous.


MISE À JOUR: AVR 2017

Firebase supporte maintenant nativement Fonctions Cloud . Vous pouvez maintenant accomplir ceci et bien plus encore en utilisant Cloud Functions sans configurer de serveur.


MISE À JOUR: OCT 2017

Fabric.io et Firebase ont collaboré et intégré Les chiffres dans l'authentification téléphonique Firebase et ont lancé davantage de fonctionnalités pour Fabric. 

27
johnozbay

Depuis le 17 mai 2017, les gens formidables chez Firebase ont intégré l'authentification téléphonique de Digits dans Firebase. C’est maintenant incroyablement facile à réaliser en mode natif dans Firebase , plus ou moins avec l’aiguillage d’un commutateur et sans le recours à un service externe ou similaire. Vous pouvez en savoir plus à ce sujet dans la documentation :)

4
johnozbay

Je ne peux pas parler de toutes les intégrations que vous avez mentionnées, mais vous voudrez peut-être essayer un autre service de Twilio, Authy. 

Nous avons récemment publié des exemples de code prêts à la production via des tutoriels pour aider les utilisateurs à résoudre ce type de problèmes.

Un tel exemple vous guide à travers: 

  • Envoi d'une notification OneTouch Push à une application mobile Authy .__ ou 
  • Envoi d'un jeton via l'application mobile Authy ou
  • Envoi d'un jeton unique dans un message texte envoyé avec Authy via Twilio.

est le 2FA avec Authy tutorial . L'extrait de code Node.js suivant montre le noeud final en attente d'approbation ou de refus du statut de l'utilisateur. Si l'utilisateur a approuvé la demande One Touch, nous enregistrons sa session sous le nom confirmed, qui les connecte officiellement.

Si la demande est refusée, nous affichons la page /verify et demandons à l'utilisateur de se connecter avec un jeton. 

// Internal endpoint for checking the status of OneTouch
exports.authyStatus = function(request, response) {
    var status = (request.user) ? request.user.authyStatus : 'unverified';
    if (status == 'approved') {
        request.session.confirmed = true;
        request.session.save(function(err) {
            if (err) return error(response, 500, 
                'There was an error validating your session.');
        });
    }
    if (!request.session) {
        return error(response, 404, 'No valid session found for this user.');
    } else {
        response.send({ status: status });
    }   
};

Donc, cela nécessite en effet que vous ayez un serveur. Toutefois, si vous avez choisi cet exemple, cela devrait vous aider à décider de ce qui fonctionnera le mieux pour votre application. 

2
Megan Speir
import Android.app.Activity;
import Android.os.Bundle;
import Android.support.annotation.NonNull;
import Android.text.TextUtils;
import Android.util.Log;
import Android.widget.EditText;
import Android.widget.Toast;

import com.google.Android.gms.tasks.OnCompleteListener;
import com.google.Android.gms.tasks.Task;
import com.google.firebase.FirebaseException;
import com.google.firebase.FirebaseTooManyRequestsException;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider;

import Java.util.concurrent.TimeUnit;

public class PhoneAutenticationService {
public PhoneAutenticationService(Activity activity,FirebaseAuth auth) {
    this.activity = activity;
    this.mAuth = auth;
    setupCallback();
}

private static final String TAG = PhoneAutenticationService.class.getSimpleName();

private Activity activity;
private String verificationCode;
private static final String KEY_VERIFY_IN_PROGRESS = "key_verify_in_progress";

private static final int STATE_INITIALIZED = 1;
private static final int STATE_CODE_SENT = 2;
private static final int STATE_VERIFY_FAILED = 3;
private static final int STATE_VERIFY_SUCCESS = 4;
private static final int STATE_SIGNIN_FAILED = 5;
private static final int STATE_SIGNIN_SUCCESS = 6;

// [START declare_auth]
private FirebaseAuth mAuth;
// [END declare_auth]

private boolean mVerificationInProgress = false;
private String mVerificationId;
private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks;
private PhoneAuthProvider.ForceResendingToken mResendToken;

protected void onSaveInstanceState(Bundle outState) {
    outState.putBoolean(KEY_VERIFY_IN_PROGRESS, mVerificationInProgress);
}

protected void onRestoreInstanceState(Bundle savedInstanceState) {
    mVerificationInProgress = savedInstanceState.getBoolean(KEY_VERIFY_IN_PROGRESS);
}


// [START on_start_check_user]
public void onStart(EditText mPhoneNumberField) {
    // Check if user is signed in (non-null) and update UI accordingly.
    FirebaseUser currentUser = mAuth.getCurrentUser();
    updateUI(currentUser);
    // [START_EXCLUDE]
    if (mVerificationInProgress && validatePhoneNumber(mPhoneNumberField)) {
        startPhoneNumberVerification(mPhoneNumberField.getText().toString());
    }
    // [END_EXCLUDE]
}
// [END on_start_check_user]

private void setupCallback(){
    mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

        @Override
        public void onVerificationCompleted(PhoneAuthCredential credential) {
            // This callback will be invoked in two situations:
            // 1 - Instant verification. In some cases the phone number can be instantly
            //     verified without needing to send or enter a verification code.
            // 2 - Auto-retrieval. On some devices Google Play services can automatically
            //     detect the incoming verification SMS and perform verificaiton without
            //     user action.
            Log.d(TAG, "onVerificationCompleted:" + credential);
            // [START_EXCLUDE silent]
            mVerificationInProgress = false;
            // [END_EXCLUDE]

            // [START_EXCLUDE silent]
            // Update the UI and attempt sign in with the phone credential
            updateUI(STATE_VERIFY_SUCCESS, credential);
            // [END_EXCLUDE]
            signInWithPhoneAuthCredential(credential);
        }

        @Override
        public void onVerificationFailed(FirebaseException e) {
            // This callback is invoked in an invalid request for verification is made,
            // for instance if the the phone number format is not valid.
            Log.w(TAG, "onVerificationFailed", e);
            // [START_EXCLUDE silent]
            mVerificationInProgress = false;
            // [END_EXCLUDE]

            if (e instanceof FirebaseAuthInvalidCredentialsException) {
                // Invalid request
                // [START_EXCLUDE]
                Toast.makeText(activity,"Invalid phone number.",Toast.LENGTH_SHORT).show();
                // [END_EXCLUDE]
            } else if (e instanceof FirebaseTooManyRequestsException) {
                // The SMS quota for the project has been exceeded
                // [START_EXCLUDE]
                Toast.makeText(activity,"Quota exceeded.",Toast.LENGTH_SHORT).show();

                // [END_EXCLUDE]
            }

            // Show a message and update the UI
            // [START_EXCLUDE]
            updateUI(STATE_VERIFY_FAILED);
            // [END_EXCLUDE]
        }

        @Override
        public void onCodeSent(String verificationId,
                               PhoneAuthProvider.ForceResendingToken token) {
            // The SMS verification code has been sent to the provided phone number, we
            // now need to ask the user to enter the code and then construct a credential
            // by combining the code with a verification ID.
            Log.d(TAG, "onCodeSent:" + verificationId);
            Toast.makeText(activity,"onCodeSent:" + verificationId,Toast.LENGTH_SHORT).show();
            verificationCode = verificationId;
            // Save verification ID and resending token so we can use them later
            mVerificationId = verificationId;
            setVerificationCode(verificationId);
            mResendToken = token;

            // [START_EXCLUDE]
            // Update UI
            updateUI(STATE_CODE_SENT);
            // [END_EXCLUDE]
        }

    };
}


public void startPhoneNumberVerification(String phoneNumber) {
    // [START start_phone_auth]
    PhoneAuthProvider.getInstance().verifyPhoneNumber(
            phoneNumber,        // Phone number to verify
            60,                 // Timeout duration
            TimeUnit.SECONDS,   // Unit of timeout
            activity,               // Activity (for callback binding)
            mCallbacks);        // OnVerificationStateChangedCallbacks
    // [END start_phone_auth]

    mVerificationInProgress = true;
}



public void verifyPhoneNumberWithCode(String verificationId, String code) {
    // [START verify_with_code]
    PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);

    // [END verify_with_code]
    signInWithPhoneAuthCredential(credential);
}

// [START resend_verification]
public void resendVerificationCode(String phoneNumber,
                                   PhoneAuthProvider.ForceResendingToken token) {
    PhoneAuthProvider.getInstance().verifyPhoneNumber(
            phoneNumber,        // Phone number to verify
            60,                 // Timeout duration
            TimeUnit.SECONDS,   // Unit of timeout
            activity,               // Activity (for callback binding)
            mCallbacks);        // resending
    // [END start_phone_auth]
}
// [END resend_verification]

// [START sign_in_with_phone]
public void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
    mAuth.signInWithCredential(credential)
            .addOnCompleteListener(activity, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        // Sign in success, update UI with the signed-in user's information
                        Log.d(TAG, "signInWithCredential:success");
                        Toast.makeText(activity,"signInWithCredential:success",Toast.LENGTH_SHORT).show();
                        FirebaseUser user = task.getResult().getUser();
                        // [START_EXCLUDE]
                        updateUI(STATE_SIGNIN_SUCCESS, user);
                        // [END_EXCLUDE]
                    } else {
                        // Sign in failed, display a message and update the UI
                        Log.w(TAG, "signInWithCredential:failure", task.getException());
                        if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
                            // The verification code entered was invalid
                            // [START_EXCLUDE silent]
                            Toast.makeText(activity,"Invalid code.",Toast.LENGTH_SHORT).show();
                            // [END_EXCLUDE]
                        }
                        // [START_EXCLUDE silent]
                        // Update UI
                        updateUI(STATE_SIGNIN_FAILED);
                        // [END_EXCLUDE]
                    }
                }
            });
}
// [END sign_in_with_phone]


public void signOut() {
    mAuth.signOut();
    updateUI(STATE_INITIALIZED);
}

private void updateUI(int uiState) {
    updateUI(uiState, mAuth.getCurrentUser(), null);
}

public void updateUI(FirebaseUser user) {
    if (user != null) {
        updateUI(STATE_SIGNIN_SUCCESS, user);
    } else {
        updateUI(STATE_INITIALIZED);
    }
}

private void updateUI(int uiState, FirebaseUser user) {
    updateUI(uiState, user, null);
}

private void updateUI(int uiState, PhoneAuthCredential cred) {
    updateUI(uiState, null, cred);
}

private void updateUI(int uiState, FirebaseUser user, PhoneAuthCredential cred) {
    switch (uiState) {
        case STATE_INITIALIZED:
            // Initialized state, show only the phone number field and start button
            Toast.makeText(activity,"Initialized state",Toast.LENGTH_SHORT).show();
            break;
        case STATE_CODE_SENT:
            // Code sent state, show the verification field, the
            Toast.makeText(activity,"Code sent state",Toast.LENGTH_SHORT).show();

            break;
        case STATE_VERIFY_FAILED:
            // Verification has failed, show all options
            Toast.makeText(activity,"Verification has failed",Toast.LENGTH_SHORT).show();

            break;
        case STATE_VERIFY_SUCCESS:
            // Verification has succeeded, proceed to firebase sign in
            Toast.makeText(activity,"Verification has succeeded",Toast.LENGTH_SHORT).show();

            // Set the verification text based on the credential
            if (cred != null) {
                if (cred.getSmsCode() != null) {
                    //mVerificationField.setText(cred.getSmsCode());
                } else {
                    Toast.makeText(activity,"Invalid verification code.",Toast.LENGTH_SHORT).show();
                }
            }

            break;
        case STATE_SIGNIN_FAILED:
            // No-op, handled by sign-in check
            Toast.makeText(activity,"Sign in failed",Toast.LENGTH_SHORT).show();

            break;
        case STATE_SIGNIN_SUCCESS:
            // Np-op, handled by sign-in check
            Toast.makeText(activity,"Sign in sucesssss!!!!",Toast.LENGTH_SHORT).show();
            break;
    }

    if (user == null) {
        // Signed out

    } else {
        // Signed in
    }
}


public boolean validatePhoneNumber(EditText mPhoneNumberField) {
    String phoneNumber = mPhoneNumberField.getText().toString();
    if (TextUtils.isEmpty(phoneNumber) || phoneNumber.length()>10 || phoneNumber.length()<9) {
        Toast.makeText(activity,"Invalid phone number.",Toast.LENGTH_SHORT).show();
        return false;
    }

    return true;
}

public PhoneAuthProvider.OnVerificationStateChangedCallbacks getmCallbacks() {
    return mCallbacks;
}

public PhoneAuthProvider.ForceResendingToken getmResendToken() {
    return mResendToken;
}

public FirebaseAuth getmAuth() {
    return mAuth;
}

public String getVerificationCode() {
    return verificationCode;
}

public void setVerificationCode(String verificationCode) {
    this.verificationCode = verificationCode;
}

}

Dans votre activité, initialisez Firebase auth et listener.

 mAuth = FirebaseAuth.getInstance();
    mAuthListener = new FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
            FirebaseUser user = firebaseAuth.getCurrentUser();
            if (user != null) {
                Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
            } else {
                Log.d(TAG, "onAuthStateChanged:signed_out");
            }
            // ...
        }
    };


    //init all auth process
    phoneAutenticationService = new PhoneAutenticationService(this,mAuth);

 @Override
public void onStart() {
    super.onStart();
    mAuth.addAuthStateListener(mAuthListener);
    getActivity().registerReceiver(smsBroadcastReceiver, filter);// define e broadcast receiver to intercept a sms verification code
}

@Override
public void onStop() {
    super.onStop();
    if (mAuthListener != null) {
        mAuth.removeAuthStateListener(mAuthListener);sms code
    }
    getActivity().unregisterReceiver(smsBroadcastReceiver);

}

et enfin appelez la méthode firebase pour l'authentification

public void startAuthenticationByPhone(){
    if (!validatePhoneNumber(phoneInput)) {
        return;
    }
    startPhoneNumberVerification(phoneInput.getText().toString());

}......
1
chry

Désormais, l'authentification téléphonique est disponible dans firebase.Voici le code d'authentification téléphonique utilisant Firebase:

EditText phoneNum,Code;// two edit text one for enter phone number other for enter OTP code
Button sent_,Verify;// sent button to request for verification and verify is for to verify code
private PhoneAuthProvider.ForceResendingToken mResendToken;
private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks;
private FirebaseAuth mAuth;
private String mVerificationId;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_phone_number_auth);

    phoneNum =(EditText) findViewById(R.id.fn_num);
    Code =(EditText) findViewById(R.id.code);

    sent_ =(Button)findViewById(R.id.sent_nu);
    Verify =(Button)findViewById(R.id.verify);

    callback_verificvation();               ///function initialization

    mAuth = FirebaseAuth.getInstance();
    sent_.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String num=phoneNum.getText().toString();
            startPhoneNumberVerification(num);          // call function for receive OTP 6 digit code
        }
    });
    Verify.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String code=Code.getText().toString();
            verifyPhoneNumberWithCode(mVerificationId,code);            //call function for verify code 

        }
    });
}

private void startPhoneNumberVerification(String phoneNumber) {
    // [START start_phone_auth]
    PhoneAuthProvider.getInstance().verifyPhoneNumber(
            phoneNumber,        // Phone number to verify
            60,                 // Timeout duration
            TimeUnit.SECONDS,   // Unit of timeout
            this,               // Activity (for callback binding)
            mCallbacks);        // OnVerificationStateChangedCallbacks
    // [END start_phone_auth]


}

private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
    mAuth.signInWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        // Sign in success, update UI with the signed-in user's information

                        FirebaseUser user = task.getResult().getUser();
                        Toast.makeText(getApplicationContext(), "sign in successfull", Toast.LENGTH_SHORT).show();

                    } else {
                        // Sign in failed, display a message and update the UI

                        if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
                            // The verification code entered was invalid

                        }


                    }
                }
            });
}
private void verifyPhoneNumberWithCode(String verificationId, String code) {
    // [START verify_with_code]
    PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
    // [END verify_with_code]
    signInWithPhoneAuthCredential(credential);
}


private void callback_verificvation() {

    mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

        @Override
        public void onVerificationCompleted(PhoneAuthCredential credential) {
            // This callback will be invoked in two situations:
            // 1 - Instant verification. In some cases the phone number can be instantly
            //     verified without needing to send or enter a verification code.
            // 2 - Auto-retrieval. On some devices Google Play services can automatically
            //     detect the incoming verification SMS and perform verificaiton without
            //     user action.





            signInWithPhoneAuthCredential(credential);
        }

        @Override
        public void onVerificationFailed(FirebaseException e) {
            // This callback is invoked in an invalid request for verification is made,
            // for instance if the the phone number format is not valid.


            if (e instanceof FirebaseAuthInvalidCredentialsException) {
                // Invalid request

            } else if (e instanceof FirebaseTooManyRequestsException) {
                // The SMS quota for the project has been exceeded

            }

            // Show a message and update the UI

        }

        @Override
        public void onCodeSent(String verificationId,
                               PhoneAuthProvider.ForceResendingToken token) {
            // The SMS verification code has been sent to the provided phone number, we
            // now need to ask the user to enter the code and then construct a credential
            // by combining the code with a verification ID.


            // Save verification ID and resending token so we can use them later
            mVerificationId = verificationId;
            mResendToken = token;


        }
    };