1. 程式人生 > >Integrating multiple reCAPTCHAs into Angular 2

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 the Window object. In the HavenLife application we use a simple service for injecting the Window
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

selector found in HTML form templates. This directive houses 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. ?

Please use a real function to get your own site-key out of configs

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.

Explicit rendering of the reCAPTCHA with success and expiration callbacks.
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 .

reCAPTCHA tokens as observable subjects on a service

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.

Sample response json