update: returns JWT in the /captcha/:id/result endpoint

This commit is contained in:
alikia2x (寒寒) 2025-05-11 19:40:24 +08:00
parent a063f2401b
commit 1633e56b1e
Signed by: alikia2x
GPG Key ID: 56209E0CCD8420C6
6 changed files with 76 additions and 13 deletions

View File

@ -19,6 +19,7 @@
"hono": "^4.7.8", "hono": "^4.7.8",
"hono-rate-limiter": "^0.4.2", "hono-rate-limiter": "^0.4.2",
"ioredis": "^5.6.1", "ioredis": "^5.6.1",
"jose": "^6.0.11",
"limiter": "^3.0.0", "limiter": "^3.0.0",
"postgres": "^3.4.5", "postgres": "^3.4.5",
"rate-limit-redis": "^4.2.0", "rate-limit-redis": "^4.2.0",
@ -814,6 +815,8 @@
"jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
"jose": ["jose@6.0.11", "", {}, "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg=="],
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
"json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="],

View File

@ -10,6 +10,7 @@
"hono": "^4.7.8", "hono": "^4.7.8",
"hono-rate-limiter": "^0.4.2", "hono-rate-limiter": "^0.4.2",
"ioredis": "^5.6.1", "ioredis": "^5.6.1",
"jose": "^6.0.11",
"limiter": "^3.0.0", "limiter": "^3.0.0",
"postgres": "^3.4.5", "postgres": "^3.4.5",
"rate-limit-redis": "^4.2.0", "rate-limit-redis": "^4.2.0",

View File

@ -2,6 +2,12 @@ import { Context } from "hono";
import { Bindings, BlankEnv, BlankInput } from "hono/types"; import { Bindings, BlankEnv, BlankInput } from "hono/types";
import { ErrorResponse } from "src/schema"; import { ErrorResponse } from "src/schema";
import { createHandlers } from "src/utils.ts"; import { createHandlers } from "src/utils.ts";
import * as jose from "jose";
interface CaptchaResponse {
success: boolean;
error?: string;
}
const getChallengeVerificationResult = async (id: string, ans: string) => { const getChallengeVerificationResult = async (id: string, ans: string) => {
const baseURL = process.env["UCAPTCHA_URL"]; const baseURL = process.env["UCAPTCHA_URL"];
@ -19,7 +25,6 @@ const getChallengeVerificationResult = async (id: string, ans: string) => {
return res; return res;
}; };
export const verifyChallengeHandler = createHandlers( export const verifyChallengeHandler = createHandlers(
async (c: Context<BlankEnv & { Bindings: Bindings }, "/captcha/:id/result", BlankInput>) => { async (c: Context<BlankEnv & { Bindings: Bindings }, "/captcha/:id/result", BlankInput>) => {
const id = c.req.param("id"); const id = c.req.param("id");
@ -32,6 +37,51 @@ export const verifyChallengeHandler = createHandlers(
return c.json<ErrorResponse<string>>(response, 400); return c.json<ErrorResponse<string>>(response, 400);
} }
const res = await getChallengeVerificationResult(id, ans); const res = await getChallengeVerificationResult(id, ans);
return res; const data: CaptchaResponse = await res.json();
if (data.error && res.status === 404) {
const response: ErrorResponse<string> = {
message: data.error,
code: "ENTITY_NOT_FOUND"
};
return c.json<ErrorResponse<string>>(response, 401);
} else if (data.error && res.status === 400) {
const response: ErrorResponse<string> = {
message: data.error,
code: "INVALID_QUERY_PARAMS"
};
return c.json<ErrorResponse<string>>(response, 400);
} else if (data.error) {
const response: ErrorResponse<string> = {
message: data.error,
code: "UNKNOWN_ERROR"
};
return c.json<ErrorResponse<string>>(response, 500);
}
if (!data.success) {
const response: ErrorResponse<string> = {
message: "Incorrect answer",
code: "INVALID_CREDENTIALS"
};
return c.json<ErrorResponse<string>>(response, 401);
}
const secret = process.env["JWT_SECRET"];
if (!secret) {
const response: ErrorResponse<string> = {
message: "JWT_SECRET is not set",
code: "SERVER_ERROR"
};
return c.json<ErrorResponse<string>>(response, 500);
}
const jwtSecret = new TextEncoder().encode(secret);
const alg = "HS256";
const jwt = await new jose.SignJWT()
.setProtectedHeader({ alg })
.setIssuedAt()
.sign(jwtSecret);
return c.json({
token: jwt
});
} }
); );

View File

@ -66,7 +66,7 @@ export const registerHandler = createHandlers(async (c: ContextType) => {
const response: ErrorResponse<string> = { const response: ErrorResponse<string> = {
message: "Invalid JSON payload.", message: "Invalid JSON payload.",
errors: [(e as Error).message], errors: [(e as Error).message],
code: "UNKNOWN_ERR" code: "UNKNOWN_ERROR"
}; };
return c.json<ErrorResponse<string>>(response, 500); return c.json<ErrorResponse<string>>(response, 500);
} }

View File

@ -96,7 +96,7 @@ export const getSnapshotsHanlder = createHandlers(async (c: ContextType) => {
return c.json<ErrorResponse<string>>(response, 400); return c.json<ErrorResponse<string>>(response, 400);
} else { } else {
const response: ErrorResponse<unknown> = { const response: ErrorResponse<unknown> = {
code: "UNKNOWN_ERR", code: "UNKNOWN_ERROR",
message: "Unhandled error", message: "Unhandled error",
errors: [e] errors: [e]
}; };

View File

@ -1,4 +1,13 @@
type ErrorCode = "INVALID_QUERY_PARAMS" | "UNKNOWN_ERR" | "INVALID_PAYLOAD" | "INVALID_FORMAT" | "BODY_TOO_LARGE"; type ErrorCode =
| "INVALID_QUERY_PARAMS"
| "UNKNOWN_ERROR"
| "INVALID_PAYLOAD"
| "INVALID_FORMAT"
| "BODY_TOO_LARGE"
| "UNAUTHORIZED"
| "INVALID_CREDENTIALS"
| "ENTITY_NOT_FOUND"
| "SERVER_ERROR";
export interface ErrorResponse<E> { export interface ErrorResponse<E> {
code: ErrorCode; code: ErrorCode;