An Ultimate Guide on Implementing Behavioral Biometrics in Android Using TypingDNA Authentication API

Boemo Mmopelwa
9 min readJul 12, 2021
Photo by Andrew Neel on Unsplash

I often get frightened by the fact that someone might spoof my computer and steal my bank account credentials. Recently I came across TypingDNA Authentication API which enables apps to authenticate their users using typing biometrics.

In this post, you will learn how to use the behavioral authentication concept in Android using TypingDNA Authentication API. We will create a demo app that will authenticate the user by using their typing pattern that has been recorded when typing the username and password.

Prerequisites and Requirements

This tutorial will teach you how to authenticate users using TypingDNA Authentication API in Android henceforth you don’t need to have a lot of knowledge on TypingDNA Authentication API. You will use the following technologies in this tutorial.

● TypingDNA Authentication API.

● Android studio

Table of Contents

  1. Creating a TypingDNA account.
  2. Adding dependencies.
  3. Recording the user’s typing pattern.
  4. Making requests to the TypingDNA Authentication API.
  5. Testing and using the demo app.
  6. Conclusion

TL;DR: In this post, you will learn how to use the TypingDNA Authentication API to authenticate users in Android.

Creating a TypingDNA account

Create a TypingDNA account here and head over to the client dashboard and copy the authentication API credentials. You will use them later in the tutorial when integrating the API.

Project Structure

To start the journey, go to the TypingDNA Android repository and download the typingdnarecordermobile library in the libs folder and add it to your project. The Authentication class will inherit methods from these classes TypingDNAOverlayService.java, TypingDNARecorderBase.java, TypingDNARecorderMobile.java. Please download them also and add them to the class package.

Adding dependencies

Go ahead and add the following dependencies in the Gradle app level and sync your project to download the Volley third-party library.

dependencies {
implementation ‘com.android.volley:volley:1.2.0’
implementation project(path: ‘:typingdnarecordermobile’)
}

In the Manifest file, You have to add the SYSTEM_ALERT_WINDOW permission and also add the TYPE_APPLICATION_OVERLAY permission.

<uses-permission android:name=”android.permission.INTERNET” />
<uses-permission android:name=”android.permission.SYSTEM_ALERT_WINDOW” />
<uses-permission android:name=”android.permission.TYPE_APPLICATION_OVERLAY” />

And finally, enable the TypingDNAOverlayService by adding it to the application. This service draws an overlay over the activity.

<application
<service android:name=”.TypingDNAOverlayService” />
</application>

Disabling autofill and suggestions

When receiving the user’s input, autofill and autocorrect have to be disabled o record an accurate typing pattern. To achieve this, set the input type as textNoSuggestions|textFilter and also add importantForAutofill=”no” .

<EditTextandroid:id=”@+id/username”android:layout_width=”match_parent”android:layout_height=”wrap_content”android:layout_marginTop=”100dp”android:hint=”@string/prompt_email”android:selectAllOnFocus=”true”android:inputType=”textNoSuggestions|textFilter”android:importantForAutofill=”no”android:autofillHints=”email address” />

In the Enroll java class, add the following code to disable suggestions. Suggestions are automatically disabled in android if the input type is set to password. So, you will only disable suggestions in the username field.

musername = findViewById(R.id.username1);
musername.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS );

Recording the user’s typing pattern

Since you have fulfilled all necessary requirements, you can now start implementing the TypingDNA Recorder. Start by Initializing the TypingDNA recorder in the onCreate method which will start recording the user’s typing pattern.

tdna = new TypingDNARecorderMobile(this);
tdna.start();

You will receive input from the password and username input fields, so you need to add the password field and username field as your targets. Typing events will only be recorded in these fields.

tdna.addTarget(R.id.password);
tdna.addTarget(R.id.username);

Add the following necessary methods of the TypingDNARecorder.

//Stops the overlay service and ends the recording of further typing events.@Override
protected void onDestroy() {
tdna.stop();
super.onDestroy();
}
@Override
protected void onPause(){
tdna.pause();
super.onPause();
}
//Stops the overlay service and ends the recording of further typing events.@Override
protected void onStop(){
tdna.stop();
super.onStop();
}
//Starts recording the typing events and also starts the overlay service.@Overrideprotected void onResume(){tdna.start();super.onResume();}

Encoding the API key and API secret to base64

In the code snippet below, we convert the API key and API secret to base64. You will later use this base64 API key and API secret when adding authorization credentials in the headers.

Note: Please do not embed your API Key or API secret directly in any client-side code.

String originalString =apiKey+”:”+ apiSecret;
String encodedString;
Base64.Encoder encoder = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
encoder = Base64.getEncoder();
encodedString = encoder.encodeToString(originalString.getBytes());

}

Hashing the user’s ID

You need to send the user’s ID and the typing pattern to the endpoint. The user’s ID is a combination of the user’s email/username and password. The ID has to be unique and has a minimum of six characters. Hash the user’s ID using the MD5 algorithm to protect the user’s privacy. Add the following method to your class.

public String md5(String a) {
try {

MessageDigest digest = java.security.MessageDigest.getInstance(“MD5”);
digest.update(s.getBytes());
byte messageDigest[] = digest.digest();

StringBuffer hexString = new StringBuffer();
for (int i=0; i<messageDigest.length; i++)
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
return hexString.toString();
}catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return “”;
}

Checking internet connection

Add the following block of code which checks if the user’s device is connected to the internet. The user will only be able to start the enrollment process if their device is connected to the internet.

public boolean isConnected() {
boolean connected = false;
try {
ConnectivityManager cm = (ConnectivityManager)getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo nInfo = cm.getActiveNetworkInfo();
connected = nInfo != null && nInfo.isAvailable() && nInfo.isConnected();
return connected;
} catch (Exception e) {
Log.e(“Connectivity Exception”, e.getMessage());
}
return connected;
}

The authenticate method

The authenticate method will send the user’s Id and typing pattern to the auto endpoint. In the end, we will print the JSON response. If we get 1 from the enrollment object this would mean that the user has been enrolled successfully. The verification process will be a success if the result object gives us a 1. We will follow the next steps in order to send the users typing pattern, ID, and receive a JSON response.

Generating the user’s typing pattern

The getTypingPattern() is a function that outputs the user’s typing pattern as a string. It takes in the following parameters.

We will set the typing pattern type to 1 as we are using the same text pattern type. We will take the input from the user and set it as text. We will set other parameters to default. The string tp, will contain the typing pattern recorded from the fields we set as targets at the beginning of this tutorial.

String mpassword = passwordone.getText().toString();String username= musername.getText().toString();usernameandPassword = username+mpassword;tp = tdna.getTypingPattern(1, 0, usernameandPassword,0);id = md5(usernameandPassword);

Making requests to the TypingDNA Authentication API

We will use the Volley HTTP library to make a Post request to the auto endpoint. Next, create a JsonObject and populate it with the user’s ID and typing pattern. We will later pass this data to the JsonObjectRequest.

String posturli = “https://api.typingdna.com/auto/"+id;
RequestQueue requestQueue = Volley.newRequestQueue(this);
JSONObject postData = new JSONObject();
try {
postData.put(“id”, id);
postData.put(“tp”, tp);
} catch (JSONException e) {
e.printStackTrace();
}

When specifying what kind of response you want from the URL, choose the JsonObjectRequest. The JSON payload will only contain objects with no arrays. And finally, we initialize the JsonObjectRequest which takes in POST as the request method, the auto endpoint URL, and the data we want to POST.

We will enroll the user three times. First, we will check if enrollment was a success when handling the JSON response. If the user has been successfully enrolled we will update the addpattern String which tells the user how many patterns they are left with before getting fully enrolled. On the last successful enrollment, the user will be granted entry to the app and receive a “successful enrollment” message.

String addpattern =”Please add two more patterns to get enrolled”;JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, posturli, postData, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
System.out.println(response);
try {
JSONObject myJsonObject = new JSONObject(response.toString());String enrollment= myJsonObject.getString(“enrollment”);if(enrollment.equals(“1”)){Toast.makeText(getApplicationContext(),”it worked”,Toast.LENGTH_LONG).show();clickcount=clickcount+1;if(clickcount==1){addpattern=”Please add one more pattern, to get enrolled”;}if (clickcount==2){addpattern=”Done”;}if(clickcount==3){Intent intent= new Intent(Enroll.this,MainActivity.class);intent.putExtra(“message_key”, “successful enrollment”);startActivity(intent);}}} catch (JSONException e) {e.printStackTrace();}

Handling JSON exception

catch (JSONException e) {
Toast.makeText(getApplicationContext(), “invalid typing pattern”, Toast.LENGTH_LONG).show();
e.printStackTrace(); }

Adding headers

To get access to the auto endpoint we will need to add the Base64 encoded api key and api secret and set application/json; charset=utf-8 as your content-type.

{@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
headers.put(“Content-Type”, “application/json; charset=utf-8”);
headers.put(“Authorization”, “Basic “ +encodedString);
return headers;
}
};
requestQueue.add(jsonObjectRequest);
}

Checking the user

Before verifying the user, we have to check if the user has once been authenticated. To be able to accomplish this task, we have to send a GET request method to the check endpoint. If the user’s mobilecount is not equal to zero we will start verifying the user. The Login() function verifies the user. Before calling the Login() function we will delay the process by two seconds. We delay the procedure because API calls are limited to one call per second when using a free TypingDNA account.

public void checkUser(View view) {
//check for internet before beginning the process
if (isConnected()) {String mpassword = passwordone.getText().toString();
String username = musername.getText().toString();
usernameandPassword = username + mpassword;
//checking both the username and password
id = md5(usernameandPassword);
//sending a get request.
String geturli = “https://api.typingdna.com/user/" + id;
RequestQueue requestQueue = Volley.newRequestQueue(this);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, posturli, null, new Response.Listener < JSONObject > () {
@Override
public void onResponse(JSONObject response) {
System.out.println(response);
try {
JSONObject myJsonObject = new JSONObject(response.toString());
String mobilecount = myJsonObject.getString(“mobilecount”);
if (!mobilecount.equals(“0”)) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
// Do something after 5s = 5000ms
Login();
}
}, 2000);
} else {
Toast.makeText(getApplicationContext(), “incorrect username or password”, Toast.LENGTH_LONG).show();
} } catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}) {
@Override
public Map < String, String > getHeaders() throws AuthFailureError {
Map < String, String > headers = new HashMap < > ();
headers.put(“Content-Type”, “application/json; charset=utf-8”);
headers.put(“Authorization”, “Basic “ + encodedString);
return headers;
}
};
requestQueue.add(jsonObjectRequest);
} else {
Toast.makeText(getApplicationContext(), “no internet connection”, Toast.LENGTH_LONG).show();
}}

Verifying the user

Before verifying the user, we will check if the action received is verify or verify;enroll when handling the JSON response. When the user is being verified we will check if they received a positive result. Once they receive a positive result we will permit them to use the app.

if (action.equals(“verify”)|| action.equals(“verify;enroll”)){
String result = myJsonObject.getString(“result”);
if (result.equals(“1”)) {
Toast.makeText(getApplicationContext(), “verified”, Toast.LENGTH_LONG).show();
//navigating the user to the home Activity
Intent secondintent= new Intent(Enroll.this,MainActivity.class);
secondintent.putExtra(“message_key”, “successful verification”);
startActivity(secondintent);
} else {
Toast.makeText(getApplicationContext(), “verification failed”, Toast.LENGTH_LONG).show();
}}

JSON payload

And finally in the onResponse() method, we simply print the response to the console. If done correctly you should see this:

{“status”:200,”message_code”:10,”action”:”enroll”,”message”:”Pattern(s) enrolled. Not enough patterns for verification.”,”enrollment”:1}

Guiding the user

For the users to get authenticated they need to have been enrolled three times, so we will add an alert dialogue that will tell them how many typing patterns they have to enter before getting authenticated. We will also reset the typingDNA recorder and input fields when the user presses the alert dialog.

AlertDialog.Builder builder = new AlertDialog.Builder(Enroll.this);builder.setTitle(“Adding Typing pattern”);builder.setMessage(addpattern);builder.setPositiveButton(“OK”,new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog,int which) {tdna.reset();musername.setText(“”);password1.setText(“”);musername.requestFocus();}});builder.show();}});

Testing and using the demo app

We will first start by enrolling the user three times and then give them access to the app.

We can start authenticating (verification process) for the second time when the user comes back.

Conclusion

In this tutorial, we have learned how to implement the TypingDNA Authentication API in Android. By doing that we also acquired these skills:

  • Hashing a string in Android using the MD5 algorithm.
  • Converting a string to Base64 in Android.
  • Disabling Autofill in Android.
  • Making a Post request using the Volley library.

The source code of the application we made in this tutorial is available on Github.

--

--

Boemo Mmopelwa

Android developer| Tech Enthusiast|Innovative thinking is my passion | Learn and experience more to discover more