@gvrs/nestjs-hcaptcha 0.1.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 +10 -0
- package/LICENSE +21 -0
- package/README.md +133 -0
- package/index.cjs.d.ts +1 -0
- package/index.cjs.default.js +1 -0
- package/index.cjs.js +28 -0
- package/index.cjs.mjs +2 -0
- package/index.esm.js +6 -0
- package/lib/get-captcha-data/get-captcha-data.cjs.js +18 -0
- package/lib/get-captcha-data/get-captcha-data.esm.js +14 -0
- package/lib/hcaptcha.exception.cjs.js +15 -0
- package/lib/hcaptcha.exception.esm.js +11 -0
- package/lib/hcaptcha.guard.cjs.js +33 -0
- package/lib/hcaptcha.guard.esm.js +29 -0
- package/lib/hcaptcha.module.cjs.js +39 -0
- package/lib/hcaptcha.module.esm.js +35 -0
- package/lib/hcaptcha.service.cjs.js +40 -0
- package/lib/hcaptcha.service.esm.js +36 -0
- package/lib/options/hcaptcha-options.module.cjs.js +40 -0
- package/lib/options/hcaptcha-options.module.esm.js +35 -0
- package/lib/verify-captcha.decorator.cjs.js +10 -0
- package/lib/verify-captcha.decorator.esm.js +6 -0
- package/package.json +76 -0
- package/src/index.d.ts +1 -0
- package/src/lib/get-captcha-data/get-captcha-data.d.ts +7 -0
- package/src/lib/get-captcha-data/index.d.ts +1 -0
- package/src/lib/hcaptcha.exception.d.ts +4 -0
- package/src/lib/hcaptcha.guard.d.ts +9 -0
- package/src/lib/hcaptcha.module.d.ts +6 -0
- package/src/lib/hcaptcha.service.d.ts +6 -0
- package/src/lib/index.d.ts +8 -0
- package/src/lib/options/hcaptcha-options.module.d.ts +7 -0
- package/src/lib/options/hcaptcha-options.types.d.ts +7 -0
- package/src/lib/options/index.d.ts +2 -0
- package/src/lib/typings.d.ts +2 -0
- package/src/lib/verify-captcha.decorator.d.ts +1 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
|
+
|
|
5
|
+
## 0.1.0 (2024-01-28)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* initial commit ([9008bc3](https://github.com/alexgavrusev/nestjs-hcaptcha/commit/9008bc3386fc8dad738af6c5a96ed28424812f63))
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Aliaksandr Haurusiou
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# @gvrs/nestjs-hcaptcha
|
|
2
|
+
|
|
3
|
+
A NestJS module for adding hCaptcha validation
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i @gvrs/nestjs-hcaptcha
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
First, provide the options in the root `AppModule`:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
@Module({
|
|
17
|
+
imports: [
|
|
18
|
+
HcaptchaModule.forRoot({
|
|
19
|
+
secret: 'YOUR_HCAPTCHA_SECRET',
|
|
20
|
+
}),
|
|
21
|
+
],
|
|
22
|
+
})
|
|
23
|
+
export class AppModule {}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Afterward, import the `HcaptchaModule` in the module where you need to use captcha validation:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
@Module({
|
|
30
|
+
imports: [HcaptchaModule],
|
|
31
|
+
controllers: [UsersController],
|
|
32
|
+
})
|
|
33
|
+
export class UsersModule {}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Finally, decorate the controller method with `@VerifyCaptcha()`:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
@Controller('users')
|
|
40
|
+
class UsersController {
|
|
41
|
+
@Post('register')
|
|
42
|
+
@VerifyCaptcha()
|
|
43
|
+
register() {}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
By default, the hCaptcha token will be extracted from the `h-captcha-response` request body field
|
|
48
|
+
|
|
49
|
+
### Setting the sitekey you expect to see
|
|
50
|
+
|
|
51
|
+
Provide the `sitekey` option in the root `HcaptchaModule`:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
@Module({
|
|
55
|
+
imports: [
|
|
56
|
+
HcaptchaModule.forRoot({
|
|
57
|
+
secret: 'YOUR_HCAPTCHA_SECRET',
|
|
58
|
+
sitekey: 'YOUR_SITEKEY',
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
})
|
|
62
|
+
export class AppModule {}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Customizing the captcha data extraction
|
|
66
|
+
|
|
67
|
+
If you want to customize the retrieval of the hCaptcha token and/or the user's IP address, provide an implementation of `getCaptchaData` in the root `HcaptchaModule`:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
@Module({
|
|
71
|
+
imports: [
|
|
72
|
+
HcaptchaModule.forRoot({
|
|
73
|
+
secret: 'YOUR_HCAPTCHA_SECRET',
|
|
74
|
+
getCaptchaData: (ctx) => {
|
|
75
|
+
const request = ctx.switchToHttp().getRequest();
|
|
76
|
+
|
|
77
|
+
const token = request.body['token'];
|
|
78
|
+
const remoteip = request.headers['x-forwarded-for'];
|
|
79
|
+
|
|
80
|
+
return { token, remoteip };
|
|
81
|
+
},
|
|
82
|
+
}),
|
|
83
|
+
],
|
|
84
|
+
})
|
|
85
|
+
export class AppModule {}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Customizing the error response
|
|
89
|
+
|
|
90
|
+
By default, when the captcha is invalid, or cannot be validated, a 403 error will be sent to the client. To customize that response, use an [exception filter](https://docs.nestjs.com/exception-filters):
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
@Catch(HcaptchaException)
|
|
94
|
+
class HcaptchaExceptionFilter implements ExceptionFilter {
|
|
95
|
+
catch(exception: HcaptchaException, host: ArgumentsHost) {
|
|
96
|
+
const ctx = host.switchToHttp();
|
|
97
|
+
const response = ctx.getResponse();
|
|
98
|
+
const status = 400;
|
|
99
|
+
|
|
100
|
+
response.status(status).json({
|
|
101
|
+
statusCode: status,
|
|
102
|
+
message: 'Invalid captcha',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Imperative captcha verification
|
|
109
|
+
|
|
110
|
+
If you don't want to, or cannot use the `@VerifyCaptha()` decorator or the `HcaptchaGuard`, you can verify the captcha by using the `HcaptchaService`:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
@Controller('users')
|
|
114
|
+
class UsersController {
|
|
115
|
+
constructor(private readonly hcaptchaService: HcaptchaService) {}
|
|
116
|
+
|
|
117
|
+
@Post('register')
|
|
118
|
+
async register(@Req() request: Request) {
|
|
119
|
+
try {
|
|
120
|
+
const token = request.body['h-captcha-response'];
|
|
121
|
+
|
|
122
|
+
// returns the hCaptcha JSON response, or throws a HcaptchaException
|
|
123
|
+
const verifyResponse = await this.hcaptchaService.verifyCaptcha(token);
|
|
124
|
+
} catch {
|
|
125
|
+
throw new BadRequestException();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT © Aliaksandr Haurusiou.
|
package/index.cjs.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./src/index";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exports._default = require('./index.cjs.js').default;
|
package/index.cjs.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var getCaptchaData = require('./lib/get-captcha-data/get-captcha-data.cjs.js');
|
|
6
|
+
var hcaptcha_exception = require('./lib/hcaptcha.exception.cjs.js');
|
|
7
|
+
var hcaptcha_guard = require('./lib/hcaptcha.guard.cjs.js');
|
|
8
|
+
var hcaptcha_module = require('./lib/hcaptcha.module.cjs.js');
|
|
9
|
+
var hcaptcha_service = require('./lib/hcaptcha.service.cjs.js');
|
|
10
|
+
var verifyCaptcha_decorator = require('./lib/verify-captcha.decorator.cjs.js');
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
exports.defaultGetCaptchaData = getCaptchaData.defaultGetCaptchaData;
|
|
15
|
+
exports.HcaptchaException = hcaptcha_exception.HcaptchaException;
|
|
16
|
+
Object.defineProperty(exports, 'HcaptchaGuard', {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return hcaptcha_guard.HcaptchaGuard; }
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(exports, 'HcaptchaModule', {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: function () { return hcaptcha_module.HcaptchaModule; }
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, 'HcaptchaService', {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () { return hcaptcha_service.HcaptchaService; }
|
|
27
|
+
});
|
|
28
|
+
exports.VerifyCaptcha = verifyCaptcha_decorator.VerifyCaptcha;
|
package/index.cjs.mjs
ADDED
package/index.esm.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { defaultGetCaptchaData } from './lib/get-captcha-data/get-captcha-data.esm.js';
|
|
2
|
+
export { HcaptchaException } from './lib/hcaptcha.exception.esm.js';
|
|
3
|
+
export { HcaptchaGuard } from './lib/hcaptcha.guard.esm.js';
|
|
4
|
+
export { HcaptchaModule } from './lib/hcaptcha.module.esm.js';
|
|
5
|
+
export { HcaptchaService } from './lib/hcaptcha.service.esm.js';
|
|
6
|
+
export { VerifyCaptcha } from './lib/verify-captcha.decorator.esm.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var hcaptcha_exception = require('../hcaptcha.exception.cjs.js');
|
|
6
|
+
|
|
7
|
+
const defaultGetCaptchaData = (context)=>{
|
|
8
|
+
const request = context.switchToHttp().getRequest();
|
|
9
|
+
const token = request.body['h-captcha-response'];
|
|
10
|
+
if (!token) {
|
|
11
|
+
throw new hcaptcha_exception.HcaptchaException(new Error('No hCaptcha token present in request body'));
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
token
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
exports.defaultGetCaptchaData = defaultGetCaptchaData;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HcaptchaException } from '../hcaptcha.exception.esm.js';
|
|
2
|
+
|
|
3
|
+
const defaultGetCaptchaData = (context)=>{
|
|
4
|
+
const request = context.switchToHttp().getRequest();
|
|
5
|
+
const token = request.body['h-captcha-response'];
|
|
6
|
+
if (!token) {
|
|
7
|
+
throw new HcaptchaException(new Error('No hCaptcha token present in request body'));
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
token
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export { defaultGetCaptchaData };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var common = require('@nestjs/common');
|
|
6
|
+
|
|
7
|
+
class HcaptchaException extends common.ForbiddenException {
|
|
8
|
+
constructor(cause, message = 'Forbidden'){
|
|
9
|
+
super(message, {
|
|
10
|
+
cause
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
exports.HcaptchaException = HcaptchaException;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _ts_decorate = require('@swc/helpers/_/_ts_decorate');
|
|
6
|
+
var _ts_metadata = require('@swc/helpers/_/_ts_metadata');
|
|
7
|
+
var _ts_param = require('@swc/helpers/_/_ts_param');
|
|
8
|
+
var common = require('@nestjs/common');
|
|
9
|
+
var hcaptchaOptions_module = require('./options/hcaptcha-options.module.cjs.js');
|
|
10
|
+
var hcaptcha_service = require('./hcaptcha.service.cjs.js');
|
|
11
|
+
|
|
12
|
+
class HcaptchaGuard {
|
|
13
|
+
async canActivate(context) {
|
|
14
|
+
const { token, remoteip } = this.options.getCaptchaData(context);
|
|
15
|
+
await this.hcaptchaService.verifyCaptcha(token, remoteip);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
constructor(options, hcaptchaService){
|
|
19
|
+
this.options = options;
|
|
20
|
+
this.hcaptchaService = hcaptchaService;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
HcaptchaGuard = _ts_decorate._([
|
|
24
|
+
common.Injectable(),
|
|
25
|
+
_ts_param._(0, common.Inject(hcaptchaOptions_module.NORMALIZED_HCAPTCHA_OPTIONS)),
|
|
26
|
+
_ts_metadata._("design:type", Function),
|
|
27
|
+
_ts_metadata._("design:paramtypes", [
|
|
28
|
+
typeof NormalizedHcaptchaOptions === "undefined" ? Object : NormalizedHcaptchaOptions,
|
|
29
|
+
typeof hcaptcha_service.HcaptchaService === "undefined" ? Object : hcaptcha_service.HcaptchaService
|
|
30
|
+
])
|
|
31
|
+
], HcaptchaGuard);
|
|
32
|
+
|
|
33
|
+
exports.HcaptchaGuard = HcaptchaGuard;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { _ } from '@swc/helpers/_/_ts_decorate';
|
|
2
|
+
import { _ as _$2 } from '@swc/helpers/_/_ts_metadata';
|
|
3
|
+
import { _ as _$1 } from '@swc/helpers/_/_ts_param';
|
|
4
|
+
import { Injectable, Inject } from '@nestjs/common';
|
|
5
|
+
import { HcaptchaService } from './hcaptcha.service.esm.js';
|
|
6
|
+
import { NORMALIZED_HCAPTCHA_OPTIONS } from './options/hcaptcha-options.module.esm.js';
|
|
7
|
+
|
|
8
|
+
class HcaptchaGuard {
|
|
9
|
+
async canActivate(context) {
|
|
10
|
+
const { token, remoteip } = this.options.getCaptchaData(context);
|
|
11
|
+
await this.hcaptchaService.verifyCaptcha(token, remoteip);
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
constructor(options, hcaptchaService){
|
|
15
|
+
this.options = options;
|
|
16
|
+
this.hcaptchaService = hcaptchaService;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
HcaptchaGuard = _([
|
|
20
|
+
Injectable(),
|
|
21
|
+
_$1(0, Inject(NORMALIZED_HCAPTCHA_OPTIONS)),
|
|
22
|
+
_$2("design:type", Function),
|
|
23
|
+
_$2("design:paramtypes", [
|
|
24
|
+
typeof NormalizedHcaptchaOptions === "undefined" ? Object : NormalizedHcaptchaOptions,
|
|
25
|
+
typeof HcaptchaService === "undefined" ? Object : HcaptchaService
|
|
26
|
+
])
|
|
27
|
+
], HcaptchaGuard);
|
|
28
|
+
|
|
29
|
+
export { HcaptchaGuard };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _ts_decorate = require('@swc/helpers/_/_ts_decorate');
|
|
6
|
+
var common = require('@nestjs/common');
|
|
7
|
+
var hcaptchaOptions_module = require('./options/hcaptcha-options.module.cjs.js');
|
|
8
|
+
var hcaptcha_service = require('./hcaptcha.service.cjs.js');
|
|
9
|
+
|
|
10
|
+
class HcaptchaModule {
|
|
11
|
+
static forRoot(options) {
|
|
12
|
+
return {
|
|
13
|
+
module: this,
|
|
14
|
+
imports: [
|
|
15
|
+
hcaptchaOptions_module.HcaptchaOptionsModule.forRoot(options)
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
static forRootAsync(options) {
|
|
20
|
+
return {
|
|
21
|
+
module: this,
|
|
22
|
+
imports: [
|
|
23
|
+
hcaptchaOptions_module.HcaptchaOptionsModule.forRootAsync(options)
|
|
24
|
+
]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
HcaptchaModule = _ts_decorate._([
|
|
29
|
+
common.Module({
|
|
30
|
+
providers: [
|
|
31
|
+
hcaptcha_service.HcaptchaService
|
|
32
|
+
],
|
|
33
|
+
exports: [
|
|
34
|
+
hcaptcha_service.HcaptchaService
|
|
35
|
+
]
|
|
36
|
+
})
|
|
37
|
+
], HcaptchaModule);
|
|
38
|
+
|
|
39
|
+
exports.HcaptchaModule = HcaptchaModule;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { _ } from '@swc/helpers/_/_ts_decorate';
|
|
2
|
+
import { Module } from '@nestjs/common';
|
|
3
|
+
import { HcaptchaService } from './hcaptcha.service.esm.js';
|
|
4
|
+
import { HcaptchaOptionsModule } from './options/hcaptcha-options.module.esm.js';
|
|
5
|
+
|
|
6
|
+
class HcaptchaModule {
|
|
7
|
+
static forRoot(options) {
|
|
8
|
+
return {
|
|
9
|
+
module: this,
|
|
10
|
+
imports: [
|
|
11
|
+
HcaptchaOptionsModule.forRoot(options)
|
|
12
|
+
]
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
static forRootAsync(options) {
|
|
16
|
+
return {
|
|
17
|
+
module: this,
|
|
18
|
+
imports: [
|
|
19
|
+
HcaptchaOptionsModule.forRootAsync(options)
|
|
20
|
+
]
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
HcaptchaModule = _([
|
|
25
|
+
Module({
|
|
26
|
+
providers: [
|
|
27
|
+
HcaptchaService
|
|
28
|
+
],
|
|
29
|
+
exports: [
|
|
30
|
+
HcaptchaService
|
|
31
|
+
]
|
|
32
|
+
})
|
|
33
|
+
], HcaptchaModule);
|
|
34
|
+
|
|
35
|
+
export { HcaptchaModule };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _ts_decorate = require('@swc/helpers/_/_ts_decorate');
|
|
6
|
+
var _ts_metadata = require('@swc/helpers/_/_ts_metadata');
|
|
7
|
+
var _ts_param = require('@swc/helpers/_/_ts_param');
|
|
8
|
+
var common = require('@nestjs/common');
|
|
9
|
+
var hcaptcha = require('hcaptcha');
|
|
10
|
+
var hcaptchaOptions_module = require('./options/hcaptcha-options.module.cjs.js');
|
|
11
|
+
var hcaptcha_exception = require('./hcaptcha.exception.cjs.js');
|
|
12
|
+
|
|
13
|
+
class HcaptchaService {
|
|
14
|
+
async verifyCaptcha(token, remoteip) {
|
|
15
|
+
let verifyResponse;
|
|
16
|
+
try {
|
|
17
|
+
verifyResponse = await hcaptcha.verify(this.options.secret, token, remoteip, this.options.sitekey);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
throw new hcaptcha_exception.HcaptchaException(e);
|
|
20
|
+
}
|
|
21
|
+
const { success } = verifyResponse;
|
|
22
|
+
if (!success) {
|
|
23
|
+
throw new hcaptcha_exception.HcaptchaException(verifyResponse);
|
|
24
|
+
}
|
|
25
|
+
return verifyResponse;
|
|
26
|
+
}
|
|
27
|
+
constructor(options){
|
|
28
|
+
this.options = options;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
HcaptchaService = _ts_decorate._([
|
|
32
|
+
common.Injectable(),
|
|
33
|
+
_ts_param._(0, common.Inject(hcaptchaOptions_module.NORMALIZED_HCAPTCHA_OPTIONS)),
|
|
34
|
+
_ts_metadata._("design:type", Function),
|
|
35
|
+
_ts_metadata._("design:paramtypes", [
|
|
36
|
+
typeof NormalizedHcaptchaOptions === "undefined" ? Object : NormalizedHcaptchaOptions
|
|
37
|
+
])
|
|
38
|
+
], HcaptchaService);
|
|
39
|
+
|
|
40
|
+
exports.HcaptchaService = HcaptchaService;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { _ } from '@swc/helpers/_/_ts_decorate';
|
|
2
|
+
import { _ as _$2 } from '@swc/helpers/_/_ts_metadata';
|
|
3
|
+
import { _ as _$1 } from '@swc/helpers/_/_ts_param';
|
|
4
|
+
import { Injectable, Inject } from '@nestjs/common';
|
|
5
|
+
import { verify } from 'hcaptcha';
|
|
6
|
+
import { HcaptchaException } from './hcaptcha.exception.esm.js';
|
|
7
|
+
import { NORMALIZED_HCAPTCHA_OPTIONS } from './options/hcaptcha-options.module.esm.js';
|
|
8
|
+
|
|
9
|
+
class HcaptchaService {
|
|
10
|
+
async verifyCaptcha(token, remoteip) {
|
|
11
|
+
let verifyResponse;
|
|
12
|
+
try {
|
|
13
|
+
verifyResponse = await verify(this.options.secret, token, remoteip, this.options.sitekey);
|
|
14
|
+
} catch (e) {
|
|
15
|
+
throw new HcaptchaException(e);
|
|
16
|
+
}
|
|
17
|
+
const { success } = verifyResponse;
|
|
18
|
+
if (!success) {
|
|
19
|
+
throw new HcaptchaException(verifyResponse);
|
|
20
|
+
}
|
|
21
|
+
return verifyResponse;
|
|
22
|
+
}
|
|
23
|
+
constructor(options){
|
|
24
|
+
this.options = options;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
HcaptchaService = _([
|
|
28
|
+
Injectable(),
|
|
29
|
+
_$1(0, Inject(NORMALIZED_HCAPTCHA_OPTIONS)),
|
|
30
|
+
_$2("design:type", Function),
|
|
31
|
+
_$2("design:paramtypes", [
|
|
32
|
+
typeof NormalizedHcaptchaOptions === "undefined" ? Object : NormalizedHcaptchaOptions
|
|
33
|
+
])
|
|
34
|
+
], HcaptchaService);
|
|
35
|
+
|
|
36
|
+
export { HcaptchaService };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _extends = require('@swc/helpers/_/_extends');
|
|
6
|
+
var _ts_decorate = require('@swc/helpers/_/_ts_decorate');
|
|
7
|
+
var common = require('@nestjs/common');
|
|
8
|
+
var getCaptchaData = require('../get-captcha-data/get-captcha-data.cjs.js');
|
|
9
|
+
|
|
10
|
+
const PROVIDED_HCAPTCHA_OPTIONS = Symbol('PROVIDED_HCAPTCHA_OPTIONS');
|
|
11
|
+
const NORMALIZED_HCAPTCHA_OPTIONS = Symbol('NORMALIZED_HCAPTCHA_OPTIONS');
|
|
12
|
+
const normalizeOptions = (options)=>_extends._({
|
|
13
|
+
getCaptchaData: getCaptchaData.defaultGetCaptchaData
|
|
14
|
+
}, options);
|
|
15
|
+
const { ASYNC_OPTIONS_TYPE: ASYNC_HCAPTCHA_OPTIONS_TYPE, ConfigurableModuleClass } = new common.ConfigurableModuleBuilder({
|
|
16
|
+
optionsInjectionToken: PROVIDED_HCAPTCHA_OPTIONS
|
|
17
|
+
}).setClassMethodName('forRoot').setExtras({}, (def)=>_extends._({}, def, {
|
|
18
|
+
global: true,
|
|
19
|
+
providers: [
|
|
20
|
+
...def.providers,
|
|
21
|
+
{
|
|
22
|
+
provide: NORMALIZED_HCAPTCHA_OPTIONS,
|
|
23
|
+
useFactory: normalizeOptions,
|
|
24
|
+
inject: [
|
|
25
|
+
PROVIDED_HCAPTCHA_OPTIONS
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
exports: [
|
|
30
|
+
NORMALIZED_HCAPTCHA_OPTIONS
|
|
31
|
+
]
|
|
32
|
+
})).build();
|
|
33
|
+
class HcaptchaOptionsModule extends ConfigurableModuleClass {
|
|
34
|
+
}
|
|
35
|
+
HcaptchaOptionsModule = _ts_decorate._([
|
|
36
|
+
common.Module({})
|
|
37
|
+
], HcaptchaOptionsModule);
|
|
38
|
+
|
|
39
|
+
exports.HcaptchaOptionsModule = HcaptchaOptionsModule;
|
|
40
|
+
exports.NORMALIZED_HCAPTCHA_OPTIONS = NORMALIZED_HCAPTCHA_OPTIONS;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { _ } from '@swc/helpers/_/_extends';
|
|
2
|
+
import { _ as _$1 } from '@swc/helpers/_/_ts_decorate';
|
|
3
|
+
import { ConfigurableModuleBuilder, Module } from '@nestjs/common';
|
|
4
|
+
import { defaultGetCaptchaData } from '../get-captcha-data/get-captcha-data.esm.js';
|
|
5
|
+
|
|
6
|
+
const PROVIDED_HCAPTCHA_OPTIONS = Symbol('PROVIDED_HCAPTCHA_OPTIONS');
|
|
7
|
+
const NORMALIZED_HCAPTCHA_OPTIONS = Symbol('NORMALIZED_HCAPTCHA_OPTIONS');
|
|
8
|
+
const normalizeOptions = (options)=>_({
|
|
9
|
+
getCaptchaData: defaultGetCaptchaData
|
|
10
|
+
}, options);
|
|
11
|
+
const { ASYNC_OPTIONS_TYPE: ASYNC_HCAPTCHA_OPTIONS_TYPE, ConfigurableModuleClass } = new ConfigurableModuleBuilder({
|
|
12
|
+
optionsInjectionToken: PROVIDED_HCAPTCHA_OPTIONS
|
|
13
|
+
}).setClassMethodName('forRoot').setExtras({}, (def)=>_({}, def, {
|
|
14
|
+
global: true,
|
|
15
|
+
providers: [
|
|
16
|
+
...def.providers,
|
|
17
|
+
{
|
|
18
|
+
provide: NORMALIZED_HCAPTCHA_OPTIONS,
|
|
19
|
+
useFactory: normalizeOptions,
|
|
20
|
+
inject: [
|
|
21
|
+
PROVIDED_HCAPTCHA_OPTIONS
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
exports: [
|
|
26
|
+
NORMALIZED_HCAPTCHA_OPTIONS
|
|
27
|
+
]
|
|
28
|
+
})).build();
|
|
29
|
+
class HcaptchaOptionsModule extends ConfigurableModuleClass {
|
|
30
|
+
}
|
|
31
|
+
HcaptchaOptionsModule = _$1([
|
|
32
|
+
Module({})
|
|
33
|
+
], HcaptchaOptionsModule);
|
|
34
|
+
|
|
35
|
+
export { HcaptchaOptionsModule, NORMALIZED_HCAPTCHA_OPTIONS };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var common = require('@nestjs/common');
|
|
6
|
+
var hcaptcha_guard = require('./hcaptcha.guard.cjs.js');
|
|
7
|
+
|
|
8
|
+
const VerifyCaptcha = ()=>common.applyDecorators(common.UseGuards(hcaptcha_guard.HcaptchaGuard));
|
|
9
|
+
|
|
10
|
+
exports.VerifyCaptcha = VerifyCaptcha;
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gvrs/nestjs-hcaptcha",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "Alex Gavrusev <alex@gavrusev.dev>",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/alexgavrusev/nestjs-hcaptcha.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/alexgavrusev/nestjs-hcaptcha#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/alexgavrusev/nestjs-hcaptcha/issues"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"prepare": "node -e \"if(require('fs').existsSync('.git')){/* proceed only if .git is found */ process.exit(1)}\" || is-ci || husky install"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@swc/helpers": "~0.5.2"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@commitlint/cli": "^18.0.0",
|
|
22
|
+
"@commitlint/config-conventional": "^18.0.0",
|
|
23
|
+
"@gvrs-nx/ts-package": "0.3.0",
|
|
24
|
+
"@jscutlery/semver": "^4.1.0",
|
|
25
|
+
"@nestjs/common": "10.3.0",
|
|
26
|
+
"@nestjs/platform-express": "10.3.0",
|
|
27
|
+
"@nestjs/testing": "10.3.0",
|
|
28
|
+
"@nx/eslint": "17.2.8",
|
|
29
|
+
"@nx/eslint-plugin": "17.2.8",
|
|
30
|
+
"@nx/js": "17.2.8",
|
|
31
|
+
"@nx/rollup": "17.2.8",
|
|
32
|
+
"@nx/vite": "17.2.8",
|
|
33
|
+
"@nx/workspace": "17.2.8",
|
|
34
|
+
"@swc-node/register": "~1.6.7",
|
|
35
|
+
"@swc/cli": "~0.1.62",
|
|
36
|
+
"@swc/core": "~1.3.85",
|
|
37
|
+
"@types/express": "4.17.21",
|
|
38
|
+
"@types/node": "18.7.1",
|
|
39
|
+
"@types/supertest": "6.0.2",
|
|
40
|
+
"@typescript-eslint/eslint-plugin": "^6.9.1",
|
|
41
|
+
"@typescript-eslint/parser": "^6.9.1",
|
|
42
|
+
"@vitest/coverage-v8": "~0.34.6",
|
|
43
|
+
"@vitest/ui": "~0.34.6",
|
|
44
|
+
"eslint": "~8.48.0",
|
|
45
|
+
"eslint-config-prettier": "^9.0.0",
|
|
46
|
+
"hcaptcha": "0.1.1",
|
|
47
|
+
"husky": "^8.0.0",
|
|
48
|
+
"is-ci": "^3.0.0",
|
|
49
|
+
"nock": "13.5.0",
|
|
50
|
+
"nx": "17.2.8",
|
|
51
|
+
"prettier": "^2.6.2",
|
|
52
|
+
"supertest": "6.3.4",
|
|
53
|
+
"tslib": "^2.3.0",
|
|
54
|
+
"typescript": "~5.2.2",
|
|
55
|
+
"unplugin-swc": "1.4.4",
|
|
56
|
+
"verdaccio": "^5.0.4",
|
|
57
|
+
"vite": "^5.0.0",
|
|
58
|
+
"vitest": "1.2.1",
|
|
59
|
+
"vitest-mock-extended": "1.3.1"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"@nestjs/common": "^10.0.0",
|
|
63
|
+
"hcaptcha": "~0.1.1"
|
|
64
|
+
},
|
|
65
|
+
"sideEffects": false,
|
|
66
|
+
"exports": {
|
|
67
|
+
"./package.json": "./package.json",
|
|
68
|
+
".": {
|
|
69
|
+
"module": "./index.esm.js",
|
|
70
|
+
"import": "./index.cjs.mjs",
|
|
71
|
+
"default": "./index.cjs.js"
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"module": "./index.esm.js",
|
|
75
|
+
"main": "./index.cjs.js"
|
|
76
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './lib';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ExecutionContext } from '@nestjs/common';
|
|
2
|
+
export type CaptchaData = {
|
|
3
|
+
token: string;
|
|
4
|
+
remoteip?: string;
|
|
5
|
+
};
|
|
6
|
+
export type GetCaptchaData = (executionContext: ExecutionContext) => CaptchaData;
|
|
7
|
+
export declare const defaultGetCaptchaData: GetCaptchaData;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './get-captcha-data';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import type { NormalizedHcaptchaOptions } from './options';
|
|
3
|
+
import { HcaptchaService } from './hcaptcha.service';
|
|
4
|
+
export declare class HcaptchaGuard implements CanActivate {
|
|
5
|
+
private readonly options;
|
|
6
|
+
private readonly hcaptchaService;
|
|
7
|
+
constructor(options: NormalizedHcaptchaOptions, hcaptchaService: HcaptchaService);
|
|
8
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DynamicModule } from '@nestjs/common';
|
|
2
|
+
import { HcaptchaOptions, AsyncHcaptchaOptions } from './options';
|
|
3
|
+
export declare class HcaptchaModule {
|
|
4
|
+
static forRoot(options: HcaptchaOptions): DynamicModule;
|
|
5
|
+
static forRootAsync(options: AsyncHcaptchaOptions): DynamicModule;
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { NormalizedHcaptchaOptions } from './options';
|
|
2
|
+
export declare class HcaptchaService {
|
|
3
|
+
private readonly options;
|
|
4
|
+
constructor(options: NormalizedHcaptchaOptions);
|
|
5
|
+
verifyCaptcha(token: string, remoteip?: string): Promise<globalThis.VerifyResponse>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './get-captcha-data';
|
|
2
|
+
export type { HcaptchaOptions, AsyncHcaptchaOptions } from './options';
|
|
3
|
+
export * from './hcaptcha.exception';
|
|
4
|
+
export * from './hcaptcha.guard';
|
|
5
|
+
export * from './hcaptcha.module';
|
|
6
|
+
export * from './hcaptcha.service';
|
|
7
|
+
export * from './verify-captcha.decorator';
|
|
8
|
+
export * from './typings';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { HcaptchaOptions } from './hcaptcha-options.types';
|
|
2
|
+
declare const NORMALIZED_HCAPTCHA_OPTIONS: unique symbol;
|
|
3
|
+
declare const ASYNC_HCAPTCHA_OPTIONS_TYPE: import("@nestjs/common").ConfigurableModuleAsyncOptions<HcaptchaOptions, "create"> & Partial<{}>, ConfigurableModuleClass: import("@nestjs/common").ConfigurableModuleCls<HcaptchaOptions, "forRoot", "create", {}>;
|
|
4
|
+
export declare class HcaptchaOptionsModule extends ConfigurableModuleClass {
|
|
5
|
+
}
|
|
6
|
+
export type AsyncHcaptchaOptions = typeof ASYNC_HCAPTCHA_OPTIONS_TYPE;
|
|
7
|
+
export { NORMALIZED_HCAPTCHA_OPTIONS };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { GetCaptchaData } from '../get-captcha-data';
|
|
2
|
+
export type HcaptchaOptions = {
|
|
3
|
+
secret: string;
|
|
4
|
+
sitekey?: string;
|
|
5
|
+
getCaptchaData?: GetCaptchaData;
|
|
6
|
+
};
|
|
7
|
+
export type NormalizedHcaptchaOptions = Required<Pick<HcaptchaOptions, 'getCaptchaData'>> & HcaptchaOptions;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VerifyCaptcha: () => <TFunction extends Function, Y>(target: object | TFunction, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|