Skip to content

Commit

Permalink
feat: add captcha validation on session creation
Browse files Browse the repository at this point in the history
  • Loading branch information
yjose committed Sep 19, 2024
1 parent 147dd05 commit d0cf3e8
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 8 deletions.
27 changes: 23 additions & 4 deletions src/actions/init-session.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,40 @@
import { app } from "@/firebase/server";
import { app } from "@/lib/firebase/server";
import { defineAction } from "astro:actions";
import { getAuth } from "firebase-admin/auth";
import { z } from "astro:schema";
import { initUserSubmission } from "@/firebase/database";
import { initUserSubmission } from "@/lib/firebase/database";
import { isCaptchaValid } from "@/lib/captcha";

export const initSession = defineAction({
accept: "json",
input: z.object({
idToken: z.string()
idToken: z.string(),
captchaToken: z.string().optional()
}),
handler: async ({ idToken }, { cookies }) => {
handler: async ({ idToken, captchaToken }, { cookies }) => {
const auth = getAuth(app);
/* Validate inputs */
if (!captchaToken && import.meta.env.CAPTCHA_ENABLED === "true") {
return {
error: "Captcha token is required"
};
}
if (!idToken) {
return {
error: "No idToken provided"
};
}

/* Validate captcha */
if (captchaToken && import.meta.env.CAPTCHA_ENABLED === "true") {
const isValid = await isCaptchaValid(captchaToken);
if (!isValid) {
return {
error: "Invalid captcha"
};
}
}

/* Verify id token and save user to database */
try {
const decodedToken = await auth.verifyIdToken(idToken);
Expand Down
4 changes: 2 additions & 2 deletions src/actions/submit-answers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { app } from "@/firebase/server";
import { app } from "@/lib/firebase/server";
import { defineAction } from "astro:actions";
import { getAuth } from "firebase-admin/auth";
import { z } from "astro:schema";
import { saveAnswers } from "@/firebase/database";
import { saveAnswers } from "@/lib/firebase/database";

export const submitAnswers = defineAction({
accept: "json",
Expand Down
3 changes: 1 addition & 2 deletions src/components/survey/section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export const ERRORS = {
const convertAnswersToNumber = (
answers: Record<string, string | string[] | null | boolean>
) => {
console.log(answers);
const convertedAnswers: Record<string, number | number[] | null> = {};

for (const [key, value] of Object.entries(answers)) {
Expand Down Expand Up @@ -131,7 +130,7 @@ export default React.memo(({ section, next, setProgress }: SectionProps) => {
<button
data-testid="next-button"
type="button"
className="px-4 py-2 bg-emerald-500 text-white rounded transition hover:bg-emerald-600"
className="px-4 py-2 min-w-[120px] bg-emerald-500 text-white rounded transition hover:bg-emerald-600"
onClick={() => nextQuestion()}
>
{loading ? "Loading..." : "Next"}
Expand Down
25 changes: 25 additions & 0 deletions src/lib/captcha.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const CLOUDFLARE_CAPTCHA_URL =
"https://challenges.cloudflare.com/turnstile/v0/siteverify";

export const isCaptchaValid = async (token: string) => {
if (import.meta.env.CAPTCHA_ENABLED === "false") return true;
if (!import.meta.env.PUBLIC_TURNSTILE_SECRET_KEY) return true;
try {
const response = await fetch(CLOUDFLARE_CAPTCHA_URL, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: JSON.stringify({
secret: import.meta.env.PUBLIC_TURNSTILE_SECRET_KEY,
response: token
})
});

const data = await response.json();
return data.success;
} catch (error) {
console.error("Error validating captcha:", error);
return false;
}
};
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit d0cf3e8

Please sign in to comment.