Introduction
Google’s reCAPTCHA API protects your website/app from malicious traffic. You might have seen the reCAPTCHA integrated on web pages. You can integrate the same in your Android apps too using SafeNet API. The service is free to use and it will show a captcha to be solved if the engine suspects user interaction to be a bot instead of a human.
Within this post, I will explain and build a simple button click application that will integrate captcha to avoid bots from submitting forms on there own. But understand that this method is not only limited to form usage but a user can integrate any this module into any app
How it works
The following point will explain the simple flow of reCAPTCHA in Android with SafetyNet API.
- First, a user needs to obtain the SafetyNet key pair by registering your app. After completing this a Site & Secret Key.
- The Site Key will be integrated into an Android app and it can be public. Secret Key should be kept on your server and it shouldn’t be exposed.
- When reCAPTCHA is invoked, it will show the Captcha challenge to a user it necessary. In this step, it communicates with the captcha server and returns “User Response Token” using Site Key.
Registering your App w/SafetyNet
To begin before diving into the application creation we need to get the keys that will be validated against.
Fist go to the site following site and sign up if you do not already have an account
- https://www.google.com/recaptcha/intro/v3.html
- After accessing your account create
- Register a new account
- Now enter your label, reCAPTCHA type, domains and then accept “Terms of Service”
NOTE: Regarding the label the title be anything that identifies the api key to yourself
NOTE: Regarding the selecting reCAPTCHA if working with android select reCAPTCHA v2 then reCAPTCHA Android
NOTE: Regarding populating the domain should be your Package Name in Package Names Section
Then, you will get the site key and secret key from SafetyNet API Server and it as well as shows client and server-side integration code snippets. The following figures show the same.
Step 1 – Create New Project w/Android Studio
Now lets begin with the fun stuff and to begin you will begin by open Android Studio and then “Create New Project”
- Begin by Start a new Android Studio Project èselect Basic Activity from templates.
NOTE: While creating, use the package name you have registered on reCAPTCHA dashboard.
Step 2 – Setting up the library & AndroidMainfest for the project
Add SafeNet and the Volley dependency to your build.gradle and rebuild the project. Here, I used the following dependency. You can change as per your Android SDK.
NOTE: Volley is used to send HTTP call to our server to validate the captcha token on the server side.
build.gradle
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
//dependency for recaptcha (safetynet)
implementation 'com.google.android.gms:play-services-safetynet:17.0.0'
//dependency for fast networking for networking
implementation 'com.android.volley:volley:1.1.0'
}
Now we need to add the app manifest file with the following permission(s). SafetyNet library is used to create the captcha validation in android. Volley library is an HTTP Networkinf library used here for validating captcha response.
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
Step 3 – Implementation of SafetyNet API
If you are still with me then let’s dive into the Java part of the project. We will first ensure that we have all the modules that will be used in the application
Required modules
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.annotation.NonNull;
//volley
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.RequestQueue;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.safetynet.SafetyNet;
import com.google.android.gms.safetynet.SafetyNetApi;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
import android.view.Menu;
import android.view.MenuItem;
Step 3 – Implementation of SafetyNet API (Continue)
If you are still with me then let’s dive into the Java part of the project. We will first ensure that we have all the modules that will be used in the application
- Replace “Site_Key” and “Site_Secret_Key” with your appropriate “Site Key” and “Secret Key” get from SafetyNet API while registering app.
- The API will check the Server and it has a separate callbacks from success and failure.
- At Success, we will get Captcha Response Token which will be used to validate the user interaction is made by a bot or real human.
- We will discuss how to validate the token with SafetyNet API Server in next step.
NOTE: the call on the created click event
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.reCaptcha);
txtV = findViewById(R.id.verifyText);
btn.setOnClickListener(this);
requestQueue = Volley.newRequestQueue(getApplicationContext());
}
public void onClick(View view){
SafetyNet.getClient(this).verifyWithRecaptcha(Site_Key)
.addOnSuccessListener(this, new OnSuccessListener <SafetyNetApi.RecaptchaTokenResponse>(){
@Override
public void onSuccess(SafetyNetApi.RecaptchaTokenResponse response){
if (!response.getTokenResult().isEmpty()){
handleCaptchaResult(response.getTokenResult());
}
}
})
.addOnFailureListener(this, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
if (e instanceof ApiException){
ApiException apiException = (ApiException)e;
Log.d(TAG, "Error Message: " + CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()));
} else {
Log.d(TAG, "Unknown error type or error" + e.getMessage());
}
}
});
}
Step 4 – Captcha Response Token Validation
- We have to verify the token getting from the server using the secret key.
- It can achieve by using the following.
-
- API Link – https://www.google.com/recaptcha/api/siteverify
- Method – POST
- Params – secret, response (We have to pass the “SECRET_KEY” and “TOKEN” respectively)
NOTE: Volley has
- RequestQueue to maintain the server calls in queue.
- RetryPolicy to retry the server call if it is fail with TimeOut and Retry Count. We can change those values.
- StringRequest is used for getting Response as JSON String.
- Method.POST denotes the call as POST method.
- Params are passed to server using Map, HashMap.
The SafetyNet API provides the response respective to the parameters passed and the success is Boolean Datatype.
void handleCaptchaResult(final String responseToken){
String url = "https://www.google.com/recaptcha/api/siteverify"; //consider using global variable here
StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
if (jsonObject.getBoolean("success")) {
txtV.setTextSize(35);
txtV.setText("Congratulations! You're not a robot anymore");
}
} catch (Exception ex) {
Log.d(TAG, "Error message: " + ex.getMessage());
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error message: " + error.getMessage());
}
})
{
@Override
protected Map<String,String> getParams(){
Map<String,String> params = new HashMap<>();
params.put("secret", Site_Secret_Key);
params.put("response", responseToken);
return params;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(50000,DefaultRetryPolicy.DEFAULT_MAX_RETRIES,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
requestQueue.add(request);
}
Full Review / Conclusion
In this blog tutorial, I was able to show you how to use Google’s reCAPTCHA in our Android app. Understand using reCAPTCHA in any app, we need to get one Site key and one Secret key and after that, we request for the captcha from the reCAPTCHA server. Once we get the reCAPTCHA and the user has entered the captcha, we send the entered value to the reCPATCA server and get the captcha token. This token is sent to our server and our server along with the secret key send the token to the reCAPTCHA server again. After that, we get some success message and that message is conveyed to our Android app.
NOTE: I have also displayed below the code for the layout for the main activity as well. This is the just a simple layout but the practical could be implemented with very little ease.
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.annotation.NonNull;
//volley
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.RequestQueue;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.safetynet.SafetyNet;
import com.google.android.gms.safetynet.SafetyNetApi;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import androidx.appcompat.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
String TAG = MainActivity.class.getSimpleName();
Button btn;
TextView txtV;
// TODO - replace the SITE KEY with yours
String Site_Key = "6LcFP8cUAAAAALPrBpvuSPileb7vd"; //consider making global variable(this will not work not a valid key)
// TODO - replace the Secret KEY with yours
String Site_Secret_Key = "6LcFP8cUAAAAAJyKpv8FKRkd1bSnR-"; //consider making global variable(this will not work not a valid key)
RequestQueue requestQueue;
//application space controls
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.reCaptcha);
txtV = findViewById(R.id.verifyText);
btn.setOnClickListener(this);
requestQueue = Volley.newRequestQueue(getApplicationContext());
}
@Override
public void onClick(View view){
SafetyNet.getClient(this).verifyWithRecaptcha(Site_Key)
.addOnSuccessListener(this, new OnSuccessListener <SafetyNetApi.RecaptchaTokenResponse>(){
@Override
public void onSuccess(SafetyNetApi.RecaptchaTokenResponse response){
if (!response.getTokenResult().isEmpty()){
handleCaptchaResult(response.getTokenResult());
}
}
})
.addOnFailureListener(this, new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
if (e instanceof ApiException){
ApiException apiException = (ApiException)e;
Log.d(TAG, "Error Message: " + CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()));
} else {
Log.d(TAG, "Unknown error type or error" + e.getMessage());
}
}
});
}
void handleCaptchaResult(final String responseToken){
String url = "https://www.google.com/recaptcha/api/siteverify"; //consider using global variable here
StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
if (jsonObject.getBoolean("success")) {
txtV.setTextSize(35);
txtV.setText("Congratulations! You're not a robot anymore");
}
} catch (Exception ex) {
Log.d(TAG, "Error message: " + ex.getMessage());
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "Error message: " + error.getMessage());
}
})
{
@Override
protected Map<String,String> getParams(){
Map<String,String> params = new HashMap<>();
params.put("secret", Site_Secret_Key);
params.put("response", responseToken);
return params;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(50000,DefaultRetryPolicy.DEFAULT_MAX_RETRIES,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
requestQueue.add(request);
}
}
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/reCaptcha"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Show reCAPTCHA"/>
<TextView
android:id="@+id/verifyText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:capitalize="characters"
android:text="Hello World"
android:textSize="24sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Awesome post! Keep up the great work! 🙂
Great content! Super high-quality! Keep it up! 🙂
Thanks for some other informative website. The place else may just I get that kind of information written in such an ideal way? I’ve a challenge that I am just now working on, and I’ve been at the look out for such info.
Hi! I just wanted to ask if you ever have any trouble with hackers? My last blog (wordpress) was hacked and I ended up losing months of hard work due to no backup. Do you have any solutions to prevent hackers?
Someone essentially lend a hand to make severely articles I might state.
This is the very first time I frequented your web page and to this point?
I surprised with the analysis you made to make this actual publish amazing.
Fantastic task!
Useful info. Fortunate me I discovered your web site accidentally, and I am surprised why this
accident didn’t came about in advance! I bookmarked it.
Very good website you have here but I was wondering if you knew of any user discussion forums
that cover the same topics talked about here? I’d really like to be a part of online community where
I can get feed-back from other knowledgeable people that share the same interest.
If you have any recommendations, please let me
know. Appreciate it!
Hiya, I am really glad I have found this info. Nowadays bloggers publish just about gossip and web stuff and this is actually frustrating. A good website with exciting content, that’s what I need. Thank you for making this web-site, and I’ll be visiting again. Do you do newsletters by email?
If you are going for most excellent contents like me, simply visit this web page all
the time as it provides quality contents, thanks