diff options
Diffstat (limited to 'src/routes/(static)')
| -rw-r--r-- | src/routes/(static)/login/+page.server.ts | 10 | ||||
| -rw-r--r-- | src/routes/(static)/login/+page.svelte | 47 | ||||
| -rw-r--r-- | src/routes/(static)/register/+page.server.ts | 4 | ||||
| -rw-r--r-- | src/routes/(static)/register/+page.svelte | 48 |
4 files changed, 101 insertions, 8 deletions
diff --git a/src/routes/(static)/login/+page.server.ts b/src/routes/(static)/login/+page.server.ts index dc68879a..dacb94b2 100644 --- a/src/routes/(static)/login/+page.server.ts +++ b/src/routes/(static)/login/+page.server.ts @@ -12,13 +12,15 @@ export const load = async ({ parent }) => { export const actions = { login: async ({ cookies, request }) => { const form = await fd(request); - let { username, password, token, loginRedirect, challenge } = form; + let { username, password, token, loginRedirect, challenge, recaptcha } = + form; const user = { - challenge, + challenge, username, password, token, + recaptcha, }; if (loginRedirect === "undefined") loginRedirect = undefined; @@ -35,7 +37,7 @@ export const actions = { nostr: async ({ cookies, fetch, request }) => { const form = await fd(request); - let { challenge, event, loginRedirect } = form; + let { challenge, event, loginRedirect, recaptcha } = form; if (loginRedirect === "null") loginRedirect = undefined; event = JSON.parse(event); @@ -43,7 +45,7 @@ export const actions = { const res = await fetch(`${PUBLIC_COINOS_URL}/nostrAuth`, { method: "POST", - body: JSON.stringify({ challenge, event }), + body: JSON.stringify({ challenge, event, recaptcha }), headers: { "content-type": "application/json", accept: "application/json", diff --git a/src/routes/(static)/login/+page.svelte b/src/routes/(static)/login/+page.svelte index 450425f6..08656949 100644 --- a/src/routes/(static)/login/+page.svelte +++ b/src/routes/(static)/login/+page.svelte @@ -2,7 +2,7 @@ import { browser } from "$app/environment"; import handler from "$lib/handler"; import { applyAction, deserialize } from "$app/forms"; - import { onMount, tick } from "svelte"; + import { onDestroy, onMount, tick } from "svelte"; import { fly } from "svelte/transition"; import { enhance } from "$app/forms"; import Pinpad from "$comp/Pinpad.svelte"; @@ -37,6 +37,7 @@ }); let { challenge } = $derived(data); + let recaptchaSiteKey = "6LfCd8YkAAAAANmVJgzN3SQY3n3fv1RhiS5PgMYM"; let token = $state(""); let cancel = () => (need2fa = false); @@ -53,6 +54,17 @@ let revealPassword = $state(false); + const getRecaptchaToken = () => + new Promise((resolve, reject) => { + if (!browser || !grecaptcha) return reject(new Error("captcha unavailable")); + grecaptcha.ready(() => { + grecaptcha + .execute(recaptchaSiteKey, { action: "login" }) + .then(resolve) + .catch(reject); + }); + }); + async function handleSubmit(e) { e.preventDefault(); let data = new FormData(this); @@ -62,6 +74,14 @@ data.set(k, user[k]); } + try { + const recaptcha = await getRecaptchaToken(); + data.set("recaptcha", recaptcha); + } catch (err) { + fail(err.message || "captcha failed"); + return; + } + const response = await fetch("?/login", { method: "POST", body: data, @@ -104,10 +124,12 @@ const formData = new FormData(); try { + const recaptcha = await getRecaptchaToken(); formData.append("loginRedirect", redirect); formData.append("token", token); formData.append("event", JSON.stringify(signedEvent)); formData.append("challenge", challenge); + formData.append("recaptcha", recaptcha); let response = await fetch("/login?/nostr", { method: "POST", @@ -121,8 +143,31 @@ fail(e.message); } }; + + onDestroy(() => { + if (!browser) return; + const nodeBadge = document.querySelector(".grecaptcha-badge"); + if (nodeBadge) { + document.body.removeChild(nodeBadge.parentNode); + } + + const scriptSelector = + "script[src='https://www.google.com/recaptcha/api.js?render=" + + recaptchaSiteKey + + "']"; + const script = document.querySelector(scriptSelector); + if (script) { + script.remove(); + } + }); </script> +<svelte:head + ><script + src="https://www.google.com/recaptcha/api.js?render=6LfCd8YkAAAAANmVJgzN3SQY3n3fv1RhiS5PgMYM" + ></script></svelte:head +> + <div class="mx-auto md:shadow-xl rounded-3xl max-w-xl w-full md:w-[480px] md:p-8 mb-20 space-y-5" > diff --git a/src/routes/(static)/register/+page.server.ts b/src/routes/(static)/register/+page.server.ts index 47ff8d27..d2dbe299 100644 --- a/src/routes/(static)/register/+page.server.ts +++ b/src/routes/(static)/register/+page.server.ts @@ -15,11 +15,11 @@ export const actions = { default: async ({ cookies, request }) => { const ip = request.headers.get("cf-connecting-ip"); const form = await fd(request); - const { picture, username, password, challenge } = form; + const { picture, username, password, challenge, recaptcha } = form; let { loginRedirect } = form; if (loginRedirect === "undefined") loginRedirect = undefined; - const user = { picture, username, password, challenge }; + const user = { picture, username, password, challenge, recaptcha }; return register(user, ip, cookies, loginRedirect); }, }; diff --git a/src/routes/(static)/register/+page.svelte b/src/routes/(static)/register/+page.svelte index 9e255b8e..f1ac582b 100644 --- a/src/routes/(static)/register/+page.svelte +++ b/src/routes/(static)/register/+page.svelte @@ -1,5 +1,5 @@ <script> - import { onMount } from "svelte"; + import { onDestroy, onMount } from "svelte"; import { browser } from "$app/environment"; import punks from "$lib/punks"; import { upload } from "$lib/upload"; @@ -28,6 +28,7 @@ let { form, data } = $props(); let { challenge } = $derived(data); + let recaptchaSiteKey = "6LfCd8YkAAAAANmVJgzN3SQY3n3fv1RhiS5PgMYM"; onMount(() => { if (browser) { @@ -93,6 +94,17 @@ btn = $state(); let loading = $state(); + const getRecaptchaToken = () => + new Promise((resolve, reject) => { + if (!browser || !grecaptcha) return reject(new Error("captcha unavailable")); + grecaptcha.ready(() => { + grecaptcha + .execute(recaptchaSiteKey, { action: "register" }) + .then(resolve) + .catch(reject); + }); + }); + async function handleSubmit(e) { e.preventDefault(); @@ -106,6 +118,15 @@ data.set(k, user[k]); } + try { + const recaptcha = await getRecaptchaToken(); + data.set("recaptcha", recaptcha); + } catch (err) { + fail(err.message || "captcha failed"); + loading = false; + return; + } + data.set("picture", `${$page.url.origin}/api/public/${punks[index]}.webp`); if ($avatar) { try { @@ -189,10 +210,12 @@ const formData = new FormData(); try { + const recaptcha = await getRecaptchaToken(); formData.append("loginRedirect", redirect); formData.append("token", token); formData.append("event", JSON.stringify(signedEvent)); formData.append("challenge", challenge); + formData.append("recaptcha", recaptcha); let response = await fetch("/login?/nostr", { method: "POST", @@ -210,8 +233,31 @@ fail(e.message); } }; + + onDestroy(() => { + if (!browser) return; + const nodeBadge = document.querySelector(".grecaptcha-badge"); + if (nodeBadge) { + document.body.removeChild(nodeBadge.parentNode); + } + + const scriptSelector = + "script[src='https://www.google.com/recaptcha/api.js?render=" + + recaptchaSiteKey + + "']"; + const script = document.querySelector(scriptSelector); + if (script) { + script.remove(); + } + }); </script> +<svelte:head + ><script + src="https://www.google.com/recaptcha/api.js?render=6LfCd8YkAAAAANmVJgzN3SQY3n3fv1RhiS5PgMYM" + ></script></svelte:head +> + {#if need2fa} <Pin bind:value={token} title="Enter 2FA Code" {cancel} notify={false} /> {/if} |
