Like many companies, Lucid employs the use of Completely Automated Public Turing tests to tell Humans and Computers Apart (CAPTCHAs). They provide our system security benefits such as decreasing the likelihood of bot accounts and hindering user enumeration attacks.
However, we have found that they also present unnecessary friction to our users. We discovered that users were hitting a CAPTCHA checkbox modal twice just in the initial purchase flow (once for registration and once for payment). Since the self-serve funnel is a key part of our products’ success, this was unacceptable, so we set out to find a way to improve the user experience around our CAPTCHAs while maintaining our site’s security.
There are many considerations to provide a smooth, secure experience while continuing to verify the humanness of our users. While this article doesn’t delve deeply into that aspect, it is important to consider alternative CAPTCHA providers like hCAPTCHA or even alternative human verification methods that don’t involve CAPTCHA. In Lucid’s case, we decided to stick with Google’s reCAPTCHA API, which has multiple options with varying levels of UX interruption. In this article, we will discuss how three Google reCAPTCHA options work and the criteria we used to decide which to implement at Lucid.
Under the hood
The original concept of CAPTCHA is quite simple–lock user interaction behind a Turing Test that is easy for a human to solve but difficult for a bot. In practice, things are a bit more complicated, especially with the introduction of “No CAPTCHA”, which has been proven to be a significant user experience improvement. In order to clarify the complex processes behind Google reCAPTCHA and compare the differences, we created some sequence diagrams using Lucidchart.
The process of verification starts when a user interacts with a UI element to which the CAPTCHA is anchored. Depending on the implementation, the interaction is either automatically intercepted or manually passed on to the CAPTCHA element. This element then communicates the registered site key and all sorts of information about the client session and user interaction to the CAPTCHA provider. Google then uses this data to determine if the user is likely human or not. If the CAPTCHA server isn’t satisfied, it will respond with a random challenge for the user. Both the solution to this challenge and information about the interaction is passed back to the server and this cycle repeats until the server is satisfied with the user’s humanness. Once this happens, the server responds with a verification token.
At this point, execution is passed back to our application code. When the user submits the form, we pass the token received from the CAPTCHA element along with the form data to our service. The service restricts completion of the request with a CAPTCHA token validation check. We provide the token along with our site secret to the CAPTCHA server, which responds with a simple success or failure that we then handle accordingly.
Where we were: reCAPTCHA v2 Modal
Normally, the reCAPTCHA v2 checkbox is embedded in the form between the final field and the submit button. The initial implementation of reCAPTCHA in our product was done for a special case surrounding rate-limiting share actions; after a certain number of sharing invitations, we show a CAPTCHA dialog box that the user must pass before continuing. In order to provide a solution quickly, we had reused this model in other pages that would have otherwise followed the normal pattern. In the flow above, the CAPTCHA element is embedded in the form and provides a CAPTCHA token prior to submission. In our implementation, CAPTCHA is not involved until after the user clicks submit, at which point a modal that houses the CAPTCHA element intercepts the user action and the user must satisfy the CAPTCHA before the submission is executed. Our application then passes the CAPTCHA token along with form data to the server for validation.
Because the modal makes it so prominent, this usage could be considered even more invasive to the user experience than the standard reCAPTCHA v2 pattern of embedding CAPTCHA into the form. We felt this pain especially in flows where the modal appears multiple times, so we began considering alternatives.
Alternative 1: Invisible reCAPTCHA v2
Google offers a straightforward solution to the annoying checkbox with Invisible reCAPTCHA v2. In this version, the user verification step is tied directly to the user action, rather than a separate widget. When the page loads, the reCAPTCHA library is loaded and attached to the DOM element for the user interaction we want to validate. Now, when the user submits the form, the CAPTCHA is automatically executed, followed by a request to complete the action once we have a validation token.
There are several advantages to this approach.
- It is very similar to our current solution: By simply inserting our new site keys and telling the API we want an invisible widget, we can migrate to the Invisible CAPTCHA with very little overhead.
- Zero additional friction for users who pass the initial check: One click, one submission. However, users who do not pass the initial check will still be presented with some friction as they will need to complete a CAPTCHA challenge to proceed. This is a significant case to consider since many legitimate users will, for various reasons, not pass the initial check.
Alternative 2: reCAPTCHA v3
The second approach we explored was Google reCAPTCHA v3. This version follows a drastically different paradigm from the previous approaches. Instead of deciding whether a client seems bot-like or human-like, Google provides a risk score and allows us to decide how to handle it. To that end, every CAPTCHA interaction results in a token, which acts more like a transaction ID than a validation token. At the time a token is requested, interaction details including a new concept of an “action” are recorded in the system. Then, when our server tries to validate the token, the CAPTCHA server runs that information through a site-specific machine learning engine to generate a risk score. It is left to our application to decide what to do with low scores.
What constitutes a low score and how to handle one should be tailored to each action, rather than simply always showing users a CAPTCHA challenge. For instance, we may decide that a score of 0.6 from the registration flow is probably suspicious and that we should require an email verification from users that fall below that score. Using reCAPTCHA v3 removes the need for a CAPTCHA challenge, removing much of the fiction that annoys users.
However, friction for bots does not go away, which is the whole point of implementing CAPTCHA in the first place. The key is that with reCAPTCHA v3, developers are given the power over whom to present friction and what form the friction takes. Google provides a dashboard tool for site admins to review score distributions for each action. Considering that there will be some bot users with higher scores than some human users, admins can use the information to determine an optimal score threshold to block the most bots while negatively impacting the least number of human users.
Because reCAPTCHA v3 uses a machine learning engine confined to individual sites, it is recommended to cover many user interactions in order to give that system enough data to make scores accurate. Since acting on a score is completely optional, there really is no limit to how much of a user’s interaction with our site we could cover without affecting the user experience. However, this does mean that in order to get reliable results, there is an added development cost. In addition, Google only allows up to one million calls to the API per month for free. More site coverage with a large user base means likely exceeding that, which means a likely additional business cost to consider aside from extra engineering.
All the reCAPTCHA options discussed here offer site security and a level of protection from bot users. The biggest component to this decision is to consider the user experience and the resources available to teams. Regular reCAPTCHA v2 is easy to implement, but it is invasive and the added interaction with the “I am not a robot” checkbox is completely unnecessary. On the other hand, Google reCAPTCHA v3 offers the best user experience, as well as the most control over that experience, but also comes with the highest cost to implement and maintain. Invisible reCAPTCHA v2 offers a nice balance. It is no more difficult to implement than regular reCAPTCHA v2 and for many users will result in no added friction to their experience.