Integrating multiple reCAPTCHAs into Angular 2
reCAPTCHA + Angular2
An uneasy pairing
Google maintains both the reCAPTCHA and Angular libraries and yet, like many siblings, the two don’t really get along. Angular 2 manages DOM manipulation using a modular system of components and templates. Functions on theWindow
object explicitly render reCAPTCHA. Drama!
To get around this conflict, we used an Angular2 directive to encapsulate behavior on the Window
. An Angular2 service handles communication between components and the client/server.
Disclaimer: it is best practice in Angular to not reference the DOM directly, including theWindow
object. In the HavenLife application we use a simple service for injecting theWindow
object into our component. For clarity in this article, we removed mention of this service and reference the window directly. Please note this is neither ideal nor advisable. Consider yourself warned. ?
Client-side Implementation
Directive and initialization
recaptcha.directive.ts
begins the implementation by listening for a creatively-named recaptcha
Window
functions to append the reCAPTCHA widget to the DOM and manages the client-side calls to Google.In the directive, we start by declaring key
and widgetId
variables and importing relevant Angular libraries. key
stores the site-key provided by Google upon registration and can be accessed via configs.
Pro Tips: Googletest keys are available that do not require registration and will always pass — never requiring users to complete a test question. These are great for automated testing but won’t catch bugs so use with caution. For local testing, you will need to register and use your local IP address, usually 127.0.0.1, as localhost is explicitly blocked.
While other writing on the integration of Angular2 and reCAPTCHA suggest creating a global variable to hold grerecaptcha
, we chose to simply declare a component-level var
of type <any>
. We avoided creating a global to limit exposure in the larger codebase. As always, do what works best in your context. ?
Next, to render the reCAPTCHA, ngOnInit()
gets the site-key and then calls two functions addScript()
and renderRecaptchaWithCallback()
.
addScript
is straightforward. The function inserts the Javascript resource, setting the onload
parameter to the name of our callback function (reCaptchaLoad)
and the render
parameter to explicit
.
addScript
also uses vanilla JS to remove any old copies of the reCAPTCHA script tag before appending to the parent. This prevents the error ReCAPTCHA placeholder element must be empty
that can occur if the user encounters multiple reCAPTCHAs in a single session.
We are now ready to render our widget! renderRecaptchaWithCallback()
sets up the config params (outlined in the docs) with styling parameters like size
and tabindex
as well as callbacks for success and reCAPTCHA expiration.
Note: this example outlines steps to implement a visible reCAPTCHA, the invisible reCAPTCHA programmatically calls grecaptcha.execute()
to invoke the challenge. More info can be found in the docs.
The next step is to define the onSuccess
callback. This function will get called when all the dependencies have loaded and returns a token used to validate on the server-side. We use an Angular service to manage tokens generated by the onSuccess
callback and made the token available as an Observable Subject .
Finally, the onExpired()
callback handles cases where the user completes the reCAPTCHA successfully but does not finish the email registration. recaptcha.service.ts
clears the token and calls upon another component-level var
instance of grecaptcha
to reset the reCAPTCHA.
This retry
function can also be used to handle any errors returned by the server-side validation. You will want to develop your own protocol for handling reCAPTCHA attempts and error messaging, but a service can be useful for managing this communication across the application.
Server-side Implementation
Token Validation
Once we pass the generated token to the server-side, we call verifyRecaptcha()
to verify that the user token is on the up-and-up before going any further in the email registration process. An HTTP request client with Promise support makes for easy error handling.
The Google reCAPTCHA API returns the following response JSON object and error codes. They are not terribly forthcoming, however, about the cause of the errors for invalid-input-response
which would ostensibly encompass any malicious token use.