Skip to content

Commit 9889d2c

Browse files
committed
feat: captcha expiry (wip)
1 parent d2e5ca5 commit 9889d2c

5 files changed

Lines changed: 41 additions & 18 deletions

File tree

lib/captcha.test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,28 @@ import { create, verify } from "./captcha";
22
import { LambdaCaptchaConfigManager } from "./config";
33

44
describe("create", () => {
5+
const config = LambdaCaptchaConfigManager.default("deadbeef");
6+
const captcha = create(config);
7+
58
it("returns a LambdaCaptcha instance", () => {
6-
const config = LambdaCaptchaConfigManager.default("deadbeef");
7-
const captcha = create(config);
89
expect(captcha.expr).toBeDefined();
910
expect(captcha.encryptedExpr).toBeDefined();
1011
expect(captcha.captchaSvg).toBeDefined();
1112
});
13+
14+
it('returns a LambdaCaptcha instance with a timestamp in the validUntil field', () => {
15+
// TODO: Test that timestamp is > now
16+
expect(false).toBeTruthy()
17+
})
1218
});
1319

1420
describe("verify", () => {
1521
const encryptedCaptchaExpression =
16-
"aeb5915bd712922cb2953ba81c0b05cb:21ce379b5284ff4c3e67350ec5d2ee50fdfa64e8b024bb5d647b071fc8b16e279e765e0947785ee1429df263e3a5c81c259aafb87d2d419d6004eea57e357433";
22+
"40edfed034f6b9d77c111509e3268644:fb0d5633049452a7f19fe89369a561f9cac391d0d4fe88d281b9dfff3f9893099947b31ae66a24dd024c951e26a9f9a905d09a39766f1f0b76f81de51de506cc08d433662f96467b8cec9ddfc9e0082ec53f8536ce259de53e2265158e401daf";
1723
const secret = "deadbeef";
1824

1925
it("returns true on success", () => {
20-
const result = verify(encryptedCaptchaExpression, 16, secret);
26+
const result = verify(encryptedCaptchaExpression, 6, secret);
2127

2228
expect(result).toBeTruthy();
2329
});

lib/captcha.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import { encrypt, decrypt, keyToBuffer } from './crypto'
77

88
export type ILambdaCaptcha = {
99
/**
10-
* An unencrypted string representation of the captcha
10+
* An unencrypted representation of the captcha
1111
*/
12-
expr: string
12+
expr: any
1313
/**
1414
* An unencrypted string representation of the captcha
1515
*/
@@ -18,6 +18,11 @@ export type ILambdaCaptcha = {
1818
* Captcha SVG
1919
*/
2020
captchaSvg: string
21+
22+
/**
23+
* Unix timestamp when the captcha expires (UTC)
24+
*/
25+
validUntil: number
2126
}
2227

2328
export function create(config: ILambdaCaptchaConfig): ILambdaCaptcha {
@@ -31,27 +36,39 @@ export function create(config: ILambdaCaptchaConfig): ILambdaCaptcha {
3136
throw new Error(`unknown captcha mode ${config.mode}`)
3237
}
3338

34-
const expressionJson = expression.toJSON()
39+
// TODO: Generate timestamp
40+
41+
const validUntil = 0
42+
const validationInfo = JSON.stringify({ expression: expression.toObject(), validUntil })
3543

3644
return {
37-
expr: expressionJson,
38-
encryptedExpr: encrypt(expressionJson, config.cryptoKey),
39-
captchaSvg: renderExpression(expression, config)
45+
expr: expression,
46+
encryptedExpr: encrypt(validationInfo, config.cryptoKey),
47+
captchaSvg: renderExpression(expression, config),
48+
validUntil
4049
}
4150
}
4251

4352
export function verify(
44-
encryptedExpression: string,
53+
validationInfo: string,
4554
solution: any,
4655
key: string
4756
) {
4857
try {
49-
const expressionJson = decrypt(encryptedExpression, keyToBuffer(key))
50-
const o = JSON.parse(expressionJson)
58+
const json = decrypt(validationInfo, keyToBuffer(key))
59+
const { expression: o, validUntil } = JSON.parse(json)
5160

61+
5262
switch (o.type) {
5363
case 'math':
5464
const expression = LambdaCaptchaMathExpression.fromJSON(o)
65+
66+
const currentTimestamp = Math.floor(Date.now() / 1000)
67+
if (validUntil < currentTimestamp) {
68+
console.log('got', validUntil, 'which is <', currentTimestamp)
69+
return false
70+
}
71+
5572
return expression.solve() == solution
5673
default:
5774
throw new Error(`unknown captcha type ${o.type}`)

lib/expressions/math-expression.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe("LambdaCaptchaMathExpression", () => {
4040
describe("fromJSON", () => {
4141
it("converts JSON back to a math expression", () => {
4242
const expression = new LambdaCaptchaMathExpression([5, 8, 11], ["+", "-"]);
43-
const rebuilt = LambdaCaptchaMathExpression.fromJSON(JSON.parse(expression.toJSON()))
43+
const rebuilt = LambdaCaptchaMathExpression.fromJSON(JSON.parse(JSON.stringify(expression.toObject())))
4444

4545
expect(rebuilt).toBeInstanceOf(LambdaCaptchaMathExpression)
4646
expect(rebuilt.operators).toStrictEqual(expression.operators)

lib/expressions/math-expression.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ export class LambdaCaptchaMathExpression implements ILambdaCaptchaExpression {
7575
return elements.join("");
7676
}
7777

78-
public toJSON() {
79-
return JSON.stringify({
78+
public toObject() {
79+
return {
8080
type: 'math',
8181
operands: this.operands,
8282
operators: this.operators
83-
})
83+
}
8484
}
8585
}

lib/expressions/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ import { LambdaCaptchaMathExpression } from "./math-expression";
22

33
export interface ILambdaCaptchaExpression {
44
solve(): number | string
5-
toJSON(): string
5+
toObject(): any
66
}

0 commit comments

Comments
 (0)