You might have used Google reCAPTCHA v2 to protect your site from bots. With reCAPTCHA version 2, you can either use the famous “I’m not a robot” checkbox or the “Invisible reCAPTCHA badge”. While the previous versions demand some user interactions, reCAPTCHA v3 is incredibly invisible. Everything works in the background and users don’t need to solve the challenges. It simply returns a score between 0.0 to 1.0 based on the traffic behavior on your site or application. If you are interested in reading more about the Google reCAPTCHA or the differences between various versions of it, check my post – What is Google reCAPTCHA? reCAPTCHA v2 vs v3 explained.
Now let’t see how to implement the latest, reCAPTCHA v3 in a Spring Boot application. First step is to register your site to generate a site key and secret key. Login to your Google account or create a new one specifically for your site and then click the below URL to go to the reCAPTCHA admin panel:
https://www.google.com/recaptcha/admin/create
The registration form looks like this:
- Label: a name given to your site to identify.
- Type: select reCAPTCHA v3
- Domains: Enter the domains and subdomains (if any) comma separated. Please note, if you are registering for an application that is going to be used/tested in a local server you can add that too in the domains list. Same is the case with your test or stage servers too. A sample list of domains can be yourdomain.com, test.yourdomain.com, localhost
- Owners: The Google account email will be prefilled.
Just keep the “Send alerts to owners” option checked to get alerts about any issues with your registered site. Next, read and accept the Terms of Service and click Submit. Once the form is submitted successfully, Google generates a site key and secret key for your domain(s). Now go to the “reCAPTCHA keys” section and copy the site key and secret key. You should have both the keys saved. This completes the registration process. Next is to add the required code on client and server sides.
Let’s add reCAPTCHA v3 to the login page, the client side.
1. Load the JavaScript API by passing the site key.
<script src="https://www.google.com/recaptcha/api.js?render=SITE_KEY"></script>
2. Just add the following hidden variables to your current login form:
<form name='loginForm' action="${HOSTPATH}/auth" method="POST">
<!-- Your current fields -->
<input type="hidden" name="actionName" value="home" />
<input type="hidden" name="g-recaptcha-tkn" id="recaptchaToken">
</form>
3. Use the grecaptcha object to call the execute() method by passing the site key and action name.
grecaptcha.ready(function() {
//calling execute method to receive a token
grecaptcha.execute('SITE_KEY', {action: 'login'}).then(function(token) {
document.getElementById('recaptchaToken').value=token; //setting the received token to be submitted with the login form.
});
});
‘Actions‘ is a new concept introduced in reCAPTCHA v3. You can have different action names specified in each place you execute reCAPTCHA. An action name helps you to view a detailed break-down of the top 10 actions in the Google admin console. Here ‘login’ is our action name. Google also suggests to verify the action name in the reCAPTCHA response on the server side. Following is the structure of the JSON object received as a response from the Google server:
{
"success": true|false, // whether the reCAPTCHA token sent from your site was valid
"score": number // the score for this request (0.0 - 1.0). Towards 0.0 it is likely a bot and towards 1.0 it is likely human.
"action": string // the action name for the current request/action
"challenge_ts": timestamp, // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
"hostname": string, // the hostname of your site
"error-codes": [...] // Will have the error codes if any.
}
Finally, let’s set up our AuthenticationSuccessHandler to verify the reCAPTCHA response. Basically we need to make a POST request to the URL – https://www.google.com/recaptcha/api/siteverify by passing the secret key and token.
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) {
String gRecaptchaToken = request.getParameter("g-recaptcha-tkn"); //the token submitted from the login form
String secretKey = 'SECRET_KEY'; //use your secret key
boolean verificationResultOk = false;
float expectedScore = 0.6f, scoreFromResponse = 0; //set the expectedScore as per your requirement
String captchaResponseActionName = "";
String captchaRequestActionName = request.getParameter("actionName"); //action name submitted from the login form
try {
URL obj = new URL("https://www.google.com/recaptcha/api/siteverify");
//make a connection to the URL
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
//pass secret key and token as POST params
StringBuilder postParams = new StringBuilder("secret=").append(secretKey).append("&response=").append(gRecaptchaToken);
con.setDoOutput(true);
OutputStream wr = con.getOutputStream();
wr.write(postParams.toString().getBytes("UTF-8"));
wr.flush();
wr.close();
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer responseStr = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
responseStr.append(inputLine);
}
in.close();
//Next, parse the JSON response
JsonReader jsonReader = Json.createReader(new StringReader(responseStr.toString()));
JsonObject jsonObject = jsonReader.readObject();
jsonReader.close();
captchaResponseActionName = jsonObject.getString("action");
scoreFromResponse = Float.parseFloat(jsonObject.get("score").toString());
verificationResultOk = jsonObject.getBoolean("success");
} catch (Exception e) {
log.error("*** captcha verification failure!"+ e.getMessage());
verificationResultOk = false; //in any case of error, set verification result flag to false
}
if(captchaResponseActionName.equals(captchaRequestActionName) && verificationResultOk && scoreFromResponse > expectedScore) {
//your code to handle the successfully authenticated user
} else {
redirectStrategy.sendRedirect(request, response, "/home?captcha_err");
}
}