altcha-lib 2.1.0 → 2.3.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/README.md +28 -0
- package/dist/cjs/v2/frameworks/express.js +7 -4
- package/dist/cjs/v2/frameworks/fastify.js +7 -4
- package/dist/cjs/v2/frameworks/h3.d.ts +1 -1
- package/dist/cjs/v2/frameworks/h3.js +11 -4
- package/dist/cjs/v2/frameworks/hono.d.ts +18 -0
- package/dist/cjs/v2/frameworks/hono.js +9 -3
- package/dist/cjs/v2/frameworks/nestjs.d.ts +5 -4
- package/dist/cjs/v2/frameworks/nestjs.js +9 -3
- package/dist/cjs/v2/frameworks/nextjs.js +9 -4
- package/dist/cjs/v2/frameworks/shared.d.ts +4 -4
- package/dist/cjs/v2/frameworks/shared.js +22 -3
- package/dist/cjs/v2/frameworks/sveltekit.js +8 -5
- package/dist/cjs/v2/frameworks/types.d.ts +17 -5
- package/dist/cjs/v2/helpers.d.ts +0 -2
- package/dist/cjs/v2/helpers.js +0 -12
- package/dist/cjs/v2/index.d.ts +3 -1
- package/dist/cjs/v2/index.js +4 -1
- package/dist/cjs/v2/types.d.ts +54 -0
- package/dist/cjs/v2/verify-server.d.ts +3 -0
- package/dist/cjs/v2/verify-server.js +57 -0
- package/dist/esm/v2/frameworks/express.js +7 -4
- package/dist/esm/v2/frameworks/fastify.js +7 -4
- package/dist/esm/v2/frameworks/h3.d.ts +1 -1
- package/dist/esm/v2/frameworks/h3.js +12 -5
- package/dist/esm/v2/frameworks/hono.d.ts +18 -0
- package/dist/esm/v2/frameworks/hono.js +9 -3
- package/dist/esm/v2/frameworks/nestjs.d.ts +5 -4
- package/dist/esm/v2/frameworks/nestjs.js +11 -4
- package/dist/esm/v2/frameworks/nextjs.js +9 -4
- package/dist/esm/v2/frameworks/shared.d.ts +4 -4
- package/dist/esm/v2/frameworks/shared.js +22 -3
- package/dist/esm/v2/frameworks/sveltekit.js +8 -5
- package/dist/esm/v2/frameworks/types.d.ts +17 -5
- package/dist/esm/v2/helpers.d.ts +0 -2
- package/dist/esm/v2/helpers.js +0 -11
- package/dist/esm/v2/index.d.ts +3 -1
- package/dist/esm/v2/index.js +3 -1
- package/dist/esm/v2/types.d.ts +54 -0
- package/dist/esm/v2/verify-server.d.ts +3 -0
- package/dist/esm/v2/verify-server.js +54 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -55,6 +55,8 @@ The library includes plugins for several popular frameworks to simplify integrat
|
|
|
55
55
|
|
|
56
56
|
If your framework is not listed, see the [Advanced Usage](/docs/advanced-usage.md) guide for custom integrations.
|
|
57
57
|
|
|
58
|
+
All plugins also support remote verification via ALTCHA Sentinel (`verifyServer` option) — see [`/docs/server-signatures.md`](/docs/server-signatures.md#remote-verification-sentinel-api).
|
|
59
|
+
|
|
58
60
|
## Documentation
|
|
59
61
|
|
|
60
62
|
- Advanced Usage: [`/docs/advanced-usage.md`](/docs/advanced-usage.md)
|
|
@@ -217,6 +219,32 @@ Returns `VerifyServerSignatureResult` (extends `VerifySolutionResult`):
|
|
|
217
219
|
| `verified` | `boolean` | Whether the signature is valid. |
|
|
218
220
|
| `verificationData` | `ServerSignatureVerificationData \| null` | Parsed verification data. |
|
|
219
221
|
|
|
222
|
+
#### `verifyServer(options: VerifyServerOptions): Promise<VerifyServerResult>`
|
|
223
|
+
|
|
224
|
+
Verifies a payload remotely by calling ALTCHA Sentinel's `POST /v1/verify/signature` API, instead of verifying the HMAC signature locally.
|
|
225
|
+
|
|
226
|
+
| Option | Type | Description |
|
|
227
|
+
|---|---|---|
|
|
228
|
+
| `payload` | `string \| ServerSignaturePayload \| Record<string, unknown>` | The payload to verify, as received from `POST /v1/verify`. |
|
|
229
|
+
| `url` | `string` | Full URL of the Sentinel `/v1/verify/signature` endpoint. |
|
|
230
|
+
| `secret` | `string?` | API key secret. If provided, Sentinel checks that it matches the API key associated with the payload. |
|
|
231
|
+
| `fetch` | `typeof fetch?` | Custom fetch implementation. Defaults to the global `fetch`. |
|
|
232
|
+
| `headers` | `Record<string, string>?` | Additional headers to send with the request. |
|
|
233
|
+
| `controller` | `AbortController?` | For cancelling the verification request. |
|
|
234
|
+
| `timeout` | `number?` | Per-attempt request timeout in milliseconds. Defaults to `10000`. |
|
|
235
|
+
| `retries` | `number?` | Number of retry attempts after the first try. Defaults to `0`. |
|
|
236
|
+
| `retryDelay` | `number?` | Base delay in milliseconds between retries. Defaults to `300`. |
|
|
237
|
+
| `retryBackoff` | `'fixed' \| 'exponential'?` | Backoff strategy for `retryDelay`. Defaults to `'exponential'`. |
|
|
238
|
+
|
|
239
|
+
Returns `VerifyServerResult`:
|
|
240
|
+
|
|
241
|
+
| Field | Type | Description |
|
|
242
|
+
|---|---|---|
|
|
243
|
+
| `verified` | `boolean` | Whether the payload was successfully verified. |
|
|
244
|
+
| `apiKey` | `string \| null?` | API key associated with the verification. |
|
|
245
|
+
| `reason` | `string?` | Reason or error message if verification failed. |
|
|
246
|
+
| `verificationData` | `ServerSignatureVerificationData \| null?` | Verification data returned by Sentinel. |
|
|
247
|
+
|
|
220
248
|
#### `obfuscate(str: string, options?): Promise<string>`
|
|
221
249
|
|
|
222
250
|
> Import from `altcha-lib/obfuscation`
|
|
@@ -13,15 +13,18 @@ const asyncHandler = (fn) => (req, res, next) => {
|
|
|
13
13
|
fn(req, res, next).catch(next);
|
|
14
14
|
};
|
|
15
15
|
function create(options) {
|
|
16
|
-
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, } = options;
|
|
16
|
+
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, verifyServer: verifyServerOptions, } = options;
|
|
17
17
|
const challengeHandler = asyncHandler(async (req, res) => {
|
|
18
|
+
if (!deriveKey || !createChallengeParameters) {
|
|
19
|
+
throw new Error('deriveKey and createChallengeParameters are required to generate challenges. Omit challengeHandler when relying on Sentinel to issue challenges.');
|
|
20
|
+
}
|
|
18
21
|
const challenge = await (0, pow_js_1.createChallenge)({
|
|
19
22
|
deriveKey,
|
|
20
23
|
hmacSignatureSecret,
|
|
21
24
|
hmacKeySignatureSecret,
|
|
22
25
|
...createChallengeParameters(),
|
|
23
26
|
});
|
|
24
|
-
res.json({
|
|
27
|
+
res.set('Cache-Control', 'no-store').json({
|
|
25
28
|
configuration: setCookie
|
|
26
29
|
? {
|
|
27
30
|
setCookie,
|
|
@@ -32,7 +35,7 @@ function create(options) {
|
|
|
32
35
|
});
|
|
33
36
|
const verifyHandler = asyncHandler(async (req, res) => {
|
|
34
37
|
const payload = await getPayloadFromRequest(req);
|
|
35
|
-
const result = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
38
|
+
const result = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
36
39
|
res.json(result);
|
|
37
40
|
});
|
|
38
41
|
const getPayloadFromRequest = async (req, cookieName) => {
|
|
@@ -45,7 +48,7 @@ function create(options) {
|
|
|
45
48
|
const { throwOnFailure = true } = options;
|
|
46
49
|
return asyncHandler(async (req, res, next) => {
|
|
47
50
|
const payload = await getPayloadFromRequest(req, setCookie?.name);
|
|
48
|
-
const { error, payload: resultPayload, verification, } = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
51
|
+
const { error, payload: resultPayload, verification, } = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
49
52
|
res.locals.altcha = {
|
|
50
53
|
error,
|
|
51
54
|
payload: resultPayload,
|
|
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "deriveHmacKeySecret", { enumerable: true, get: f
|
|
|
10
10
|
const capped_map_js_1 = require("../capped-map.js");
|
|
11
11
|
Object.defineProperty(exports, "CappedMap", { enumerable: true, get: function () { return capped_map_js_1.CappedMap; } });
|
|
12
12
|
function create(options) {
|
|
13
|
-
const { createChallengeParameters, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, } = options;
|
|
13
|
+
const { createChallengeParameters, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, verifyServer: verifyServerOptions, } = options;
|
|
14
14
|
const deleteCookie = (reply, name, path = '/') => {
|
|
15
15
|
reply.header('Set-Cookie', `${name}=; Path=${path ?? '/'}; Max-Age=0`);
|
|
16
16
|
};
|
|
@@ -34,13 +34,16 @@ function create(options) {
|
|
|
34
34
|
return request.body?.altcha;
|
|
35
35
|
};
|
|
36
36
|
const challengeHandler = async (request, reply) => {
|
|
37
|
+
if (!deriveKey || !createChallengeParameters) {
|
|
38
|
+
throw new Error('deriveKey and createChallengeParameters are required to generate challenges. Omit challengeHandler when relying on Sentinel to issue challenges.');
|
|
39
|
+
}
|
|
37
40
|
const challenge = await (0, pow_js_1.createChallenge)({
|
|
38
41
|
deriveKey,
|
|
39
42
|
hmacSignatureSecret,
|
|
40
43
|
hmacKeySignatureSecret,
|
|
41
44
|
...createChallengeParameters(),
|
|
42
45
|
});
|
|
43
|
-
return reply.send({
|
|
46
|
+
return reply.header('Cache-Control', 'no-store').send({
|
|
44
47
|
configuration: setCookie
|
|
45
48
|
? {
|
|
46
49
|
setCookie,
|
|
@@ -51,14 +54,14 @@ function create(options) {
|
|
|
51
54
|
};
|
|
52
55
|
const verifyHandler = async (request, reply) => {
|
|
53
56
|
const payload = await getPayloadFromRequest(request);
|
|
54
|
-
const result = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
57
|
+
const result = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
55
58
|
return reply.send(result);
|
|
56
59
|
};
|
|
57
60
|
const middleware = (options = {}) => {
|
|
58
61
|
const { throwOnFailure = true } = options;
|
|
59
62
|
return async (request, reply) => {
|
|
60
63
|
const payload = await getPayloadFromRequest(request, setCookie?.name);
|
|
61
|
-
const { error, payload: resultPayload, verification, } = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
64
|
+
const { error, payload: resultPayload, verification, } = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
62
65
|
request.altcha = {
|
|
63
66
|
error,
|
|
64
67
|
payload: resultPayload,
|
|
@@ -22,7 +22,7 @@ export declare function create(options: AltchaOptions): {
|
|
|
22
22
|
verifyHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
23
23
|
error: string | null;
|
|
24
24
|
payload: import("../types.js").Payload | import("../types.js").ServerSignaturePayload | null;
|
|
25
|
-
verification: import("../types.js").VerifySolutionResult | null;
|
|
25
|
+
verification: import("../types.js").VerifySolutionResult | import("../types.js").VerifyServerResult | null;
|
|
26
26
|
}>>;
|
|
27
27
|
getPayloadFromEvent: (event: H3Event, cookieName?: string) => Promise<string | undefined>;
|
|
28
28
|
middleware: (options?: AltchaMiddlewareOptions) => import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<void>>;
|
|
@@ -11,8 +11,15 @@ Object.defineProperty(exports, "CappedMap", { enumerable: true, get: function ()
|
|
|
11
11
|
const shared_js_1 = require("./shared.js");
|
|
12
12
|
Object.defineProperty(exports, "deriveHmacKeySecret", { enumerable: true, get: function () { return shared_js_1.deriveHmacKeySecret; } });
|
|
13
13
|
function create(options) {
|
|
14
|
-
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, } = options;
|
|
15
|
-
const challengeHandler = (0, h3_1.defineEventHandler)(async () => {
|
|
14
|
+
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, verifyServer: verifyServerOptions, } = options;
|
|
15
|
+
const challengeHandler = (0, h3_1.defineEventHandler)(async (event) => {
|
|
16
|
+
if (!deriveKey || !createChallengeParameters) {
|
|
17
|
+
throw new h3_1.HTTPError({
|
|
18
|
+
message: 'deriveKey and createChallengeParameters are required to generate challenges. Omit challengeHandler when relying on Sentinel to issue challenges.',
|
|
19
|
+
status: 500,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
(0, h3_1.setResponseHeader)(event, 'Cache-Control', 'no-store');
|
|
16
23
|
return {
|
|
17
24
|
configuration: setCookie
|
|
18
25
|
? {
|
|
@@ -28,7 +35,7 @@ function create(options) {
|
|
|
28
35
|
};
|
|
29
36
|
});
|
|
30
37
|
const verifyHandler = (0, h3_1.defineEventHandler)(async (event) => {
|
|
31
|
-
return await (0, shared_js_1.verify)(await getPayloadFromEvent(event), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
38
|
+
return await (0, shared_js_1.verify)(await getPayloadFromEvent(event), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
32
39
|
});
|
|
33
40
|
const getPayloadFromEvent = async (event, cookieName) => {
|
|
34
41
|
let payload = undefined;
|
|
@@ -59,7 +66,7 @@ function create(options) {
|
|
|
59
66
|
const middleware = (options = {}) => {
|
|
60
67
|
const { throwOnFailure = true } = options;
|
|
61
68
|
return (0, h3_1.defineEventHandler)(async (event) => {
|
|
62
|
-
const { error, payload, verification } = await (0, shared_js_1.verify)(await getPayloadFromEvent(event, setCookie?.name), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
69
|
+
const { error, payload, verification } = await (0, shared_js_1.verify)(await getPayloadFromEvent(event, setCookie?.name), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
63
70
|
event.context.altcha = {
|
|
64
71
|
error,
|
|
65
72
|
payload,
|
|
@@ -84,6 +84,24 @@ export declare function create(options: AltchaOptions): {
|
|
|
84
84
|
invalidSolution: boolean | null;
|
|
85
85
|
time: number;
|
|
86
86
|
verified: boolean;
|
|
87
|
+
} | {
|
|
88
|
+
apiKey?: string | null | undefined;
|
|
89
|
+
reason?: string | undefined;
|
|
90
|
+
verificationData?: {
|
|
91
|
+
[x: string]: import("hono/utils/types.js").JSONValue;
|
|
92
|
+
classification?: import("../types.js").ServerClassification | undefined;
|
|
93
|
+
email?: string | undefined;
|
|
94
|
+
expire?: number | undefined;
|
|
95
|
+
fields?: string[] | undefined;
|
|
96
|
+
fieldsHash?: string | undefined;
|
|
97
|
+
id?: string | undefined;
|
|
98
|
+
ipAddress?: string | undefined;
|
|
99
|
+
reasons?: string[] | undefined;
|
|
100
|
+
score?: number | undefined;
|
|
101
|
+
time?: number | undefined;
|
|
102
|
+
verified?: boolean | undefined;
|
|
103
|
+
} | null | undefined;
|
|
104
|
+
verified: boolean;
|
|
87
105
|
} | null;
|
|
88
106
|
}, import("hono/utils/http-status.js").ContentfulStatusCode, "json">>;
|
|
89
107
|
getPayloadFromContext: (c: Context, cookieName?: string) => Promise<string | undefined>;
|
|
@@ -12,8 +12,14 @@ Object.defineProperty(exports, "CappedMap", { enumerable: true, get: function ()
|
|
|
12
12
|
const shared_js_1 = require("./shared.js");
|
|
13
13
|
Object.defineProperty(exports, "deriveHmacKeySecret", { enumerable: true, get: function () { return shared_js_1.deriveHmacKeySecret; } });
|
|
14
14
|
function create(options) {
|
|
15
|
-
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, } = options;
|
|
15
|
+
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, verifyServer: verifyServerOptions, } = options;
|
|
16
16
|
const challengeHandler = async (c) => {
|
|
17
|
+
if (!deriveKey || !createChallengeParameters) {
|
|
18
|
+
throw new http_exception_1.HTTPException(500, {
|
|
19
|
+
message: 'deriveKey and createChallengeParameters are required to generate challenges. Omit challengeHandler when relying on Sentinel to issue challenges.',
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
c.header('Cache-Control', 'no-store');
|
|
17
23
|
return c.json({
|
|
18
24
|
configuration: setCookie
|
|
19
25
|
? {
|
|
@@ -29,7 +35,7 @@ function create(options) {
|
|
|
29
35
|
});
|
|
30
36
|
};
|
|
31
37
|
const verifyHandler = async (c) => {
|
|
32
|
-
return c.json(await (0, shared_js_1.verify)(await getPayloadFromContext(c), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store));
|
|
38
|
+
return c.json(await (0, shared_js_1.verify)(await getPayloadFromContext(c), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions));
|
|
33
39
|
};
|
|
34
40
|
const getPayloadFromContext = async (c, cookieName) => {
|
|
35
41
|
let payload = undefined;
|
|
@@ -53,7 +59,7 @@ function create(options) {
|
|
|
53
59
|
const middleware = (options = {}) => {
|
|
54
60
|
const { throwOnFailure = true } = options;
|
|
55
61
|
return async (c, next) => {
|
|
56
|
-
const { error, payload, verification } = await (0, shared_js_1.verify)(await getPayloadFromContext(c, setCookie?.name), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
62
|
+
const { error, payload, verification } = await (0, shared_js_1.verify)(await getPayloadFromContext(c, setCookie?.name), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
57
63
|
c.set('altcha', {
|
|
58
64
|
error,
|
|
59
65
|
payload,
|
|
@@ -22,13 +22,14 @@ export declare function createAltchaMiddleware(options?: AltchaMiddlewareOptions
|
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
24
|
export declare class AltchaService {
|
|
25
|
-
private readonly hmacSignatureSecret
|
|
25
|
+
private readonly hmacSignatureSecret?;
|
|
26
26
|
private readonly hmacKeySignatureSecret?;
|
|
27
27
|
private readonly createChallengeParameters;
|
|
28
|
-
private readonly deriveKey
|
|
28
|
+
private readonly deriveKey?;
|
|
29
29
|
private readonly fieldName;
|
|
30
30
|
private readonly setCookieOptions?;
|
|
31
31
|
private readonly store?;
|
|
32
|
+
private readonly verifyServerOptions?;
|
|
32
33
|
constructor(options: AltchaOptions);
|
|
33
34
|
get setCookie(): RequireField<SetCookieOptions, 'name'> | undefined;
|
|
34
35
|
getChallenge(): Promise<{
|
|
@@ -43,7 +44,7 @@ export declare class AltchaService {
|
|
|
43
44
|
verify(payload: string | undefined): Promise<{
|
|
44
45
|
error: string | null;
|
|
45
46
|
payload: import("../types.js").Payload | import("../types.js").ServerSignaturePayload | null;
|
|
46
|
-
verification: import("../types.js").VerifySolutionResult | null;
|
|
47
|
+
verification: import("../types.js").VerifySolutionResult | import("../types.js").VerifyServerResult | null;
|
|
47
48
|
}>;
|
|
48
49
|
}
|
|
49
50
|
export declare class AltchaController {
|
|
@@ -60,7 +61,7 @@ export declare class AltchaController {
|
|
|
60
61
|
verifySolution(req: Request): Promise<{
|
|
61
62
|
error: string | null;
|
|
62
63
|
payload: import("../types.js").Payload | import("../types.js").ServerSignaturePayload | null;
|
|
63
|
-
verification: import("../types.js").VerifySolutionResult | null;
|
|
64
|
+
verification: import("../types.js").VerifySolutionResult | import("../types.js").VerifyServerResult | null;
|
|
64
65
|
}>;
|
|
65
66
|
}
|
|
66
67
|
export declare class AltchaMiddleware implements NestMiddleware {
|
|
@@ -67,16 +67,21 @@ let AltchaService = class AltchaService {
|
|
|
67
67
|
this.fieldName = options.fieldName || 'altcha';
|
|
68
68
|
this.setCookieOptions = options.setCookie;
|
|
69
69
|
this.store = options.store;
|
|
70
|
+
this.verifyServerOptions = options.verifyServer;
|
|
70
71
|
}
|
|
71
72
|
get setCookie() {
|
|
72
73
|
return this.setCookieOptions;
|
|
73
74
|
}
|
|
74
75
|
async getChallenge() {
|
|
76
|
+
const { createChallengeParameters, deriveKey } = this;
|
|
77
|
+
if (!deriveKey || !createChallengeParameters) {
|
|
78
|
+
throw new common_1.HttpException('deriveKey and createChallengeParameters are required to generate challenges. Omit the /challenge route when relying on Sentinel to issue challenges.', common_1.HttpStatus.INTERNAL_SERVER_ERROR);
|
|
79
|
+
}
|
|
75
80
|
const challenge = await (0, pow_js_1.createChallenge)({
|
|
76
|
-
deriveKey
|
|
81
|
+
deriveKey,
|
|
77
82
|
hmacSignatureSecret: this.hmacSignatureSecret,
|
|
78
83
|
hmacKeySignatureSecret: this.hmacKeySignatureSecret,
|
|
79
|
-
...
|
|
84
|
+
...createChallengeParameters(),
|
|
80
85
|
});
|
|
81
86
|
return {
|
|
82
87
|
configuration: this.setCookieOptions
|
|
@@ -92,7 +97,7 @@ let AltchaService = class AltchaService {
|
|
|
92
97
|
return req.body?.[this.fieldName];
|
|
93
98
|
}
|
|
94
99
|
async verify(payload) {
|
|
95
|
-
return (0, shared_js_1.verify)(payload, this.deriveKey, this.hmacSignatureSecret, this.hmacKeySignatureSecret, this.store);
|
|
100
|
+
return (0, shared_js_1.verify)(payload, this.deriveKey, this.hmacSignatureSecret, this.hmacKeySignatureSecret, this.store, this.verifyServerOptions);
|
|
96
101
|
}
|
|
97
102
|
};
|
|
98
103
|
exports.AltchaService = AltchaService;
|
|
@@ -116,6 +121,7 @@ let AltchaController = class AltchaController {
|
|
|
116
121
|
exports.AltchaController = AltchaController;
|
|
117
122
|
__decorate([
|
|
118
123
|
(0, common_1.Get)('challenge'),
|
|
124
|
+
(0, common_1.Header)('Cache-Control', 'no-store'),
|
|
119
125
|
__metadata("design:type", Function),
|
|
120
126
|
__metadata("design:paramtypes", []),
|
|
121
127
|
__metadata("design:returntype", Promise)
|
|
@@ -21,9 +21,12 @@ function deleteCookie(res, name, path) {
|
|
|
21
21
|
res.headers.append('Set-Cookie', `${name}=; Path=${path ?? '/'}; Max-Age=0`);
|
|
22
22
|
}
|
|
23
23
|
function create(options) {
|
|
24
|
-
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, } = options;
|
|
24
|
+
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, verifyServer: verifyServerOptions, } = options;
|
|
25
25
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
26
26
|
async function challengeHandler(_req) {
|
|
27
|
+
if (!deriveKey || !createChallengeParameters) {
|
|
28
|
+
throw new Error('deriveKey and createChallengeParameters are required to generate challenges. Omit challengeHandler when relying on Sentinel to issue challenges.');
|
|
29
|
+
}
|
|
27
30
|
const challenge = await (0, pow_js_1.createChallenge)({
|
|
28
31
|
deriveKey,
|
|
29
32
|
hmacSignatureSecret,
|
|
@@ -38,12 +41,14 @@ function create(options) {
|
|
|
38
41
|
: undefined,
|
|
39
42
|
...challenge,
|
|
40
43
|
};
|
|
41
|
-
const response = Response.json(body
|
|
44
|
+
const response = Response.json(body, {
|
|
45
|
+
headers: { 'Cache-Control': 'no-store' },
|
|
46
|
+
});
|
|
42
47
|
return response;
|
|
43
48
|
}
|
|
44
49
|
async function verifyHandler(req) {
|
|
45
50
|
const payload = await getPayloadFromRequest(req);
|
|
46
|
-
const result = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
51
|
+
const result = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
47
52
|
return Response.json(result);
|
|
48
53
|
}
|
|
49
54
|
async function getPayloadFromRequest(req, cookieName) {
|
|
@@ -67,7 +72,7 @@ function create(options) {
|
|
|
67
72
|
}
|
|
68
73
|
async function middleware(req, throwOnFailure = true) {
|
|
69
74
|
const payload = await getPayloadFromRequest(req, setCookie?.name);
|
|
70
|
-
const { error, payload: verifiedPayload, verification, } = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
75
|
+
const { error, payload: verifiedPayload, verification, } = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
71
76
|
const result = {
|
|
72
77
|
error,
|
|
73
78
|
payload: verifiedPayload,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { DeriveKeyFunction, Payload, ServerSignaturePayload, VerifySolutionResult } from '../types.js';
|
|
2
|
-
import type { Store } from './types.js';
|
|
1
|
+
import { DeriveKeyFunction, Payload, ServerSignaturePayload, VerifyServerResult, VerifySolutionResult } from '../types.js';
|
|
2
|
+
import type { AltchaVerifyServerOptions, Store } from './types.js';
|
|
3
3
|
export declare function deriveHmacKeySecret(masterSecret: string): Promise<string>;
|
|
4
|
-
export declare function verify(payload: unknown, deriveKey: DeriveKeyFunction, hmacSignatureSecret: string, hmacKeySignatureSecret?: string, store?: Store): Promise<{
|
|
4
|
+
export declare function verify(payload: unknown, deriveKey: DeriveKeyFunction | undefined, hmacSignatureSecret: string | undefined, hmacKeySignatureSecret?: string, store?: Store, verifyServerOptions?: AltchaVerifyServerOptions): Promise<{
|
|
5
5
|
error: string | null;
|
|
6
6
|
payload: Payload | ServerSignaturePayload | null;
|
|
7
|
-
verification: VerifySolutionResult | null;
|
|
7
|
+
verification: VerifySolutionResult | VerifyServerResult | null;
|
|
8
8
|
}>;
|
|
@@ -4,12 +4,13 @@ exports.deriveHmacKeySecret = deriveHmacKeySecret;
|
|
|
4
4
|
exports.verify = verify;
|
|
5
5
|
const pow_js_1 = require("../pow.js");
|
|
6
6
|
const server_signature_js_1 = require("../server-signature.js");
|
|
7
|
+
const verify_server_js_1 = require("../verify-server.js");
|
|
7
8
|
const helpers_js_1 = require("../helpers.js");
|
|
8
9
|
const types_js_1 = require("../types.js");
|
|
9
10
|
async function deriveHmacKeySecret(masterSecret) {
|
|
10
11
|
return (0, helpers_js_1.bufferToHex)(await (0, helpers_js_1.hmac)(types_js_1.HmacAlgorithm.SHA_256, masterSecret, 'derived-secret'));
|
|
11
12
|
}
|
|
12
|
-
async function verify(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store) {
|
|
13
|
+
async function verify(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions) {
|
|
13
14
|
if (!payload) {
|
|
14
15
|
return {
|
|
15
16
|
error: 'ALTCHA payload is missing.',
|
|
@@ -33,6 +34,12 @@ async function verify(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureS
|
|
|
33
34
|
try {
|
|
34
35
|
switch (type) {
|
|
35
36
|
case 'client':
|
|
37
|
+
if (!deriveKey) {
|
|
38
|
+
throw new Error('deriveKey is required to verify self-hosted ALTCHA challenges.');
|
|
39
|
+
}
|
|
40
|
+
if (!hmacSignatureSecret) {
|
|
41
|
+
throw new Error('hmacSignatureSecret is required to verify self-hosted ALTCHA challenges.');
|
|
42
|
+
}
|
|
36
43
|
challengeId = getChallengeId(payload);
|
|
37
44
|
if (store && challengeId) {
|
|
38
45
|
await checkChallengeId(store, challengeId);
|
|
@@ -44,7 +51,18 @@ async function verify(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureS
|
|
|
44
51
|
if (store && challengeId) {
|
|
45
52
|
await checkChallengeId(store, challengeId);
|
|
46
53
|
}
|
|
47
|
-
|
|
54
|
+
if (verifyServerOptions) {
|
|
55
|
+
verification = await (0, verify_server_js_1.verifyServer)({
|
|
56
|
+
...verifyServerOptions,
|
|
57
|
+
payload: payload,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
if (!hmacSignatureSecret) {
|
|
62
|
+
throw new Error('hmacSignatureSecret or verifyServer must be configured to verify this payload.');
|
|
63
|
+
}
|
|
64
|
+
verification = await verifyServerSignaturePayload(payload, hmacSignatureSecret);
|
|
65
|
+
}
|
|
48
66
|
break;
|
|
49
67
|
default:
|
|
50
68
|
throw new Error('ALTCHA payload is invalid.');
|
|
@@ -59,7 +77,8 @@ async function verify(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureS
|
|
|
59
77
|
}
|
|
60
78
|
if (!verification?.verified) {
|
|
61
79
|
return {
|
|
62
|
-
error: '
|
|
80
|
+
error: (verification && 'reason' in verification && verification.reason) ||
|
|
81
|
+
'ALTCHA verification failed.',
|
|
63
82
|
payload: payload,
|
|
64
83
|
verification,
|
|
65
84
|
};
|
|
@@ -27,8 +27,11 @@ async function getPayloadFromEvent(event, fieldName, cookieName) {
|
|
|
27
27
|
return body?.[fieldName];
|
|
28
28
|
}
|
|
29
29
|
function create(options) {
|
|
30
|
-
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, } = options;
|
|
30
|
+
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, verifyServer: verifyServerOptions, } = options;
|
|
31
31
|
async function challengeHandler() {
|
|
32
|
+
if (!deriveKey || !createChallengeParameters) {
|
|
33
|
+
(0, kit_1.error)(500, 'deriveKey and createChallengeParameters are required to generate challenges. Omit challengeHandler when relying on Sentinel to issue challenges.');
|
|
34
|
+
}
|
|
32
35
|
const challenge = await (0, pow_js_1.createChallenge)({
|
|
33
36
|
deriveKey,
|
|
34
37
|
hmacSignatureSecret,
|
|
@@ -42,18 +45,18 @@ function create(options) {
|
|
|
42
45
|
}
|
|
43
46
|
: undefined,
|
|
44
47
|
...challenge,
|
|
45
|
-
});
|
|
48
|
+
}, { headers: { 'Cache-Control': 'no-store' } });
|
|
46
49
|
}
|
|
47
50
|
async function verifyHandler(event) {
|
|
48
51
|
const payload = await getPayloadFromEvent(event, fieldName);
|
|
49
|
-
const result = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
52
|
+
const result = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
50
53
|
return (0, kit_1.json)(result);
|
|
51
54
|
}
|
|
52
55
|
function createHandle(middlewareOptions = {}) {
|
|
53
56
|
const { throwOnFailure = true } = middlewareOptions;
|
|
54
57
|
return async ({ event, resolve }) => {
|
|
55
58
|
const payload = await getPayloadFromEvent(event, fieldName, setCookie?.name);
|
|
56
|
-
const { error: verifyError, payload: resultPayload, verification, } = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
59
|
+
const { error: verifyError, payload: resultPayload, verification, } = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
57
60
|
event.locals.altcha = {
|
|
58
61
|
error: verifyError,
|
|
59
62
|
payload: resultPayload,
|
|
@@ -72,7 +75,7 @@ function create(options) {
|
|
|
72
75
|
}
|
|
73
76
|
async function verifyEvent(event) {
|
|
74
77
|
const payload = await getPayloadFromEvent(event, fieldName, setCookie?.name);
|
|
75
|
-
const { error: verifyError, payload: resultPayload, verification, } = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
78
|
+
const { error: verifyError, payload: resultPayload, verification, } = await (0, shared_js_1.verify)(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store, verifyServerOptions);
|
|
76
79
|
if (setCookie) {
|
|
77
80
|
event.cookies.delete(setCookie.name, {
|
|
78
81
|
path: setCookie.path ?? '/',
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import type { CreateChallengeOptions, DeriveKeyFunction, Payload, ServerSignaturePayload, SetCookieOptions, VerifySolutionResult } from '../types.js';
|
|
1
|
+
import type { CreateChallengeOptions, DeriveKeyFunction, Payload, ServerSignaturePayload, SetCookieOptions, VerifyServerOptions, VerifyServerResult, VerifySolutionResult } from '../types.js';
|
|
2
|
+
export type AltchaVerifyServerOptions = Omit<VerifyServerOptions, 'payload'>;
|
|
2
3
|
export interface AltchaOptions {
|
|
3
4
|
/**
|
|
4
5
|
* Returns configuration options passed to `createChallenge`.
|
|
5
6
|
* Must include `algorithm` and `cost`; all other `CreateChallengeOptions` fields are optional.
|
|
7
|
+
* Required to use `challengeHandler`; optional if challenges are issued by Sentinel and
|
|
8
|
+
* only `verifyServer` is used.
|
|
6
9
|
*/
|
|
7
|
-
createChallengeParameters
|
|
10
|
+
createChallengeParameters?: () => Pick<CreateChallengeOptions, 'algorithm' | 'cost'> & Partial<CreateChallengeOptions>;
|
|
8
11
|
/**
|
|
9
12
|
* Algorithm-specific key derivation function used to generate challenge keys.
|
|
13
|
+
* Required to use `challengeHandler` or to verify self-hosted (client-type) payloads;
|
|
14
|
+
* optional if challenges are issued by Sentinel and only `verifyServer` is used.
|
|
10
15
|
*/
|
|
11
|
-
deriveKey
|
|
16
|
+
deriveKey?: DeriveKeyFunction;
|
|
12
17
|
/**
|
|
13
18
|
* Name of the form field that carries the ALTCHA payload.
|
|
14
19
|
* Defaults to `'altcha'` if not specified.
|
|
@@ -16,8 +21,10 @@ export interface AltchaOptions {
|
|
|
16
21
|
fieldName?: string;
|
|
17
22
|
/**
|
|
18
23
|
* HMAC secret used to sign and verify challenge signatures.
|
|
24
|
+
* Optional if `verifyServer` is configured and only Sentinel-issued (server-signed)
|
|
25
|
+
* payloads are verified remotely.
|
|
19
26
|
*/
|
|
20
|
-
hmacSignatureSecret
|
|
27
|
+
hmacSignatureSecret?: string;
|
|
21
28
|
/**
|
|
22
29
|
* HMAC secret used to sign keys in deterministic mode.
|
|
23
30
|
*/
|
|
@@ -31,6 +38,11 @@ export interface AltchaOptions {
|
|
|
31
38
|
* Store implementation for tracking used challenges and preventing replay attacks.
|
|
32
39
|
*/
|
|
33
40
|
store?: Store;
|
|
41
|
+
/**
|
|
42
|
+
* When set, Sentinel-issued (server-signed) payloads are verified remotely via
|
|
43
|
+
* `POST /v1/verify/signature` instead of locally with `hmacSignatureSecret`.
|
|
44
|
+
*/
|
|
45
|
+
verifyServer?: AltchaVerifyServerOptions;
|
|
34
46
|
}
|
|
35
47
|
export interface AltchaMiddlewareOptions {
|
|
36
48
|
throwOnFailure?: boolean;
|
|
@@ -38,7 +50,7 @@ export interface AltchaMiddlewareOptions {
|
|
|
38
50
|
export interface AltchaResult {
|
|
39
51
|
error: string | null;
|
|
40
52
|
payload: Payload | ServerSignaturePayload | null;
|
|
41
|
-
verification: VerifySolutionResult | null;
|
|
53
|
+
verification: VerifySolutionResult | VerifyServerResult | null;
|
|
42
54
|
}
|
|
43
55
|
export type RequireField<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
|
|
44
56
|
export interface Store {
|
package/dist/cjs/v2/helpers.d.ts
CHANGED
|
@@ -17,8 +17,6 @@ export declare function delay(ms: number): Promise<void>;
|
|
|
17
17
|
export declare function hash(algorithm: string, data: ArrayBuffer | string): Promise<Uint8Array>;
|
|
18
18
|
/** Computes an HMAC signature using the Web Crypto API. */
|
|
19
19
|
export declare function hmac(algorithm: HmacAlgorithm, data: string | Uint8Array, keyStr: string): Promise<Uint8Array>;
|
|
20
|
-
/** Inject CSS tag into the document */
|
|
21
|
-
export declare function injectCss(css: string, id?: string): void;
|
|
22
20
|
/** Generates a random integer between the specified minimum and maximum values (inclusive). */
|
|
23
21
|
export declare function randomInt(max: number, min?: number): number;
|
|
24
22
|
/** Recursively sorts object keys alphabetically for deterministic serialization. */
|
package/dist/cjs/v2/helpers.js
CHANGED
|
@@ -9,7 +9,6 @@ exports.constantTimeEqual = constantTimeEqual;
|
|
|
9
9
|
exports.delay = delay;
|
|
10
10
|
exports.hash = hash;
|
|
11
11
|
exports.hmac = hmac;
|
|
12
|
-
exports.injectCss = injectCss;
|
|
13
12
|
exports.randomInt = randomInt;
|
|
14
13
|
exports.sortKeys = sortKeys;
|
|
15
14
|
exports.timeDuration = timeDuration;
|
|
@@ -88,17 +87,6 @@ async function hmac(algorithm, data, keyStr) {
|
|
|
88
87
|
: data);
|
|
89
88
|
return new Uint8Array(signature);
|
|
90
89
|
}
|
|
91
|
-
/** Inject CSS tag into the document */
|
|
92
|
-
function injectCss(css, id = 'altcha-css') {
|
|
93
|
-
if (typeof document !== 'undefined' &&
|
|
94
|
-
document &&
|
|
95
|
-
!document.getElementById(id)) {
|
|
96
|
-
const style = document.createElement('style');
|
|
97
|
-
style.id = id;
|
|
98
|
-
style.textContent = css;
|
|
99
|
-
document.head.appendChild(style);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
90
|
/** Generates a random integer between the specified minimum and maximum values (inclusive). */
|
|
103
91
|
function randomInt(max, min = 1) {
|
|
104
92
|
const ab = new Uint32Array(1);
|
package/dist/cjs/v2/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createChallenge, solveChallenge, solveChallengeWorkers, verifySolution } from './pow.js';
|
|
2
2
|
import { verifyFieldsHash, verifyServerSignature } from './server-signature.js';
|
|
3
|
+
import { verifyServer } from './verify-server.js';
|
|
3
4
|
import { CappedMap } from './capped-map.js';
|
|
4
5
|
import { randomInt } from './helpers.js';
|
|
5
6
|
import { HmacAlgorithm } from './types.js';
|
|
6
7
|
export type * from './types.js';
|
|
7
|
-
export { CappedMap, HmacAlgorithm, createChallenge, randomInt, solveChallenge, solveChallengeWorkers, verifyFieldsHash, verifyServerSignature, verifySolution, };
|
|
8
|
+
export { CappedMap, HmacAlgorithm, createChallenge, randomInt, solveChallenge, solveChallengeWorkers, verifyFieldsHash, verifyServer, verifyServerSignature, verifySolution, };
|
|
8
9
|
declare const _default: {
|
|
9
10
|
CappedMap: typeof CappedMap;
|
|
10
11
|
HmacAlgorithm: typeof HmacAlgorithm;
|
|
@@ -13,6 +14,7 @@ declare const _default: {
|
|
|
13
14
|
solveChallenge: typeof solveChallenge;
|
|
14
15
|
solveChallengeWorkers: typeof solveChallengeWorkers;
|
|
15
16
|
verifyFieldsHash: typeof verifyFieldsHash;
|
|
17
|
+
verifyServer: typeof verifyServer;
|
|
16
18
|
verifyServerSignature: typeof verifyServerSignature;
|
|
17
19
|
verifySolution: typeof verifySolution;
|
|
18
20
|
};
|
package/dist/cjs/v2/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.verifySolution = exports.verifyServerSignature = exports.verifyFieldsHash = exports.solveChallengeWorkers = exports.solveChallenge = exports.randomInt = exports.createChallenge = exports.HmacAlgorithm = exports.CappedMap = void 0;
|
|
3
|
+
exports.verifySolution = exports.verifyServerSignature = exports.verifyServer = exports.verifyFieldsHash = exports.solveChallengeWorkers = exports.solveChallenge = exports.randomInt = exports.createChallenge = exports.HmacAlgorithm = exports.CappedMap = void 0;
|
|
4
4
|
const pow_js_1 = require("./pow.js");
|
|
5
5
|
Object.defineProperty(exports, "createChallenge", { enumerable: true, get: function () { return pow_js_1.createChallenge; } });
|
|
6
6
|
Object.defineProperty(exports, "solveChallenge", { enumerable: true, get: function () { return pow_js_1.solveChallenge; } });
|
|
@@ -9,6 +9,8 @@ Object.defineProperty(exports, "verifySolution", { enumerable: true, get: functi
|
|
|
9
9
|
const server_signature_js_1 = require("./server-signature.js");
|
|
10
10
|
Object.defineProperty(exports, "verifyFieldsHash", { enumerable: true, get: function () { return server_signature_js_1.verifyFieldsHash; } });
|
|
11
11
|
Object.defineProperty(exports, "verifyServerSignature", { enumerable: true, get: function () { return server_signature_js_1.verifyServerSignature; } });
|
|
12
|
+
const verify_server_js_1 = require("./verify-server.js");
|
|
13
|
+
Object.defineProperty(exports, "verifyServer", { enumerable: true, get: function () { return verify_server_js_1.verifyServer; } });
|
|
12
14
|
const capped_map_js_1 = require("./capped-map.js");
|
|
13
15
|
Object.defineProperty(exports, "CappedMap", { enumerable: true, get: function () { return capped_map_js_1.CappedMap; } });
|
|
14
16
|
const helpers_js_1 = require("./helpers.js");
|
|
@@ -23,6 +25,7 @@ exports.default = {
|
|
|
23
25
|
solveChallenge: pow_js_1.solveChallenge,
|
|
24
26
|
solveChallengeWorkers: pow_js_1.solveChallengeWorkers,
|
|
25
27
|
verifyFieldsHash: server_signature_js_1.verifyFieldsHash,
|
|
28
|
+
verifyServer: verify_server_js_1.verifyServer,
|
|
26
29
|
verifyServerSignature: server_signature_js_1.verifyServerSignature,
|
|
27
30
|
verifySolution: pow_js_1.verifySolution,
|
|
28
31
|
};
|