1 | # Angular component for Google reCAPTCHA
|
2 |
|
3 | ## ng-recaptcha [](https://www.npmjs.com/package/ng-recaptcha)
|
4 |
|
5 | [](https://raw.githubusercontent.com/dethariel/ng-recaptcha/master/LICENSE)
|
6 | [](https://app.travis-ci.com/DethAriel/ng-recaptcha)
|
7 | [](https://coveralls.io/github/DethAriel/ng-recaptcha?branch=master)
|
8 | [](https://www.npmjs.com/package/ng-recaptcha)
|
9 |
|
10 | A simple, configurable, easy-to-start component for handling reCAPTCHA v2 and v3.
|
11 |
|
12 | ## Table of contents
|
13 |
|
14 | 1. [Installation](#installation)
|
15 | 1. [Basic Usage](#example-basic)
|
16 | - [reCAPTCHA v3 Usage](#example-basic-v3)
|
17 | - [Playground](#playground)
|
18 | 1. [Working with `@angular/forms`](#forms-ready)
|
19 | 1. [API](#api)
|
20 | - [Input Options](#api-options)
|
21 | - [Events](#api-events)
|
22 | - [Methods](#api-methods)
|
23 | 1. [Angular version compatibility](#angular-versions)
|
24 | 1. [Examples](#examples)
|
25 | - [Configuring the component globally](#example-global-config)
|
26 | - [Specifying a different language](#example-language)
|
27 | - [Handling errors](#example-error-handling)
|
28 | - [Loading the reCAPTCHA API by yourself](#example-preload-api)
|
29 | - [Usage with `required` in forms](#example-forms)
|
30 | - [Working with invisible reCAPTCHA](#example-invisible)
|
31 | - [Resizing](#example-resizing)
|
32 | - [SystemJS configuration](#example-systemjs)
|
33 | - [Loading from a different location](#example-different-url)
|
34 | - [Specifying nonce for Content-Security-Policy](#example-csp-nonce)
|
35 | - [Listening for all actions with reCAPTCHA v3](#example-v3-all-actions)
|
36 | - [Loading reCAPTCHA keys asynchronously](#async-config-load)
|
37 | - [Hiding reCAPTCHA badge](#hide-recaptcha-badge)
|
38 |
|
39 | ## <a name="installation"></a>Installation
|
40 |
|
41 | The easiest way is to install through [yarn](https://yarnpkg.com/package/ng-recaptcha) or [npm](https://www.npmjs.com/package/ng-recaptcha):
|
42 |
|
43 | ```sh
|
44 | yarn add ng-recaptcha
|
45 | npm i ng-recaptcha --save
|
46 | ```
|
47 |
|
48 | ## <a name="example-basic"></a>Basic Usage [(see in action)](https://dethariel.github.io/ng-recaptcha/)
|
49 |
|
50 | The below applies to reCAPTCHA v2, for basic usage with reCAPTCHA v3 scroll down to [here](#example-basic-v3).
|
51 |
|
52 | To start with, you need to import the `RecaptchaModule` (more on that [later](#modules)):
|
53 |
|
54 | ```typescript
|
55 | // app.module.ts
|
56 | import { RecaptchaModule } from "ng-recaptcha";
|
57 | // if you need forms support:
|
58 | // import { RecaptchaModule, RecaptchaFormsModule } from 'ng-recaptcha';
|
59 | import { BrowserModule } from "@angular/platform-browser";
|
60 | import { MyApp } from "./app.component.ts";
|
61 |
|
62 | @NgModule({
|
63 | bootstrap: [MyApp],
|
64 | declarations: [MyApp],
|
65 | imports: [
|
66 | BrowserModule,
|
67 | RecaptchaModule,
|
68 | // RecaptchaFormsModule, // if you need forms support
|
69 | ],
|
70 | })
|
71 | export class MyAppModule {}
|
72 | ```
|
73 |
|
74 | Once you have done that, the rest is simple:
|
75 |
|
76 | ```typescript
|
77 | // app.component.ts
|
78 | import { Component } from "@angular/core";
|
79 |
|
80 | @Component({
|
81 | selector: "my-app",
|
82 | template: `<re-captcha (resolved)="resolved($event)" siteKey="YOUR_SITE_KEY"></re-captcha>`,
|
83 | })
|
84 | export class MyApp {
|
85 | resolved(captchaResponse: string) {
|
86 | console.log(`Resolved captcha with response: ${captchaResponse}`);
|
87 | }
|
88 | }
|
89 | ```
|
90 |
|
91 | ```typescript
|
92 | // main.ts
|
93 | import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
94 | import { MyAppModule } from "./app.module.ts";
|
95 |
|
96 | platformBrowserDynamic().bootstrapModule(MyAppModule);
|
97 | ```
|
98 |
|
99 | ### <a name="example-basic-v3"></a>reCAPTCHA v3 Usage [(see in action)](https://dethariel.github.io/ng-recaptcha/v3)
|
100 |
|
101 | [reCAPTCHA v3](https://developers.google.com/recaptcha/docs/v3) introduces a different way of bot protection. To work with v3 APIs, `ng-recaptcha` provides a service (as opposed to a component). To start with, you need to import the `RecaptchaV3Module` and provide your reCAPTCHA v3 site key using `RECAPTCHA_V3_SITE_KEY` injection token:
|
102 |
|
103 | ```ts
|
104 | import { BrowserModule } from "@angular/platform-browser";
|
105 | import { RECAPTCHA_V3_SITE_KEY, RecaptchaV3Module } from "ng-recaptcha";
|
106 |
|
107 | import { MyApp } from "./app.component.ts";
|
108 |
|
109 | @NgModule({
|
110 | bootstrap: [MyApp],
|
111 | declarations: [MyApp],
|
112 | imports: [BrowserModule, RecaptchaV3Module],
|
113 | providers: [{ provide: RECAPTCHA_V3_SITE_KEY, useValue: "<YOUR_SITE_KEY>" }],
|
114 | })
|
115 | export class MyAppModule {}
|
116 | ```
|
117 |
|
118 | In order to execute a reCAPTCHA v3 action, import the `ReCaptchaV3Service` into your desired component:
|
119 |
|
120 | ```ts
|
121 | import { ReCaptchaV3Service } from 'ng-recaptcha';
|
122 |
|
123 | @Component({
|
124 | selector: 'recaptcha-demo',
|
125 | template: `
|
126 | <button (click)="executeImportantAction()">Important action</button>
|
127 | `,
|
128 | })
|
129 | export class RecaptchaV3DemoComponent {
|
130 | constructor(
|
131 | private recaptchaV3Service: ReCaptchaV3Service,
|
132 | ) {
|
133 | }
|
134 |
|
135 | public executeImportantAction(): void {
|
136 | this.recaptchaV3Service.execute('importantAction')
|
137 | .subscribe((token) => this.handleToken(token));
|
138 | }
|
139 | ```
|
140 |
|
141 | As always with subscriptions, please don't forget to **unsubscribe**.
|
142 |
|
143 | ❗️ **Important note**: If your site uses both v2 and v3, then you should _always_ provide `RECAPTCHA_V3_SITE_KEY` (even in modules that only rely on v2 functionality). This will prevent bugs in your code by allowing `ng-recaptcha` to follow reCAPTCHA development guidelines properly ([this one](https://developers.google.com/recaptcha/docs/faq#can-i-run-recaptcha-v2-and-v3-on-the-same-page) in particular).
|
144 |
|
145 | A more advanced v3 usage scenario includes listening to all actions and their respectively emitted tokens. This is covered [later on this page](#example-v3-all-actions).
|
146 |
|
147 | ### <a name="playground"></a>Playground
|
148 |
|
149 | You can also play with [this Stackblitz demo](https://stackblitz.com/edit/ng-recaptcha-example) to get a feel of how this component can be used.
|
150 |
|
151 | ## <a name="forms-ready"></a>Working with `@angular/forms`
|
152 |
|
153 | There are two modules available for you:
|
154 |
|
155 | ```typescript
|
156 | import { RecaptchaModule, RecaptchaFormsModule } from "ng-recaptcha";
|
157 | ```
|
158 |
|
159 | If you want your `<re-captcha>` element to work correctly with `[(ngModel)]` directive,
|
160 | you need to import the `RecaptchaFormsModule` into your application module (pretty much
|
161 | like with Angular own `'@angular/forms'` module).
|
162 |
|
163 | ## <a name="api"></a>API
|
164 |
|
165 | ### <a name="api-options"></a>Input Options
|
166 |
|
167 | The component supports this options:
|
168 |
|
169 | - `siteKey`
|
170 | - `theme`
|
171 | - `type`
|
172 | - `size`
|
173 | - `tabIndex`
|
174 | - `badge`
|
175 |
|
176 | They are all pretty well described either in the [reCAPTCHA docs](https://developers.google.com/recaptcha/docs/display), or in the [invisible reCAPTCHA docs](https://developers.google.com/recaptcha/docs/invisible),
|
177 | so I won't duplicate it here.
|
178 |
|
179 | One additional option that component accepts is `errorMode`. You can learn more about it in the [Handling errors](#example-error-handling) section below.
|
180 |
|
181 | Besides specifying these options on the component itself, you can provide a global `<re-captcha>` configuration - see [Configuring the component globally](#example-global-config) section below.
|
182 |
|
183 | ### <a name="api-events"></a>Events
|
184 |
|
185 | - `resolved(response: string)`. Occurs when the captcha resolution value changed.
|
186 | When user resolves captcha, use `response` parameter to send to the server for verification.
|
187 | This parameter is equivalent to calling [`grecaptcha.getResponse`](https://developers.google.com/recaptcha/docs/display#js_api).
|
188 |
|
189 | If the captcha has expired prior to submitting its value to the server, the component
|
190 | will reset the captcha, and trigger the `resolved` event with `response === null`.
|
191 |
|
192 | - `errored(errorDetails: RecaptchaErrorParameters)`. Occurs when reCAPTCHA encounters an error (usually a connectivity problem) **if and only if** `errorMode` input has been set to `"handled"`.
|
193 | `errorDetails` is a simple propagation of any arguments that the original `error-callback` has provided, and is documented here for the purposes of completeness and future-proofing. This array will most often (if not always) be empty. A good strategy would be to rely on just the fact that this event got triggered, and show a message to your app's user telling them to retry.
|
194 |
|
195 | ### <a name="api-methods"></a>Methods
|
196 |
|
197 | - `reset()`. Performs a manual captcha reset. This method might be useful if your form
|
198 | validation failed, and you need the user to re-enter the captcha.
|
199 | - `execute()`. Executes the invisible recaptcha. Does nothing if component's size is not set to "invisible". See [Invisible reCAPTCHA developers guide](https://developers.google.com/recaptcha/docs/invisible#js_api) for more information.
|
200 |
|
201 | ## <a name="angular-versions"></a>Angular version compatibility
|
202 |
|
203 | | `ng-recaptcha` version | Supported Angular versions |
|
204 | | -------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
205 | | `13.x.x` | `17.x.x` |
|
206 | | `12.x.x` | `16.x.x` |
|
207 | | `11.x.x` | `15.x.x` |
|
208 | | `10.x.x` | `14.x.x` |
|
209 | | `9.x.x` | `13.x.x` |
|
210 | | `8.x.x` | `12.x.x` |
|
211 | | `7.x.x` | `11.x.x` |
|
212 | | ⬆️ Starting with `ng-recaptcha@7`, only one version of Angular will be supported |
|
213 | | `6.x.x` | `6.x.x \|\| 7.x.x \|\| 8.x.x \|\| 9.x.x \|\| 10.x.x` |
|
214 | | `5.x.x` | `6.x.x \|\| 7.x.x \|\| 8.x.x` |
|
215 | | `4.x.x` | `6.x.x \|\| 7.x.x` |
|
216 | | `3.x.x` | `4.x.x \|\| 5.x.x` |
|
217 | | `2.x.x` | `2.x.x \|\| 4.x.x` |
|
218 | | `1.x.x` | `2.x.x` |
|
219 |
|
220 | ## <a name="examples"></a>Examples
|
221 |
|
222 | ### <a name="example-global-config"></a>Configuring the component globally [(see in action)](https://dethariel.github.io/ng-recaptcha/global-config)
|
223 |
|
224 | Some properties are global - including `siteKey`, `size`, and others. You can provide them at the module-level using the `RECAPTCHA_SETTINGS` provider:
|
225 |
|
226 | ```typescript
|
227 | import { RECAPTCHA_SETTINGS, RecaptchaSettings } from "ng-recaptcha";
|
228 |
|
229 | @NgModule({
|
230 | providers: [
|
231 | {
|
232 | provide: RECAPTCHA_SETTINGS,
|
233 | useValue: { siteKey: "<YOUR_KEY>" } as RecaptchaSettings,
|
234 | },
|
235 | ],
|
236 | })
|
237 | export class MyModule {}
|
238 | ```
|
239 |
|
240 | Global properties can be overridden on a case-by-case basis - the values on the `<re-captcha>` component itself take precedence over global settings.
|
241 |
|
242 | ### <a name="example-language"></a>Specifying a different language [(see in action)](https://dethariel.github.io/ng-recaptcha/basic?lang-fr)
|
243 |
|
244 | `<re-captcha>` supports various languages. By default recaptcha will guess the user's language itself
|
245 | (which is beyond the scope of this lib).
|
246 | But you can override this behavior and provide a specific language to use by setting the `"hl"` search param in the `onBeforeLoad` hook.
|
247 | Note, that the language setting is **global**, and cannot be set on a per-captcha basis.
|
248 |
|
249 | A good way to synchronize reCAPTCHA language with the rest of your application is relying on `LOCALE_ID` value like so:
|
250 |
|
251 | ```typescript
|
252 | import { LOCALE_ID } from "@angular/core";
|
253 | import { RECAPTCHA_LOADER_OPTIONS } from "ng-recaptcha";
|
254 |
|
255 | @NgModule({
|
256 | providers: [
|
257 | {
|
258 | provide: RECAPTCHA_LOADER_OPTIONS,
|
259 | useFactory: (locale: string) => ({
|
260 | onBeforeLoad(url) {
|
261 | url.searchParams.set("hl", locale);
|
262 |
|
263 | return { url };
|
264 | },
|
265 | }),
|
266 | deps: [LOCALE_ID],
|
267 | },
|
268 | ],
|
269 | })
|
270 | export class MyModule {}
|
271 | ```
|
272 |
|
273 | Alternatively, a specific language can be provided like so:
|
274 |
|
275 | ```typescript
|
276 | import { RECAPTCHA_LOADER_OPTIONS } from "ng-recaptcha";
|
277 |
|
278 | @NgModule({
|
279 | providers: [
|
280 | {
|
281 | provide: RECAPTCHA_LOADER_OPTIONS,
|
282 | useValue: {
|
283 | onBeforeLoad(url) {
|
284 | url.searchParams.set("hl", "fr"); // use French language
|
285 |
|
286 | return { url };
|
287 | },
|
288 | },
|
289 | },
|
290 | ],
|
291 | })
|
292 | export class MyModule {}
|
293 | ```
|
294 |
|
295 | You can find the list of supported languages in [reCAPTCHA docs](https://developers.google.com/recaptcha/docs/language).
|
296 |
|
297 | ### <a name="example-error-handling"></a>Handling errors
|
298 |
|
299 | Sometimes reCAPTCHA encounters an error, which is usually a network connectivity problem. It cannot continue until connectivity is restored. By default, reCAPTCHA lets the user know that an error has happened (it's a built-in functionality of reCAPTCHA itself, and this lib is not in control of it). The downside of such behavior is that you, as a developer, don't get notified about this in any way. Opting into such notifications is easy, but comes at a cost of assuming responsibility for informing the user that they should retry. Here's how you would do this:
|
300 |
|
301 | ```typescript
|
302 | import { Component } from "@angular/core";
|
303 |
|
304 | @Component({
|
305 | selector: "my-app",
|
306 | template: `<re-captcha (resolved)="resolved($event)" (errored)="errored($event)" errorMode="handled"></re-captcha>`,
|
307 | })
|
308 | export class MyApp {
|
309 | resolved(captchaResponse: string) {
|
310 | console.log(`Resolved captcha with response: ${captchaResponse}`);
|
311 | }
|
312 |
|
313 | errored() {
|
314 | console.warn(`reCAPTCHA error encountered`);
|
315 | }
|
316 | }
|
317 | ```
|
318 |
|
319 | You can see this in action by navigating to either [basic example demo](https://dethariel.github.io/ng-recaptcha/basic) or [invisible demo](https://dethariel.github.io/ng-recaptcha/invisible) and trying to interact with reCAPTCHA after setting the network to "Offline".
|
320 |
|
321 | The `errorMode` input has two possible values -- `"handled"` and `"default"`, with latter being the default as the name suggests. Not specifying `errorMode`, or setting it to anything other than `"handled"` will not invoke your `(errored)` callback, and will instead result in default reCAPTCHA functionality.
|
322 |
|
323 | The `(errored)` callback will propagate all of the parameters that it receives from `grecaptcha['error-callback']` (which might be none) as an array.
|
324 |
|
325 | ### <a name="example-preload-api"></a>Loading the reCAPTCHA API by yourself [(see in action)](https://dethariel.github.io/ng-recaptcha/v8/preload-api)
|
326 |
|
327 | By default, the component assumes that the reCAPTCHA API loading will be handled
|
328 | by the `RecaptchaLoaderService`. However, you can override that by providing your
|
329 | instance of this service to the Angular DI.
|
330 |
|
331 | The below code snippet is an example of how such a provider can be implemented.
|
332 |
|
333 | **TL;DR**: there should be an `Observable` that eventually resolves to a
|
334 | `grecaptcha`-compatible object (e.g. `grecaptcha` itself).
|
335 |
|
336 | ```html
|
337 | <script src="https://www.google.com/recaptcha/api.js?render=explicit&onload=onloadCallback"></script>
|
338 |
|
339 | <script>
|
340 | // bootstrap the application once the reCAPTCHA api has loaded
|
341 | function onloadCallback() {
|
342 | System.import("app").catch(function (err) {
|
343 | console.error(err);
|
344 | });
|
345 | }
|
346 | </script>
|
347 | ```
|
348 |
|
349 | ```typescript
|
350 | import { RecaptchaLoaderService } from "ng-recaptcha";
|
351 |
|
352 | @Injectable()
|
353 | export class PreloadedRecaptchaAPIService {
|
354 | public ready: Observable<ReCaptchaV2.ReCaptcha>;
|
355 |
|
356 | constructor() {
|
357 | let readySubject = new BehaviorSubject<ReCaptchaV2.ReCaptcha>(grecaptcha);
|
358 | this.ready = readySubject.asObservable();
|
359 | }
|
360 | }
|
361 |
|
362 | @NgModule({
|
363 | providers: [
|
364 | {
|
365 | provide: RecaptchaLoaderService,
|
366 | useValue: new PreloadedRecaptchaAPIService(),
|
367 | },
|
368 | ],
|
369 | })
|
370 | export class MyModule {}
|
371 | ```
|
372 |
|
373 | ### <a name="example-forms"></a>Usage with `required` in forms [(see in action)](https://dethariel.github.io/ng-recaptcha/forms)
|
374 |
|
375 | It's very easy to put `<re-captcha>` in an Angular form and have it `require`d - just
|
376 | add the `required` attribute to the `<re-captcha>` element. Do not forget to import `RecaptchaFormsModule` from `'ng-recaptcha'`!
|
377 |
|
378 | ```typescript
|
379 | @Component({
|
380 | selector: "my-form",
|
381 | template: ` <form>
|
382 | <re-captcha [(ngModel)]="formModel.captcha" name="captcha" required siteKey="YOUR_SITE_KEY"></re-captcha>
|
383 | </form>`,
|
384 | })
|
385 | export class MyForm {
|
386 | formModel = new MyFormModel();
|
387 | }
|
388 | ```
|
389 |
|
390 | A similar approach can be taken for reactive forms:
|
391 |
|
392 | ```typescript
|
393 | @Component({
|
394 | selector: "my-reactive-form",
|
395 | template: `
|
396 | <form [formGroup]="reactiveForm">
|
397 | <re-captcha formControlName="recaptchaReactive"></re-captcha>
|
398 | <button [disabled]="reactiveForm.invalid">Submit</button>
|
399 | </form>
|
400 | `,
|
401 | })
|
402 | export class MyReactiveForm {
|
403 | reactiveForm: FormGroup;
|
404 |
|
405 | ngOnInit() {
|
406 | this.reactiveForm = new FormGroup({
|
407 | recaptchaReactive: new FormControl(null, Validators.required),
|
408 | });
|
409 | }
|
410 | }
|
411 | ```
|
412 |
|
413 | ### <a name="example-invisible"></a>Working with invisible reCAPTCHA [(see in action)](https://dethariel.github.io/ng-recaptcha/invisible)
|
414 |
|
415 | Working with [invisible reCAPTCHA](https://developers.google.com/recaptcha/docs/invisible) is almost the same as with regular one.
|
416 | First, you need to provide the right size:
|
417 |
|
418 | ```html
|
419 | <re-captcha size="invisible" ...></re-captcha>
|
420 | ```
|
421 |
|
422 | You will also need to invoke the [`"execute()"`](https://developers.google.com/recaptcha/docs/invisible#programmatic_execute) method manually. This can be done by either obtaining a reference to `RecaptchaComponent` via `@ViewChild()`, or by using inline template reference:
|
423 |
|
424 | ```html
|
425 | <re-captcha #captchaRef="reCaptcha" ...></re-captcha>
|
426 | ...
|
427 | <button (click)="captchaRef.execute()">Submit</button>
|
428 | ```
|
429 |
|
430 | Normally you would only submit a form when recaptcha response has been received. This can be achieved by reacting to `(resolved)` event and invoking submit logic when the captcha response is truthy (this will not try to submit the form when recaptcha response has expired). A sample implementation would look like this:
|
431 |
|
432 | ```typescript
|
433 | @Component({
|
434 | selector: "my-form",
|
435 | template: ` <form>
|
436 | <re-captcha
|
437 | #captchaRef="reCaptcha"
|
438 | siteKey="YOUR_SITE_KEY"
|
439 | size="invisible"
|
440 | (resolved)="$event && submit($event)"
|
441 | ></re-captcha>
|
442 | <button (click)="captchaRef.execute()">Submit</button>
|
443 | </form>`,
|
444 | })
|
445 | export class MyForm {
|
446 | public submit(captchaResponse: string): void {
|
447 | this.http.post({
|
448 | captcha: captchaResponse,
|
449 | /* ... */
|
450 | });
|
451 | }
|
452 | }
|
453 | ```
|
454 |
|
455 | ### <a name="example-resizing"></a>Resizing
|
456 |
|
457 | Making reCAPTCHA responsive is sometimes necessary, especially when working with mobile devices. You can use css-transforms to achieve that as in [this StackOverflow answer](https://stackoverflow.com/a/29521983/2645305), which is also ell-described in [this blog post](https://geekgoddess.com/how-to-resize-the-google-nocaptcha-recaptcha/). You can also take a look at a [live example](https://stackblitz.com/edit/ng-recaptcha-example-uncvxq?file=src/app/app.component.html) of how this might be implemented. This boils down to
|
458 |
|
459 | ```html
|
460 | <div style="transform:scale(0.7);transform-origin:0;">
|
461 | <re-captcha></re-captcha>
|
462 | </div>
|
463 | ```
|
464 |
|
465 | ### <a name="example-systemjs"></a>SystemJS configuration
|
466 |
|
467 | To configure the package to work with SystemJS, you would configure it approximately like that (assuming you've installed `ng-recaptcha` using `npm`):
|
468 |
|
469 | ```javascript
|
470 | // SystemJS config file
|
471 | (function () {
|
472 | System.config({
|
473 | paths: {
|
474 | "npm:": "/node_modules/",
|
475 | },
|
476 | map: {
|
477 | "ng-recaptcha": "npm:ng-recaptcha",
|
478 | },
|
479 | packages: {
|
480 | "ng-recaptcha": { main: "./index.js" },
|
481 | },
|
482 | });
|
483 | })();
|
484 | ```
|
485 |
|
486 | ### <a name="example-different-url"></a>Loading from a different location
|
487 |
|
488 | Since `"google.com"` domain might be unavailable in some countries, reCAPTCHA core team has a solution for that - using `"recaptcha.net"` domain. You can configure the component to use that by using the `onBeforeLoad` hook of `RECAPTCHA_LOADER_OPTIONS`, for example:
|
489 |
|
490 | ```javascript
|
491 | import { RECAPTCHA_LOADER_OPTIONS } from "ng-recaptcha";
|
492 |
|
493 | @NgModule({
|
494 | providers: [
|
495 | {
|
496 | provide: RECAPTCHA_LOADER_OPTIONS,
|
497 | useValue: {
|
498 | onBeforeLoad(_url) {
|
499 | return {
|
500 | url: new URL("https://www.recaptcha.net/recaptcha/api.js"), // use recaptcha.net script source for some of our users
|
501 | };
|
502 | },
|
503 | },
|
504 | },
|
505 | ],
|
506 | })
|
507 | export class MyModule {}
|
508 | ```
|
509 |
|
510 | ### <a name="example-csp-nonce"></a>Specifying nonce for Content-Security-Policy
|
511 |
|
512 | Per [reCAPTCHA FAQ on CSP](https://developers.google.com/recaptcha/docs/faq#im-using-content-security-policy-csp-on-my-website-how-can-i-configure-it-to-work-with-recaptcha), the recommended approach for that is to supply nonce to the script tag. This is possible by providing the nonce as part of the `onBeforeLoad` hook of `RECAPTCHA_LOADER_OPTIONS`, for example
|
513 |
|
514 | ```javascript
|
515 | import { RECAPTCHA_LOADER_OPTIONS } from "ng-recaptcha";
|
516 |
|
517 | @NgModule({
|
518 | providers: [
|
519 | {
|
520 | provide: RECAPTCHA_LOADER_OPTIONS,
|
521 | useValue: {
|
522 | onBeforeLoad(_url) {
|
523 | return {
|
524 | url,
|
525 | nonce: "<YOUR_NONCE_VALUE>",
|
526 | };
|
527 | },
|
528 | },
|
529 | },
|
530 | ],
|
531 | })
|
532 | export class MyModule {}
|
533 | ```
|
534 |
|
535 | ### <a name="example-v3-all-actions"></a>Listening for all actions with reCAPTCHA v3
|
536 |
|
537 | More often than not you will need to only get a reCAPTCHA token with the action the user is currently taking, and submit it to the backend at that time. However, having a single listener for all events will be desirable.
|
538 |
|
539 | There is an `Observable` exactly for that purpose: `ReCaptchaV3Service.onExecute`. It emits a value every time a token is received from reCAPTCHA. The shape of payload it operates on is defined via `OnExecuteData` interface:
|
540 |
|
541 | ```ts
|
542 | interface OnExecuteData {
|
543 | action: string;
|
544 | token: string;
|
545 | }
|
546 | ```
|
547 |
|
548 | where `action` is the name of the action that has been executed, and `token` is what reCAPTCHA v3 provided when executing that action.
|
549 |
|
550 | Here's how you would potentially set this up:
|
551 |
|
552 | ```ts
|
553 | import { OnExecuteData, ReCaptchaV3Service } from "ng-recaptcha";
|
554 |
|
555 | @Component({
|
556 | selector: "my-component",
|
557 | templateUrl: "./v3-demo.component.html",
|
558 | })
|
559 | export class MyComponent implements OnInit, OnDestroy {
|
560 | private subscription: Subscription;
|
561 |
|
562 | constructor(private recaptchaV3Service: ReCaptchaV3Service) {}
|
563 |
|
564 | public ngOnInit() {
|
565 | this.subscription = this.recaptchaV3Service.onExecute.subscribe((data: OnExecuteData) => {
|
566 | this.handleRecaptchaExecute(data.action, data.token);
|
567 | });
|
568 | }
|
569 |
|
570 | public ngOnDestroy() {
|
571 | if (this.subscription) {
|
572 | this.subscription.unsubscribe();
|
573 | }
|
574 | }
|
575 | }
|
576 | ```
|
577 |
|
578 | There are a couple things to keep in mind:
|
579 |
|
580 | - `onExecute` will trigger for **all** actions. If you only need to bulk-process some actions, and not others - you will have to apply filtering yourself.
|
581 | - `onExecute` observable will provide you with all the events emitted **after** you have subscribed to it - it doesn't keep references to the previously emitted actions. So make sure you add such a subscription as early in your code as you feel is necessary.
|
582 | - `onExecute` does not emit anything for when a `grecaptcha` error occurs. Use `onExecuteError` Observable for that.
|
583 |
|
584 | ### <a name="async-config-load"></a>Loading reCAPTCHA keys asynchronously
|
585 |
|
586 | If your use-case needs to load the reCAPTCHA v2/v3 key from the backend (as opposed to specifying it in-code during build time), the Angular-idiomatic way to do that is by relying on [`APP_INITIALIZER`](https://angular.io/api/core/APP_INITIALIZER). You can find an example of how this could look like below, and you can also consult the source code for the demo site.
|
587 |
|
588 | ```ts
|
589 | // config.service.ts
|
590 | import { Injectable } from "@angular/core";
|
591 |
|
592 | @Injectable({
|
593 | providedIn: "root",
|
594 | })
|
595 | export class ConfigService {
|
596 | public recaptchaSiteKeyV2: string | null = null;
|
597 | public recaptchaSiteKeyV3: string | null = null;
|
598 |
|
599 | public async loadConfig(): Promise<void> {
|
600 | const { siteKeyV2, siteKeyV3 } = await fetchConfig(/* some API call */);
|
601 | this.recaptchaSiteKeyV2 = siteKeyV2;
|
602 | this.recaptchaSiteKeyV3 = siteKeyV3;
|
603 | }
|
604 | }
|
605 |
|
606 | // app.module.ts
|
607 | import { APP_INITIALIZER, NgModule } from "@angular/core";
|
608 | import { RECAPTCHA_SETTINGS, RecaptchaSettings, RECAPTCHA_V3_SITE_KEY } from "ng-recaptcha";
|
609 |
|
610 | import { ConfigService } from "./config.service";
|
611 |
|
612 | function appLoadFactory(config: ConfigService) {
|
613 | return () => config.loadConfig().then(() => console.log(`config resolved`, config));
|
614 | }
|
615 |
|
616 | @NgModule({
|
617 | providers: [
|
618 | {
|
619 | provide: RECAPTCHA_V3_SITE_KEY,
|
620 | useFactory: (config: ConfigService) => {
|
621 | return config.recaptchaSiteKeyV3;
|
622 | },
|
623 | deps: [ConfigService],
|
624 | },
|
625 | {
|
626 | provide: RECAPTCHA_SETTINGS,
|
627 | useFactory: (config: ConfigService): RecaptchaSettings => {
|
628 | return { siteKey: config.recaptchaSiteKeyV2 };
|
629 | },
|
630 | deps: [ConfigService],
|
631 | },
|
632 | {
|
633 | provide: APP_INITIALIZER,
|
634 | useFactory: appLoadFactory,
|
635 | deps: [ConfigService],
|
636 | multi: true,
|
637 | },
|
638 | ],
|
639 | })
|
640 | export class AppModule {}
|
641 | ```
|
642 |
|
643 | ### <a name="hide-recaptcha-badge"></a>Hiding reCAPTCHA badge
|
644 |
|
645 | To start with, this is not strictly under `ng-recaptcha` library control.
|
646 | However, there is a way of doing so (albeit subject to certain conditions).
|
647 | Please refer to the [FAQ section of reCAPTCHA documentation](https://developers.google.com/recaptcha/docs/faq#id-like-to-hide-the-recaptcha-badge.-what-is-allowed) to get an idea of what you're required to do.
|