Android – Implementing two-step authentication through SMS text
This tutorial is a follow up to the previous one on Implementing two-step authentication through Google authenticator. I provided source code for a sample project that contains both ways for 2FA implemented (SMS and authentication apps), and I explained the context ( which service we’ll use for that, setting the project and libraries to add to gradle, ect…) on the previous tutorial. So I suggest you start with it if you need to learn about implementing the second method, or at least you have to read it if you only need to implement 2FA through SMS.
This tutorial will be very concise. We’ll start with a demo for what we’ll build :
In this demo we registered a user after asking them if they Enable two-factor authentication on login or not. Then on login we either redirected them to home ui or sent them a 2FA SMS text and asked them to enter it for verification. Decision was made depending on the isTwoFactorAuthOn attribute we read for DB on user login.
You can check out the Google authenticator tutorial ti implement this part : ” Enabling two-factor authentication “. Here we’ll go on how to send One Time Password sms text, and how to verify it using Authy API.
Sending One Time Password through SMS
As explained in details in the previous tutorial, to be able to use this Authy API you have to register your app on your Twilio console, and register user’s phone number and email to authy service each time you register a user to your app.
Creating an app on Authy console goes as follows:
Adding a user to authy API is done through a simple REST call to
https://api.authy.com/protected/json/users/new
?user[email]=X&user[country_code]=Y&user[cellphone]=Z
Now sending OTPs to your user’s phone number goes as follows :
(findViewById(R.id.smsOptionLyt)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); Statics.usersTable.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { /** 1.Get user creds! **/ email = (dataSnapshot.getValue(User.class)).getEmailAddress(); username = (dataSnapshot.getValue(User.class)).getFirstName() + " " + (dataSnapshot.getValue(User.class)).getLastName(); // password= (dataSnapshot.getValue(User.class)).getPassword(); phoneNumber = (dataSnapshot.getValue(User.class)).getPhoneNumber(); countryCode = (dataSnapshot.getValue(User.class)).getPhoneCountryCode(); addUserUrl = "https://api.authy.com/protected/json/users/new?user[email]=" + email + "&user[cellphone]=" + phoneNumber + "&user[country_code]=" + countryCode + "&api_key=xxxxxxxxxxxxxx"; /** 2.Add the user to the Authy API **/ // post call for Authy api to add a user | response contains the added user's id JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.POST, addUserUrl, null, new Response.Listener() { @Override public void onResponse(JSONObject response) { Gson gson = new Gson(); try { /** get the returned id **/ JsonObject addedUser = gson.fromJson(response.getString("user"), JsonObject.class); addedUserId = (addedUser.get("id")).getAsString(); // Toast.makeText(getApplicationContext(), "Res: "+addedUserId, Toast.LENGTH_LONG).show(); /** 3.call the Authy API to send a code through sms **/ sendSecurityCodeTo(addedUserId); /** 4.call the Authy API to validate code provided by user **/ // included in sendSecurityCodeTo method } catch (JSONException e) { e.printStackTrace(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.e("ERROR! ", "ee: " + error.getMessage()); } }); (AppSingleton.getInstance(getContext()).getRequestQueue()).add(jsObjRequest); }
sendSecurityCodeTo(addedUserId) method performs another call to Authy API :
GET https://api.authy.com/protected/json/sms/{AUTHY_ID}?api_key=myApiKey
– the user Id is returned by the call we made when we registered the user to authy API. If you jump back to step 2 of previous code, you’ll find I retreived the userId:
/** get the returned id **/ JsonObject addedUser = gson.fromJson(response.getString("user"),JsonObject.class); addedUserId = (addedUser.get("id")).getAsString();
– and the api key is found on your app dashboard on twilio console :
so the call to send OTPs through SMS would be as follows:
private void sendSecurityCodeTo(final String userId) { String getCodeSMS = "https://api.authy.com/protected/json/sms/" + userId + "?api_key=xxxxxxxxxxxxxxxxxxx&force=true"; JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, getCodeSMS, null, new Response.Listener() { @Override public void onResponse(JSONObject response) { dismiss(); final Dialog dialog = new Dialog(getContext(), R.style.TwoFADialogs); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setCancelable(false); dialog.setContentView(R.layout.validate2fasms_ui); dialog.findViewById(R.id.validateBtn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { /** call authy api to validate code provided by the user **/ Statics.validateSecurityCode(((EditText)dialog.findViewById(R.id.codeEdtx)).getText().toString(),userId,getContext() ,((EditText)dialog.findViewById(R.id.codeEdtx)),((TextView)dialog.findViewById(R.id.errorTxt))); } }); dialog.findViewById(R.id.cancelBtn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); dialog.show(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.e("ERROR! ", error.getMessage()); } }); (AppSingleton.getInstance(getContext()).getRequestQueue()).add(jsObjRequest); }
Verifying TOPs against user provided tokens
To verify a token provided by the user you will need to perfom this REST call to Authy API:
GET https://api.authy.com/protected/json/verify/{TOKEN}/{AUTHY_ID}
with AUTHY_ID is the user ID you retreived when adding user to authy and TOKEN is the passcode you want to verify. For all verification call in this sample I used the following static method :
public static void validateSecurityCode(String code, final String userId, final Context context, final EditText codeTxt, final TextView errorTxt){
String codeValidationUrl="https://api.authy.com/protected/json/verify/"+code+"/"+userId+"?api_key=xxxxxxxxxxxxxxxxxxxxx";
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET,codeValidationUrl,null,
new Response.Listener() {
@Override
public void onResponse(JSONObject response) {
try {
if((response.getString("token")).equals("is valid"))
context.startActivity(new Intent(context, HomeActivity.class));
else
Toast.makeText(context, "You typed a wrong code!", Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
codeTxt.setText("");
errorTxt.setVisibility(View.VISIBLE);
codeTxt.startAnimation( AnimationUtils.loadAnimation(context, R.anim.errormsg_slide));
}
});
(AppSingleton.getInstance(context).getRequestQueue()).add(jsObjRequest);
}
That’s all coders !
I provide you with the Source code for the whole sample app and other references that helped me build this tutorial:
– Documentation for Authy API .
– Google Authenticator app support now available in Authy API on twilio.com.
Let me know in comments below if you have any doubts or questions.
I am extremely impressed with your writing skills as well as with the layout on your weblog.
Is this a paid theme or did you customize it yourself?
Anyway keep up the nice quality writing, it is rare to see a nice blog like this one today.