Firebase SMS Verification / Authentication
For a client project I'm creating a simple hybrid app that serves a very simple function, yet will have high traffic. The app wouldn't normally need a backend, since it's very very simple, and firebase seems like a perfect solution for the project.
The only part where I'm stuck at is SMS Verification / Authentication with Firebase. However, after some intense googling, and doc reading, I've come to realize that there's no easy way to do this. Here's what I've looked into so far :
Usually with hybrid mobile apps, the non-native nature of them or JS APIs are to blame, but for the first time (for me at least) it feels like this isn't the case. I presume at this point Firebase isn't a valid option, but wanted to ask the loving and caring members of the community one last time before starting to look into AWS, and setting up an entire backend for the client.
Is there any other way to handle this type of authentication minus the middle-service / without a backend server? Anyone has any experience using these solutions?
UPDATE : MAY 2017
Phone Verification & Authentication is now natively available in Firebase. See my self-posted answer below.
UPDATE : APR 2017
Firebase now natively supports Cloud Functions. You can now accomplish this and a lot more using Cloud Functions without setting up any servers.
As of May 17 2017 , the amazing people at Firebase have baked Digits' phone authentication into Firebase. This is now incredibly easy to achieve natively within Firebase, more or less with the flip of a switch and without the need of an external service or anything alike. You can read more about it in the docs :)
I can't speak to every integration you mentioned, but you might want to try out another one Twilio's services, Authy.
We've recently released production ready code samples via tutorials to help people work through these kinds of problems.
One such example walks you through:
is the 2FA with Authy tutorial. The following Node.js snippet shows the endpoint waiting for user status to be approved or denied. If the User has approved the OneTouch request, we will save their session as confirmed
, which officially logs them in.
If the request was denied we render the /verify
page and ask the User to log in with a Token.
// 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 });
}
};
So, this indeed requires you have a server. But given a go at the sample, this should help you decide what will work best for your app.
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;
}
}
In your activity initialize Firebase auth and 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);
}
and finally call firebase method for authentication
public void startAuthenticationByPhone(){
if (!validatePhoneNumber(phoneInput)) {
return;
}
startPhoneNumberVerification(phoneInput.getText().toString());
}......
链接地址: http://www.djcxy.com/p/33088.html
上一篇: 无法在nexmo中显示此响应错误的概述
下一篇: Firebase SMS验证/身份验证