Firebase SMS验证/身份验证

对于客户端项目,我创建了一个简单的混合应用程序,它提供了一个非常简单的功能,但它的流量很大。 该应用程序通常不需要后端,因为它非常非常简单,Firebase似乎是该项目的完美解决方案。

我坚持的唯一部分就是使用Firebase进行SMS验证/身份验证。 但是,经过一番激烈的搜索和文档阅读之后,我发现没有简单的方法可以做到这一点。 以下是我到目前为止所研究的内容:

  • Fabric.io Digits有一个很棒的JS API,但是由于某些原因,Firebase和数字不能很好地融合在一起:https://groups.google.com/forum/#!topic/firebase-talk/sB7lPuyCVBQ
  • Facebook账户工具包 - 就在一周前,Facebook发布了一个新的用于SMS验证和认证的工具包,尽管它仍然觉得它与fabric.io数字有相同的问题,至少在另有证明之前。
  • Twilio / Nexmo通过NodeJS - 这些都是具有优秀JS API的史诗服务,但是从我所了解的情况来看,这需要单独的后端服务器来处理JWT令牌交换。 而且它本身就是另一台服务器,这将成为高流量时的瓶颈,另一个安全漏洞点,客户团队将不得不单独管理。 不是最愉快的。
  • Twilio / Nexmo&Auth0 - 迄今为止,这似乎是Auth0处理认证和用户管理的最佳选择,但鉴于twilio或nexmo和auth0均为付费解决方案,此解决方案可能会很快变得昂贵。 并不是说我是一个便宜的人,期望免费的东西能够工作 - 但由于它只是转发令牌,所以感觉像是一个非常昂贵的额外步骤。 [见:来自地狱的客户]
  • 我记得有人在某处读过这样的建议,比如将电话号码用作firebase上的电子邮件,例如:123-456-7890@example.com,并使用通过短信发送的安全代码作为密码,这听起来很粗略,原因很多。
  • 通常对于混合移动应用程序,它们或JS API的非本地特性是应该责备的,但第一次(至少对我来说)感觉不是这样。 我认为在这一点上,Firebase不是一个有效的选择,但是在开始查看AWS之前,最后一次想问问社区的爱心和关怀成员,并为客户端设置完整的后端。

    有没有其他的方式来处理这种类型的认证减去中间服务/没有后端服务器? 任何人有使用这些解决方案的经验?


    更新:2017年5月

    电话验证和身份验证现已在Firebase中原生提供。 在下面看到我自己发布的答案。


    更新:2017年4月

    Firebase现在支持云端功能。 您现在可以在不设置任何服务器的情况下使用云功能完成更多任务。


    截至2017年5月17日 ,Firebase的精彩人物已将Digits的手机身份验证融入到Firebase中。 现在,在Firebase中本地实现这一点非常容易,或多或少地通过切换开关并且无需外部服务或任何类似服务。 你可以在文档中阅读更多关于它的信息:)


    我无法向你提到的每一个整合说话,但你可能想尝试另一个Twilio的服务,Authy。

    我们最近通过教程发布了现成的代码示例,以帮助人们解决这些问题。

    一个这样的例子会引导您:

  • 发送OneTouch推送通知到手机Authy应用程序或
  • 通过手机Authy应用程序或发送令牌
  • 在Authy通过Twilio发送的文本消息中发送一次性令牌。
  • 是Authy教程的2FA。 以下Node.js代码片段显示了等待用户状态被批准或拒绝的端点。 如果用户已经批准了OneTouch请求,我们会将他们的会话保存为confirmed ,并正式登录。

    如果请求被拒绝,我们将呈现/verify页面并要求用户使用令牌登录。

    // 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 });
        }   
    };
    

    所以,这确实需要你有一台服务器。 但考虑一下示例,这应该有助于您决定哪种方式最适合您的应用。


    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;
    }
    

    }

    在您的活动中初始化Firebase身份验证和侦听器

     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);
    
    }
    

    最后调用firebase方法进行身份验证

    public void startAuthenticationByPhone(){
        if (!validatePhoneNumber(phoneInput)) {
            return;
        }
        startPhoneNumberVerification(phoneInput.getText().toString());
    
    }......
    
    链接地址: http://www.djcxy.com/p/33087.html

    上一篇: Firebase SMS Verification / Authentication

    下一篇: Error in Grails for interating Nexmo plugin