altcha-lib 1.4.1 → 2.0.0-beta.2
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/LICENSE.txt +1 -1
- package/README.md +45 -149
- package/bin/cli.mjs +35 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/cjs/v2/algorithms/argon2id.d.ts +2 -0
- package/dist/cjs/v2/algorithms/argon2id.js +26 -0
- package/dist/cjs/v2/algorithms/pbkdf2.d.ts +2 -0
- package/dist/cjs/v2/algorithms/pbkdf2.js +23 -0
- package/dist/cjs/v2/algorithms/scrypt.d.ts +2 -0
- package/dist/cjs/v2/algorithms/scrypt.js +17 -0
- package/dist/cjs/v2/algorithms/sha.d.ts +2 -0
- package/dist/cjs/v2/algorithms/sha.js +35 -0
- package/dist/cjs/v2/algorithms/web/pbkdf2.d.ts +2 -0
- package/dist/cjs/v2/algorithms/web/pbkdf2.js +19 -0
- package/dist/cjs/v2/algorithms/web/sha.d.ts +2 -0
- package/dist/cjs/v2/algorithms/web/sha.js +23 -0
- package/dist/cjs/v2/capped-map.d.ts +7 -0
- package/dist/cjs/v2/capped-map.js +18 -0
- package/dist/cjs/v2/frameworks/express.d.ts +21 -0
- package/dist/cjs/v2/frameworks/express.js +77 -0
- package/dist/cjs/v2/frameworks/fastify.d.ts +26 -0
- package/dist/cjs/v2/frameworks/fastify.js +88 -0
- package/dist/cjs/v2/frameworks/h3.d.ts +37 -0
- package/dist/cjs/v2/frameworks/h3.js +89 -0
- package/dist/cjs/v2/frameworks/hono.d.ts +99 -0
- package/dist/cjs/v2/frameworks/hono.js +86 -0
- package/dist/cjs/v2/frameworks/nestjs.d.ts +79 -0
- package/dist/cjs/v2/frameworks/nestjs.js +198 -0
- package/dist/cjs/v2/frameworks/nextjs.d.ts +21 -0
- package/dist/cjs/v2/frameworks/nextjs.js +112 -0
- package/dist/cjs/v2/frameworks/shared.d.ts +8 -0
- package/dist/cjs/v2/frameworks/shared.js +121 -0
- package/dist/cjs/v2/frameworks/sveltekit.d.ts +29 -0
- package/dist/cjs/v2/frameworks/sveltekit.js +101 -0
- package/dist/cjs/v2/frameworks/types.d.ts +47 -0
- package/dist/cjs/v2/frameworks/types.js +2 -0
- package/dist/cjs/v2/helpers.d.ts +27 -0
- package/dist/cjs/v2/helpers.js +127 -0
- package/dist/cjs/v2/index.d.ts +19 -0
- package/dist/cjs/v2/index.js +28 -0
- package/dist/cjs/v2/obfuscation.d.ts +11 -0
- package/dist/cjs/v2/obfuscation.js +74 -0
- package/dist/cjs/v2/pow.d.ts +60 -0
- package/dist/cjs/v2/pow.js +287 -0
- package/dist/cjs/v2/server-signature.d.ts +12 -0
- package/dist/cjs/v2/server-signature.js +68 -0
- package/dist/cjs/v2/types.d.ts +277 -0
- package/dist/cjs/v2/types.js +18 -0
- package/dist/cjs/v2/workers/argon2id.js +7 -0
- package/dist/cjs/v2/workers/pbkdf2.js +7 -0
- package/dist/cjs/v2/workers/scrypt.d.ts +1 -0
- package/dist/cjs/v2/workers/scrypt.js +7 -0
- package/dist/cjs/v2/workers/sha.d.ts +1 -0
- package/dist/cjs/v2/workers/sha.js +7 -0
- package/dist/cjs/v2/workers/shared.d.ts +4 -0
- package/dist/cjs/v2/workers/shared.js +33 -0
- package/dist/esm/tsconfig.build.tsbuildinfo +1 -0
- package/dist/esm/v1/types.js +1 -0
- package/dist/esm/v1/worker.d.ts +1 -0
- package/dist/esm/v2/algorithms/argon2id.d.ts +2 -0
- package/dist/esm/v2/algorithms/argon2id.js +20 -0
- package/dist/esm/v2/algorithms/pbkdf2.d.ts +2 -0
- package/dist/esm/v2/algorithms/pbkdf2.js +20 -0
- package/dist/esm/v2/algorithms/scrypt.d.ts +2 -0
- package/dist/esm/v2/algorithms/scrypt.js +14 -0
- package/dist/esm/v2/algorithms/sha.d.ts +2 -0
- package/dist/esm/v2/algorithms/sha.js +32 -0
- package/dist/esm/v2/algorithms/web/pbkdf2.d.ts +2 -0
- package/dist/esm/v2/algorithms/web/pbkdf2.js +16 -0
- package/dist/esm/v2/algorithms/web/sha.d.ts +2 -0
- package/dist/esm/v2/algorithms/web/sha.js +20 -0
- package/dist/esm/v2/capped-map.d.ts +7 -0
- package/dist/esm/v2/capped-map.js +15 -0
- package/dist/esm/v2/frameworks/express.d.ts +21 -0
- package/dist/esm/v2/frameworks/express.js +71 -0
- package/dist/esm/v2/frameworks/fastify.d.ts +26 -0
- package/dist/esm/v2/frameworks/fastify.js +82 -0
- package/dist/esm/v2/frameworks/h3.d.ts +37 -0
- package/dist/esm/v2/frameworks/h3.js +83 -0
- package/dist/esm/v2/frameworks/hono.d.ts +99 -0
- package/dist/esm/v2/frameworks/hono.js +80 -0
- package/dist/esm/v2/frameworks/nestjs.d.ts +79 -0
- package/dist/esm/v2/frameworks/nestjs.js +202 -0
- package/dist/esm/v2/frameworks/nextjs.d.ts +21 -0
- package/dist/esm/v2/frameworks/nextjs.js +106 -0
- package/dist/esm/v2/frameworks/shared.d.ts +8 -0
- package/dist/esm/v2/frameworks/shared.js +117 -0
- package/dist/esm/v2/frameworks/sveltekit.d.ts +29 -0
- package/dist/esm/v2/frameworks/sveltekit.js +95 -0
- package/dist/esm/v2/frameworks/types.d.ts +47 -0
- package/dist/esm/v2/frameworks/types.js +1 -0
- package/dist/esm/v2/helpers.d.ts +27 -0
- package/dist/esm/v2/helpers.js +112 -0
- package/dist/esm/v2/index.d.ts +19 -0
- package/dist/esm/v2/index.js +17 -0
- package/dist/esm/v2/obfuscation.d.ts +11 -0
- package/dist/esm/v2/obfuscation.js +70 -0
- package/dist/esm/v2/pow.d.ts +60 -0
- package/dist/esm/v2/pow.js +282 -0
- package/dist/esm/v2/server-signature.d.ts +12 -0
- package/dist/esm/v2/server-signature.js +63 -0
- package/dist/esm/v2/types.d.ts +277 -0
- package/dist/esm/v2/types.js +15 -0
- package/dist/esm/v2/workers/argon2id.d.ts +1 -0
- package/dist/esm/v2/workers/argon2id.js +5 -0
- package/dist/esm/v2/workers/pbkdf2.d.ts +1 -0
- package/dist/esm/v2/workers/pbkdf2.js +5 -0
- package/dist/esm/v2/workers/scrypt.d.ts +1 -0
- package/dist/esm/v2/workers/scrypt.js +5 -0
- package/dist/esm/v2/workers/sha.d.ts +1 -0
- package/dist/esm/v2/workers/sha.js +5 -0
- package/dist/esm/v2/workers/shared.d.ts +4 -0
- package/dist/esm/v2/workers/shared.js +30 -0
- package/package.json +139 -27
- package/cjs/dist/tsconfig.cjs.tsbuildinfo +0 -1
- package/cjs/package.json +0 -3
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- /package/{cjs/dist → dist/cjs/v1}/helpers.d.ts +0 -0
- /package/{cjs/dist → dist/cjs/v1}/helpers.js +0 -0
- /package/{cjs/dist → dist/cjs/v1}/index.d.ts +0 -0
- /package/{cjs/dist → dist/cjs/v1}/index.js +0 -0
- /package/{cjs/dist → dist/cjs/v1}/types.d.ts +0 -0
- /package/{cjs/dist → dist/cjs/v1}/types.js +0 -0
- /package/{cjs/dist → dist/cjs/v1}/worker.d.ts +0 -0
- /package/{cjs/dist → dist/cjs/v1}/worker.js +0 -0
- /package/dist/{types.js → cjs/v2/workers/argon2id.d.ts} +0 -0
- /package/dist/{worker.d.ts → cjs/v2/workers/pbkdf2.d.ts} +0 -0
- /package/dist/{helpers.d.ts → esm/v1/helpers.d.ts} +0 -0
- /package/dist/{helpers.js → esm/v1/helpers.js} +0 -0
- /package/dist/{index.d.ts → esm/v1/index.d.ts} +0 -0
- /package/dist/{index.js → esm/v1/index.js} +0 -0
- /package/dist/{types.d.ts → esm/v1/types.d.ts} +0 -0
- /package/dist/{worker.js → esm/v1/worker.js} +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { randomInt } from '../helpers.js';
|
|
2
|
+
import { deriveHmacKeySecret, verify } from './shared.js';
|
|
3
|
+
import { CappedMap } from '../capped-map.js';
|
|
4
|
+
import type { AltchaMiddlewareOptions, AltchaOptions, AltchaResult } from './types.js';
|
|
5
|
+
import type { FastifyRequest, FastifyReply } from 'fastify';
|
|
6
|
+
export { CappedMap, deriveHmacKeySecret, randomInt };
|
|
7
|
+
export type { AltchaOptions, AltchaResult };
|
|
8
|
+
declare module 'fastify' {
|
|
9
|
+
interface FastifyRequest {
|
|
10
|
+
altcha?: AltchaResult;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export declare function create(options: AltchaOptions): {
|
|
14
|
+
challengeHandler: (request: FastifyRequest, reply: FastifyReply) => Promise<never>;
|
|
15
|
+
verifyHandler: (request: FastifyRequest, reply: FastifyReply) => Promise<never>;
|
|
16
|
+
getPayloadFromRequest: (request: FastifyRequest, cookieName?: string) => Promise<string | undefined>;
|
|
17
|
+
middleware: (options?: AltchaMiddlewareOptions) => (request: FastifyRequest, reply: FastifyReply) => Promise<undefined>;
|
|
18
|
+
verify: typeof verify;
|
|
19
|
+
};
|
|
20
|
+
declare const _default: {
|
|
21
|
+
CappedMap: typeof CappedMap;
|
|
22
|
+
create: typeof create;
|
|
23
|
+
deriveHmacKeySecret: typeof deriveHmacKeySecret;
|
|
24
|
+
randomInt: typeof randomInt;
|
|
25
|
+
};
|
|
26
|
+
export default _default;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createChallenge } from '../pow.js';
|
|
2
|
+
import { randomInt } from '../helpers.js';
|
|
3
|
+
import { deriveHmacKeySecret, verify } from './shared.js';
|
|
4
|
+
import { CappedMap } from '../capped-map.js';
|
|
5
|
+
export { CappedMap, deriveHmacKeySecret, randomInt };
|
|
6
|
+
export function create(options) {
|
|
7
|
+
const { createChallengeParameters, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, } = options;
|
|
8
|
+
const deleteCookie = (reply, name, path = '/') => {
|
|
9
|
+
reply.header('Set-Cookie', `${name}=; Path=${path ?? '/'}; Max-Age=0`);
|
|
10
|
+
};
|
|
11
|
+
const parseCookies = (request) => {
|
|
12
|
+
if ('cookies' in request) {
|
|
13
|
+
return request.cookies;
|
|
14
|
+
}
|
|
15
|
+
const header = request.headers.cookie;
|
|
16
|
+
if (!header) {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
return Object.fromEntries(header.split(';').map((pair) => {
|
|
20
|
+
const [key, ...rest] = pair.trim().split('=');
|
|
21
|
+
return [key, decodeURIComponent(rest.join('='))];
|
|
22
|
+
}));
|
|
23
|
+
};
|
|
24
|
+
const getPayloadFromRequest = async (request, cookieName) => {
|
|
25
|
+
if (cookieName) {
|
|
26
|
+
return parseCookies(request)[cookieName];
|
|
27
|
+
}
|
|
28
|
+
return request.body?.altcha;
|
|
29
|
+
};
|
|
30
|
+
const challengeHandler = async (request, reply) => {
|
|
31
|
+
const challenge = await createChallenge({
|
|
32
|
+
deriveKey,
|
|
33
|
+
hmacSignatureSecret,
|
|
34
|
+
hmacKeySignatureSecret,
|
|
35
|
+
...createChallengeParameters(),
|
|
36
|
+
});
|
|
37
|
+
return reply.send({
|
|
38
|
+
configuration: setCookie
|
|
39
|
+
? {
|
|
40
|
+
setCookie,
|
|
41
|
+
}
|
|
42
|
+
: undefined,
|
|
43
|
+
...challenge,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
const verifyHandler = async (request, reply) => {
|
|
47
|
+
const payload = await getPayloadFromRequest(request);
|
|
48
|
+
const result = await verify(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
49
|
+
return reply.send(result);
|
|
50
|
+
};
|
|
51
|
+
const middleware = (options = {}) => {
|
|
52
|
+
const { throwOnFailure = true } = options;
|
|
53
|
+
return async (request, reply) => {
|
|
54
|
+
const payload = await getPayloadFromRequest(request, setCookie?.name);
|
|
55
|
+
const { error, payload: resultPayload, verification, } = await verify(payload, deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
56
|
+
request.altcha = {
|
|
57
|
+
error,
|
|
58
|
+
payload: resultPayload,
|
|
59
|
+
verification,
|
|
60
|
+
};
|
|
61
|
+
if (setCookie) {
|
|
62
|
+
deleteCookie(reply, setCookie.name, setCookie.path);
|
|
63
|
+
}
|
|
64
|
+
if (error && throwOnFailure) {
|
|
65
|
+
return reply.code(403).send({ error });
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
challengeHandler,
|
|
71
|
+
verifyHandler,
|
|
72
|
+
getPayloadFromRequest,
|
|
73
|
+
middleware,
|
|
74
|
+
verify,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export default {
|
|
78
|
+
CappedMap,
|
|
79
|
+
create,
|
|
80
|
+
deriveHmacKeySecret,
|
|
81
|
+
randomInt,
|
|
82
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { randomInt } from '../helpers.js';
|
|
2
|
+
import { CappedMap } from '../capped-map.js';
|
|
3
|
+
import { deriveHmacKeySecret, verify } from './shared.js';
|
|
4
|
+
import type { AltchaMiddlewareOptions, AltchaOptions, AltchaResult } from './types.js';
|
|
5
|
+
import type { H3Event } from 'h3';
|
|
6
|
+
export { CappedMap, deriveHmacKeySecret, randomInt };
|
|
7
|
+
export type { AltchaOptions, AltchaResult };
|
|
8
|
+
declare module 'h3' {
|
|
9
|
+
interface H3EventContext {
|
|
10
|
+
altcha?: AltchaResult;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export declare function create(options: AltchaOptions): {
|
|
14
|
+
challengeHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
15
|
+
codeChallenge?: import("../types.js").CodeChallenge;
|
|
16
|
+
parameters: import("../types.js").ChallengeParameters;
|
|
17
|
+
signature?: string;
|
|
18
|
+
configuration: {
|
|
19
|
+
setCookie: import("./types.js").RequireField<import("../types.js").SetCookieOptions, "name">;
|
|
20
|
+
} | undefined;
|
|
21
|
+
}>>;
|
|
22
|
+
verifyHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
23
|
+
error: string | null;
|
|
24
|
+
payload: import("../types.js").Payload | import("../types.js").ServerSignaturePayload | null;
|
|
25
|
+
verification: import("../types.js").VerifySolutionResult | null;
|
|
26
|
+
}>>;
|
|
27
|
+
getPayloadFromEvent: (event: H3Event, cookieName?: string) => Promise<string | undefined>;
|
|
28
|
+
middleware: (options?: AltchaMiddlewareOptions) => import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<void>>;
|
|
29
|
+
verify: typeof verify;
|
|
30
|
+
};
|
|
31
|
+
declare const _default: {
|
|
32
|
+
CappedMap: typeof CappedMap;
|
|
33
|
+
create: typeof create;
|
|
34
|
+
deriveHmacKeySecret: typeof deriveHmacKeySecret;
|
|
35
|
+
randomInt: typeof randomInt;
|
|
36
|
+
};
|
|
37
|
+
export default _default;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { HTTPError, defineEventHandler, getCookie, deleteCookie, readBody, } from 'h3';
|
|
2
|
+
import { createChallenge } from '../pow.js';
|
|
3
|
+
import { randomInt } from '../helpers.js';
|
|
4
|
+
import { CappedMap } from '../capped-map.js';
|
|
5
|
+
import { deriveHmacKeySecret, verify } from './shared.js';
|
|
6
|
+
export { CappedMap, deriveHmacKeySecret, randomInt };
|
|
7
|
+
export function create(options) {
|
|
8
|
+
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, } = options;
|
|
9
|
+
const challengeHandler = defineEventHandler(async () => {
|
|
10
|
+
return {
|
|
11
|
+
configuration: setCookie
|
|
12
|
+
? {
|
|
13
|
+
setCookie,
|
|
14
|
+
}
|
|
15
|
+
: undefined,
|
|
16
|
+
...(await createChallenge({
|
|
17
|
+
deriveKey,
|
|
18
|
+
hmacSignatureSecret,
|
|
19
|
+
hmacKeySignatureSecret,
|
|
20
|
+
...createChallengeParameters(),
|
|
21
|
+
})),
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
const verifyHandler = defineEventHandler(async (event) => {
|
|
25
|
+
return await verify(await getPayloadFromEvent(event), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
26
|
+
});
|
|
27
|
+
const getPayloadFromEvent = async (event, cookieName) => {
|
|
28
|
+
let payload = undefined;
|
|
29
|
+
if (cookieName) {
|
|
30
|
+
payload = getCookie(event, cookieName);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const contentType = event.req.headers.get('content-type') ?? '';
|
|
34
|
+
let body = null;
|
|
35
|
+
if (contentType.includes('application/json')) {
|
|
36
|
+
body = await readBody(event);
|
|
37
|
+
}
|
|
38
|
+
else if (contentType.includes('multipart/form-data')) {
|
|
39
|
+
const formData = await event.req.formData();
|
|
40
|
+
const value = formData.get(fieldName);
|
|
41
|
+
if (typeof value === 'string') {
|
|
42
|
+
payload = value;
|
|
43
|
+
}
|
|
44
|
+
return payload;
|
|
45
|
+
}
|
|
46
|
+
else if (contentType.includes('application/x-www-form-urlencoded')) {
|
|
47
|
+
body = await readBody(event);
|
|
48
|
+
}
|
|
49
|
+
payload = body?.[fieldName];
|
|
50
|
+
}
|
|
51
|
+
return payload;
|
|
52
|
+
};
|
|
53
|
+
const middleware = (options = {}) => {
|
|
54
|
+
const { throwOnFailure = true } = options;
|
|
55
|
+
return defineEventHandler(async (event) => {
|
|
56
|
+
const { error, payload, verification } = await verify(await getPayloadFromEvent(event, setCookie?.name), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
57
|
+
event.context.altcha = {
|
|
58
|
+
error,
|
|
59
|
+
payload,
|
|
60
|
+
verification,
|
|
61
|
+
};
|
|
62
|
+
if (setCookie) {
|
|
63
|
+
deleteCookie(event, setCookie.name);
|
|
64
|
+
}
|
|
65
|
+
if (error && throwOnFailure) {
|
|
66
|
+
throw new HTTPError({ message: error, status: 403 });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
challengeHandler,
|
|
72
|
+
verifyHandler,
|
|
73
|
+
getPayloadFromEvent,
|
|
74
|
+
middleware,
|
|
75
|
+
verify,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export default {
|
|
79
|
+
CappedMap,
|
|
80
|
+
create,
|
|
81
|
+
deriveHmacKeySecret,
|
|
82
|
+
randomInt,
|
|
83
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { randomInt } from '../helpers.js';
|
|
2
|
+
import { CappedMap } from '../capped-map.js';
|
|
3
|
+
import { deriveHmacKeySecret, verify } from './shared.js';
|
|
4
|
+
import type { AltchaMiddlewareOptions, AltchaOptions, AltchaResult } from './types.js';
|
|
5
|
+
import type { Context, Env, MiddlewareHandler } from 'hono';
|
|
6
|
+
export { CappedMap, deriveHmacKeySecret, randomInt };
|
|
7
|
+
export type { AltchaOptions, AltchaResult };
|
|
8
|
+
declare module 'hono' {
|
|
9
|
+
interface ContextVariableMap {
|
|
10
|
+
altcha?: AltchaResult;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export declare function create(options: AltchaOptions): {
|
|
14
|
+
challengeHandler: <E extends Env, P extends string>(c: Context<E, P>) => Promise<Response & import("hono").TypedResponse<{
|
|
15
|
+
codeChallenge?: {
|
|
16
|
+
image: string;
|
|
17
|
+
audio?: string | undefined;
|
|
18
|
+
length?: number | undefined;
|
|
19
|
+
} | undefined;
|
|
20
|
+
parameters: {
|
|
21
|
+
algorithm: string;
|
|
22
|
+
nonce: string;
|
|
23
|
+
salt: string;
|
|
24
|
+
cost: number;
|
|
25
|
+
keyLength: number;
|
|
26
|
+
keyPrefix: string;
|
|
27
|
+
keySignature?: string | undefined;
|
|
28
|
+
memoryCost?: number | undefined;
|
|
29
|
+
parallelism?: number | undefined;
|
|
30
|
+
expiresAt?: number | undefined;
|
|
31
|
+
data?: {
|
|
32
|
+
[x: string]: string | number | boolean | null;
|
|
33
|
+
} | undefined;
|
|
34
|
+
};
|
|
35
|
+
signature?: string | undefined;
|
|
36
|
+
configuration: {
|
|
37
|
+
setCookie: {
|
|
38
|
+
domain?: string | undefined;
|
|
39
|
+
maxAge?: number | undefined;
|
|
40
|
+
path?: string | undefined;
|
|
41
|
+
sameSite?: string | undefined;
|
|
42
|
+
secure?: boolean | undefined;
|
|
43
|
+
name: string;
|
|
44
|
+
};
|
|
45
|
+
} | undefined;
|
|
46
|
+
}, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
|
|
47
|
+
verifyHandler: <E extends Env, P extends string>(c: Context<E, P>) => Promise<Response & import("hono").TypedResponse<{
|
|
48
|
+
error: string | null;
|
|
49
|
+
payload: {
|
|
50
|
+
challenge: {
|
|
51
|
+
signature?: string | undefined;
|
|
52
|
+
parameters: {
|
|
53
|
+
algorithm: string;
|
|
54
|
+
nonce: string;
|
|
55
|
+
salt: string;
|
|
56
|
+
cost: number;
|
|
57
|
+
keyLength: number;
|
|
58
|
+
keyPrefix: string;
|
|
59
|
+
keySignature?: string | undefined;
|
|
60
|
+
memoryCost?: number | undefined;
|
|
61
|
+
parallelism?: number | undefined;
|
|
62
|
+
expiresAt?: number | undefined;
|
|
63
|
+
data?: {
|
|
64
|
+
[x: string]: string | number | boolean | null;
|
|
65
|
+
} | undefined;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
solution: {
|
|
69
|
+
counter: number;
|
|
70
|
+
derivedKey: string;
|
|
71
|
+
time?: number | undefined;
|
|
72
|
+
};
|
|
73
|
+
} | {
|
|
74
|
+
algorithm: string;
|
|
75
|
+
apiKey?: string | undefined;
|
|
76
|
+
id?: string | undefined;
|
|
77
|
+
signature: string;
|
|
78
|
+
verificationData: string;
|
|
79
|
+
verified: boolean;
|
|
80
|
+
} | null;
|
|
81
|
+
verification: {
|
|
82
|
+
expired: boolean;
|
|
83
|
+
invalidSignature: boolean | null;
|
|
84
|
+
invalidSolution: boolean | null;
|
|
85
|
+
time: number;
|
|
86
|
+
verified: boolean;
|
|
87
|
+
} | null;
|
|
88
|
+
}, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
|
|
89
|
+
getPayloadFromContext: (c: Context, cookieName?: string) => Promise<string | undefined>;
|
|
90
|
+
middleware: <E extends Env, P extends string>(options?: AltchaMiddlewareOptions) => MiddlewareHandler<E, P>;
|
|
91
|
+
verify: typeof verify;
|
|
92
|
+
};
|
|
93
|
+
declare const _default: {
|
|
94
|
+
CappedMap: typeof CappedMap;
|
|
95
|
+
create: typeof create;
|
|
96
|
+
deriveHmacKeySecret: typeof deriveHmacKeySecret;
|
|
97
|
+
randomInt: typeof randomInt;
|
|
98
|
+
};
|
|
99
|
+
export default _default;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { deleteCookie, getCookie } from 'hono/cookie';
|
|
2
|
+
import { HTTPException } from 'hono/http-exception';
|
|
3
|
+
import { createChallenge } from '../pow.js';
|
|
4
|
+
import { randomInt } from '../helpers.js';
|
|
5
|
+
import { CappedMap } from '../capped-map.js';
|
|
6
|
+
import { deriveHmacKeySecret, verify } from './shared.js';
|
|
7
|
+
export { CappedMap, deriveHmacKeySecret, randomInt };
|
|
8
|
+
export function create(options) {
|
|
9
|
+
const { createChallengeParameters, deriveKey, fieldName = 'altcha', hmacSignatureSecret, hmacKeySignatureSecret, setCookie, store, } = options;
|
|
10
|
+
const challengeHandler = async (c) => {
|
|
11
|
+
return c.json({
|
|
12
|
+
configuration: setCookie
|
|
13
|
+
? {
|
|
14
|
+
setCookie,
|
|
15
|
+
}
|
|
16
|
+
: undefined,
|
|
17
|
+
...(await createChallenge({
|
|
18
|
+
deriveKey,
|
|
19
|
+
hmacSignatureSecret,
|
|
20
|
+
hmacKeySignatureSecret,
|
|
21
|
+
...createChallengeParameters(),
|
|
22
|
+
})),
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const verifyHandler = async (c) => {
|
|
26
|
+
return c.json(await verify(await getPayloadFromContext(c), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store));
|
|
27
|
+
};
|
|
28
|
+
const getPayloadFromContext = async (c, cookieName) => {
|
|
29
|
+
let payload = undefined;
|
|
30
|
+
if (cookieName) {
|
|
31
|
+
payload = getCookie(c, cookieName);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
const contentType = c.req.header('content-type') ?? '';
|
|
35
|
+
let body = null;
|
|
36
|
+
if (contentType.includes('application/json')) {
|
|
37
|
+
body = await c.req.json();
|
|
38
|
+
}
|
|
39
|
+
else if (contentType.includes('multipart/form-data') ||
|
|
40
|
+
contentType.includes('application/x-www-form-urlencoded')) {
|
|
41
|
+
body = await c.req.parseBody();
|
|
42
|
+
}
|
|
43
|
+
payload = body?.[fieldName];
|
|
44
|
+
}
|
|
45
|
+
return payload;
|
|
46
|
+
};
|
|
47
|
+
const middleware = (options = {}) => {
|
|
48
|
+
const { throwOnFailure = true } = options;
|
|
49
|
+
return async (c, next) => {
|
|
50
|
+
const { error, payload, verification } = await verify(await getPayloadFromContext(c, setCookie?.name), deriveKey, hmacSignatureSecret, hmacKeySignatureSecret, store);
|
|
51
|
+
c.set('altcha', {
|
|
52
|
+
error,
|
|
53
|
+
payload,
|
|
54
|
+
verification,
|
|
55
|
+
});
|
|
56
|
+
if (setCookie) {
|
|
57
|
+
deleteCookie(c, setCookie.name);
|
|
58
|
+
}
|
|
59
|
+
if (error && throwOnFailure) {
|
|
60
|
+
throw new HTTPException(403, {
|
|
61
|
+
message: error,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return next();
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
challengeHandler,
|
|
69
|
+
verifyHandler,
|
|
70
|
+
getPayloadFromContext,
|
|
71
|
+
middleware,
|
|
72
|
+
verify,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export default {
|
|
76
|
+
CappedMap,
|
|
77
|
+
create,
|
|
78
|
+
deriveHmacKeySecret,
|
|
79
|
+
randomInt,
|
|
80
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { NestMiddleware, DynamicModule, InjectionToken, OptionalFactoryDependency, ForwardReference, Type } from '@nestjs/common';
|
|
2
|
+
import { randomInt } from '../helpers.js';
|
|
3
|
+
import { deriveHmacKeySecret } from './shared.js';
|
|
4
|
+
import { CappedMap } from '../capped-map.js';
|
|
5
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
6
|
+
import type { SetCookieOptions } from '../types.js';
|
|
7
|
+
import type { AltchaMiddlewareOptions, AltchaOptions, AltchaResult, RequireField } from './types.js';
|
|
8
|
+
export { CappedMap, deriveHmacKeySecret, randomInt };
|
|
9
|
+
export type { AltchaOptions, AltchaResult };
|
|
10
|
+
declare global {
|
|
11
|
+
namespace Express {
|
|
12
|
+
interface Request {
|
|
13
|
+
altcha?: AltchaResult;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export declare const Altcha: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
18
|
+
export declare function createAltchaMiddleware(options?: AltchaMiddlewareOptions): {
|
|
19
|
+
new (altchaService: AltchaService): {
|
|
20
|
+
readonly altchaService: AltchaService;
|
|
21
|
+
use(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export declare class AltchaService {
|
|
25
|
+
private readonly hmacSignatureSecret;
|
|
26
|
+
private readonly hmacKeySignatureSecret?;
|
|
27
|
+
private readonly createChallengeParameters;
|
|
28
|
+
private readonly deriveKey;
|
|
29
|
+
private readonly fieldName;
|
|
30
|
+
private readonly setCookieOptions?;
|
|
31
|
+
private readonly store?;
|
|
32
|
+
constructor(options: AltchaOptions);
|
|
33
|
+
get setCookie(): RequireField<SetCookieOptions, 'name'> | undefined;
|
|
34
|
+
getChallenge(): Promise<{
|
|
35
|
+
codeChallenge?: import("../types.js").CodeChallenge;
|
|
36
|
+
parameters: import("../types.js").ChallengeParameters;
|
|
37
|
+
signature?: string;
|
|
38
|
+
configuration: {
|
|
39
|
+
setCookie: RequireField<SetCookieOptions, "name">;
|
|
40
|
+
} | undefined;
|
|
41
|
+
}>;
|
|
42
|
+
getPayloadFromRequest(req: Request, cookieName?: string): string | undefined;
|
|
43
|
+
verify(payload: string | undefined): Promise<{
|
|
44
|
+
error: string | null;
|
|
45
|
+
payload: import("../types.js").Payload | import("../types.js").ServerSignaturePayload | null;
|
|
46
|
+
verification: import("../types.js").VerifySolutionResult | null;
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
export declare class AltchaController {
|
|
50
|
+
private readonly altchaService;
|
|
51
|
+
constructor(altchaService: AltchaService);
|
|
52
|
+
getChallenge(): Promise<{
|
|
53
|
+
codeChallenge?: import("../types.js").CodeChallenge;
|
|
54
|
+
parameters: import("../types.js").ChallengeParameters;
|
|
55
|
+
signature?: string;
|
|
56
|
+
configuration: {
|
|
57
|
+
setCookie: RequireField<SetCookieOptions, "name">;
|
|
58
|
+
} | undefined;
|
|
59
|
+
}>;
|
|
60
|
+
verifySolution(req: Request): Promise<{
|
|
61
|
+
error: string | null;
|
|
62
|
+
payload: import("../types.js").Payload | import("../types.js").ServerSignaturePayload | null;
|
|
63
|
+
verification: import("../types.js").VerifySolutionResult | null;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
export declare class AltchaMiddleware implements NestMiddleware {
|
|
67
|
+
private readonly altchaService;
|
|
68
|
+
constructor(altchaService: AltchaService);
|
|
69
|
+
use(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
export declare class AltchaModule {
|
|
72
|
+
static register(options: AltchaOptions): DynamicModule;
|
|
73
|
+
static registerAsync(asyncOptions: {
|
|
74
|
+
useFactory: (...args: unknown[]) => Promise<AltchaOptions> | AltchaOptions;
|
|
75
|
+
inject?: (InjectionToken | OptionalFactoryDependency)[];
|
|
76
|
+
imports?: (DynamicModule | Type<unknown> | ForwardReference<unknown> | Promise<DynamicModule>)[];
|
|
77
|
+
}): DynamicModule;
|
|
78
|
+
}
|
|
79
|
+
export default AltchaModule;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
var AltchaModule_1;
|
|
14
|
+
import { Module, Controller, Get, Post, Req, Injectable, Inject, HttpException, HttpStatus, createParamDecorator, } from '@nestjs/common';
|
|
15
|
+
import { randomInt } from '../helpers.js';
|
|
16
|
+
import { createChallenge } from '../pow.js';
|
|
17
|
+
import { deriveHmacKeySecret, verify } from './shared.js';
|
|
18
|
+
import { CappedMap } from '../capped-map.js';
|
|
19
|
+
export { CappedMap, deriveHmacKeySecret, randomInt };
|
|
20
|
+
const ALTCHA_OPTIONS = Symbol('ALTCHA_OPTIONS');
|
|
21
|
+
export const Altcha = createParamDecorator((_data, ctx) => {
|
|
22
|
+
const request = ctx.switchToHttp().getRequest();
|
|
23
|
+
return request.altcha;
|
|
24
|
+
});
|
|
25
|
+
export function createAltchaMiddleware(options = {}) {
|
|
26
|
+
const { throwOnFailure = true } = options;
|
|
27
|
+
let AltchaMiddleware = class AltchaMiddleware {
|
|
28
|
+
altchaService;
|
|
29
|
+
constructor(altchaService) {
|
|
30
|
+
this.altchaService = altchaService;
|
|
31
|
+
}
|
|
32
|
+
async use(req, res, next) {
|
|
33
|
+
const payload = this.altchaService.getPayloadFromRequest(req);
|
|
34
|
+
const { error, payload: resultPayload, verification, } = await this.altchaService.verify(payload);
|
|
35
|
+
req.altcha = {
|
|
36
|
+
error,
|
|
37
|
+
payload: resultPayload,
|
|
38
|
+
verification,
|
|
39
|
+
};
|
|
40
|
+
const setCookie = this.altchaService.setCookie;
|
|
41
|
+
if (setCookie) {
|
|
42
|
+
res.clearCookie(setCookie.name);
|
|
43
|
+
}
|
|
44
|
+
if (error && throwOnFailure) {
|
|
45
|
+
throw new HttpException(error, HttpStatus.BAD_REQUEST);
|
|
46
|
+
}
|
|
47
|
+
next();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
AltchaMiddleware = __decorate([
|
|
51
|
+
Injectable(),
|
|
52
|
+
__metadata("design:paramtypes", [AltchaService])
|
|
53
|
+
], AltchaMiddleware);
|
|
54
|
+
return AltchaMiddleware;
|
|
55
|
+
}
|
|
56
|
+
let AltchaService = class AltchaService {
|
|
57
|
+
hmacSignatureSecret;
|
|
58
|
+
hmacKeySignatureSecret;
|
|
59
|
+
createChallengeParameters;
|
|
60
|
+
deriveKey;
|
|
61
|
+
fieldName;
|
|
62
|
+
setCookieOptions;
|
|
63
|
+
store;
|
|
64
|
+
constructor(options) {
|
|
65
|
+
this.hmacSignatureSecret = options.hmacSignatureSecret;
|
|
66
|
+
this.hmacKeySignatureSecret = options.hmacKeySignatureSecret;
|
|
67
|
+
this.createChallengeParameters = options.createChallengeParameters;
|
|
68
|
+
this.deriveKey = options.deriveKey;
|
|
69
|
+
this.fieldName = options.fieldName || 'altcha';
|
|
70
|
+
this.setCookieOptions = options.setCookie;
|
|
71
|
+
this.store = options.store;
|
|
72
|
+
}
|
|
73
|
+
get setCookie() {
|
|
74
|
+
return this.setCookieOptions;
|
|
75
|
+
}
|
|
76
|
+
async getChallenge() {
|
|
77
|
+
const challenge = await createChallenge({
|
|
78
|
+
deriveKey: this.deriveKey,
|
|
79
|
+
hmacSignatureSecret: this.hmacSignatureSecret,
|
|
80
|
+
hmacKeySignatureSecret: this.hmacKeySignatureSecret,
|
|
81
|
+
...this.createChallengeParameters(),
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
configuration: this.setCookieOptions
|
|
85
|
+
? { setCookie: this.setCookieOptions }
|
|
86
|
+
: undefined,
|
|
87
|
+
...challenge,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
getPayloadFromRequest(req, cookieName) {
|
|
91
|
+
if (cookieName) {
|
|
92
|
+
return req.cookies?.[cookieName];
|
|
93
|
+
}
|
|
94
|
+
return req.body?.[this.fieldName];
|
|
95
|
+
}
|
|
96
|
+
async verify(payload) {
|
|
97
|
+
return verify(payload, this.deriveKey, this.hmacSignatureSecret, this.hmacKeySignatureSecret, this.store);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
AltchaService = __decorate([
|
|
101
|
+
Injectable(),
|
|
102
|
+
__param(0, Inject(ALTCHA_OPTIONS)),
|
|
103
|
+
__metadata("design:paramtypes", [Object])
|
|
104
|
+
], AltchaService);
|
|
105
|
+
export { AltchaService };
|
|
106
|
+
let AltchaController = class AltchaController {
|
|
107
|
+
altchaService;
|
|
108
|
+
constructor(altchaService) {
|
|
109
|
+
this.altchaService = altchaService;
|
|
110
|
+
}
|
|
111
|
+
async getChallenge() {
|
|
112
|
+
return this.altchaService.getChallenge();
|
|
113
|
+
}
|
|
114
|
+
async verifySolution(req) {
|
|
115
|
+
const payload = this.altchaService.getPayloadFromRequest(req);
|
|
116
|
+
return this.altchaService.verify(payload);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
__decorate([
|
|
120
|
+
Get('challenge'),
|
|
121
|
+
__metadata("design:type", Function),
|
|
122
|
+
__metadata("design:paramtypes", []),
|
|
123
|
+
__metadata("design:returntype", Promise)
|
|
124
|
+
], AltchaController.prototype, "getChallenge", null);
|
|
125
|
+
__decorate([
|
|
126
|
+
Post('verify'),
|
|
127
|
+
__param(0, Req()),
|
|
128
|
+
__metadata("design:type", Function),
|
|
129
|
+
__metadata("design:paramtypes", [Object]),
|
|
130
|
+
__metadata("design:returntype", Promise)
|
|
131
|
+
], AltchaController.prototype, "verifySolution", null);
|
|
132
|
+
AltchaController = __decorate([
|
|
133
|
+
Controller('altcha'),
|
|
134
|
+
__metadata("design:paramtypes", [AltchaService])
|
|
135
|
+
], AltchaController);
|
|
136
|
+
export { AltchaController };
|
|
137
|
+
let AltchaMiddleware = class AltchaMiddleware {
|
|
138
|
+
altchaService;
|
|
139
|
+
constructor(altchaService) {
|
|
140
|
+
this.altchaService = altchaService;
|
|
141
|
+
}
|
|
142
|
+
async use(req, res, next) {
|
|
143
|
+
const payload = this.altchaService.getPayloadFromRequest(req, this.altchaService.setCookie?.name);
|
|
144
|
+
const { error, payload: resultPayload, verification, } = await this.altchaService.verify(payload);
|
|
145
|
+
req.altcha = {
|
|
146
|
+
error,
|
|
147
|
+
payload: resultPayload,
|
|
148
|
+
verification,
|
|
149
|
+
};
|
|
150
|
+
const setCookie = this.altchaService.setCookie;
|
|
151
|
+
if (setCookie) {
|
|
152
|
+
res.clearCookie(setCookie.name);
|
|
153
|
+
}
|
|
154
|
+
if (error) {
|
|
155
|
+
throw new HttpException(error, HttpStatus.BAD_REQUEST);
|
|
156
|
+
}
|
|
157
|
+
next();
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
AltchaMiddleware = __decorate([
|
|
161
|
+
Injectable(),
|
|
162
|
+
__metadata("design:paramtypes", [AltchaService])
|
|
163
|
+
], AltchaMiddleware);
|
|
164
|
+
export { AltchaMiddleware };
|
|
165
|
+
let AltchaModule = AltchaModule_1 = class AltchaModule {
|
|
166
|
+
static register(options) {
|
|
167
|
+
return {
|
|
168
|
+
module: AltchaModule_1,
|
|
169
|
+
controllers: [AltchaController],
|
|
170
|
+
providers: [
|
|
171
|
+
{
|
|
172
|
+
provide: ALTCHA_OPTIONS,
|
|
173
|
+
useValue: options,
|
|
174
|
+
},
|
|
175
|
+
AltchaService,
|
|
176
|
+
],
|
|
177
|
+
exports: [AltchaService, AltchaMiddleware],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
static registerAsync(asyncOptions) {
|
|
181
|
+
return {
|
|
182
|
+
module: AltchaModule_1,
|
|
183
|
+
imports: asyncOptions.imports ?? [],
|
|
184
|
+
controllers: [AltchaController],
|
|
185
|
+
providers: [
|
|
186
|
+
{
|
|
187
|
+
provide: ALTCHA_OPTIONS,
|
|
188
|
+
useFactory: asyncOptions.useFactory,
|
|
189
|
+
inject: asyncOptions.inject ?? [],
|
|
190
|
+
},
|
|
191
|
+
AltchaService,
|
|
192
|
+
AltchaMiddleware,
|
|
193
|
+
],
|
|
194
|
+
exports: [AltchaService, AltchaMiddleware],
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
AltchaModule = AltchaModule_1 = __decorate([
|
|
199
|
+
Module({})
|
|
200
|
+
], AltchaModule);
|
|
201
|
+
export { AltchaModule };
|
|
202
|
+
export default AltchaModule;
|