ng-recaptcha-2 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +530 -0
- package/LICENSE +21 -0
- package/README.md +647 -0
- package/esm2022/index.mjs +15 -0
- package/esm2022/lib/load-script.mjs +28 -0
- package/esm2022/lib/recaptcha-common.module.mjs +16 -0
- package/esm2022/lib/recaptcha-forms.module.mjs +19 -0
- package/esm2022/lib/recaptcha-loader.service.mjs +104 -0
- package/esm2022/lib/recaptcha-settings.mjs +2 -0
- package/esm2022/lib/recaptcha-v3.module.mjs +16 -0
- package/esm2022/lib/recaptcha-v3.service.mjs +106 -0
- package/esm2022/lib/recaptcha-value-accessor.directive.mjs +68 -0
- package/esm2022/lib/recaptcha.component.mjs +171 -0
- package/esm2022/lib/recaptcha.module.mjs +19 -0
- package/esm2022/lib/tokens.mjs +14 -0
- package/esm2022/ng-recaptcha-2.mjs +5 -0
- package/fesm2022/ng-recaptcha-2.mjs +536 -0
- package/fesm2022/ng-recaptcha-2.mjs.map +1 -0
- package/index.d.ts +9 -0
- package/lib/load-script.d.ts +27 -0
- package/lib/recaptcha-common.module.d.ts +7 -0
- package/lib/recaptcha-forms.module.d.ts +9 -0
- package/lib/recaptcha-loader.service.d.ts +28 -0
- package/lib/recaptcha-settings.d.ts +8 -0
- package/lib/recaptcha-v3.module.d.ts +6 -0
- package/lib/recaptcha-v3.service.d.ts +70 -0
- package/lib/recaptcha-value-accessor.directive.d.ts +18 -0
- package/lib/recaptcha.component.d.ts +64 -0
- package/lib/recaptcha.module.d.ts +8 -0
- package/lib/tokens.d.ts +116 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,647 @@
|
|
|
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.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { RecaptchaComponent } from "./lib/recaptcha.component";
|
|
2
|
+
export { RecaptchaLoaderService } from "./lib/recaptcha-loader.service";
|
|
3
|
+
export { RecaptchaModule } from "./lib/recaptcha.module";
|
|
4
|
+
export { RecaptchaV3Module } from "./lib/recaptcha-v3.module";
|
|
5
|
+
export { ReCaptchaV3Service } from "./lib/recaptcha-v3.service";
|
|
6
|
+
export { RecaptchaFormsModule } from "./lib/recaptcha-forms.module";
|
|
7
|
+
export { RecaptchaValueAccessorDirective } from "./lib/recaptcha-value-accessor.directive";
|
|
8
|
+
export {
|
|
9
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
10
|
+
RECAPTCHA_LANGUAGE,
|
|
11
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
12
|
+
RECAPTCHA_BASE_URL,
|
|
13
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
14
|
+
RECAPTCHA_NONCE, RECAPTCHA_SETTINGS, RECAPTCHA_V3_SITE_KEY, RECAPTCHA_LOADER_OPTIONS, } from "./lib/tokens";
|
|
15
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9wcm9qZWN0cy9uZy1yZWNhcHRjaGEvc3JjL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxrQkFBa0IsRUFBNEIsTUFBTSwyQkFBMkIsQ0FBQztBQUN6RixPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN4RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFHekQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDOUQsT0FBTyxFQUFxQyxrQkFBa0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRW5HLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ3BFLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLDBDQUEwQyxDQUFDO0FBRTNGLE9BQU87QUFDTCxtREFBbUQ7QUFDbkQsa0JBQWtCO0FBQ2xCLG1EQUFtRDtBQUNuRCxrQkFBa0I7QUFDbEIsbURBQW1EO0FBQ25ELGVBQWUsRUFDZixrQkFBa0IsRUFDbEIscUJBQXFCLEVBQ3JCLHdCQUF3QixHQUV6QixNQUFNLGNBQWMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7IFJlY2FwdGNoYUNvbXBvbmVudCwgUmVjYXB0Y2hhRXJyb3JQYXJhbWV0ZXJzIH0gZnJvbSBcIi4vbGliL3JlY2FwdGNoYS5jb21wb25lbnRcIjtcbmV4cG9ydCB7IFJlY2FwdGNoYUxvYWRlclNlcnZpY2UgfSBmcm9tIFwiLi9saWIvcmVjYXB0Y2hhLWxvYWRlci5zZXJ2aWNlXCI7XG5leHBvcnQgeyBSZWNhcHRjaGFNb2R1bGUgfSBmcm9tIFwiLi9saWIvcmVjYXB0Y2hhLm1vZHVsZVwiO1xuZXhwb3J0IHsgUmVjYXB0Y2hhU2V0dGluZ3MgfSBmcm9tIFwiLi9saWIvcmVjYXB0Y2hhLXNldHRpbmdzXCI7XG5cbmV4cG9ydCB7IFJlY2FwdGNoYVYzTW9kdWxlIH0gZnJvbSBcIi4vbGliL3JlY2FwdGNoYS12My5tb2R1bGVcIjtcbmV4cG9ydCB7IE9uRXhlY3V0ZURhdGEsIE9uRXhlY3V0ZUVycm9yRGF0YSwgUmVDYXB0Y2hhVjNTZXJ2aWNlIH0gZnJvbSBcIi4vbGliL3JlY2FwdGNoYS12My5zZXJ2aWNlXCI7XG5cbmV4cG9ydCB7IFJlY2FwdGNoYUZvcm1zTW9kdWxlIH0gZnJvbSBcIi4vbGliL3JlY2FwdGNoYS1mb3Jtcy5tb2R1bGVcIjtcbmV4cG9ydCB7IFJlY2FwdGNoYVZhbHVlQWNjZXNzb3JEaXJlY3RpdmUgfSBmcm9tIFwiLi9saWIvcmVjYXB0Y2hhLXZhbHVlLWFjY2Vzc29yLmRpcmVjdGl2ZVwiO1xuXG5leHBvcnQge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb25cbiAgUkVDQVBUQ0hBX0xBTkdVQUdFLFxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb25cbiAgUkVDQVBUQ0hBX0JBU0VfVVJMLFxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgZGVwcmVjYXRpb24vZGVwcmVjYXRpb25cbiAgUkVDQVBUQ0hBX05PTkNFLFxuICBSRUNBUFRDSEFfU0VUVElOR1MsXG4gIFJFQ0FQVENIQV9WM19TSVRFX0tFWSxcbiAgUkVDQVBUQ0hBX0xPQURFUl9PUFRJT05TLFxuICBSZWNhcHRjaGFMb2FkZXJPcHRpb25zLFxufSBmcm9tIFwiLi9saWIvdG9rZW5zXCI7XG4iXX0=
|