diff options
| author | Adam Soltys <[email protected]> | 2025-01-09 23:44:40 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2025-01-09 23:44:40 -0800 |
| commit | 8588de25f16486b56ef0ad533013bd8cb48ff120 (patch) | |
| tree | 4c1e39704e2edb332c5fe8c58fb0d81c8154af2b /src/routes/(static) | |
| parent | 2358d1bf39c34a508db6c50f0f6312ee411a8a17 (diff) | |
| download | coinos-ui-8588de25f16486b56ef0ad533013bd8cb48ff120.tar.gz coinos-ui-8588de25f16486b56ef0ad533013bd8cb48ff120.zip | |
nostr signing
Diffstat (limited to 'src/routes/(static)')
| -rw-r--r-- | src/routes/(static)/+layout.svelte | 2 | ||||
| -rw-r--r-- | src/routes/(static)/login/+page.server.ts | 8 | ||||
| -rw-r--r-- | src/routes/(static)/login/+page.svelte | 76 | ||||
| -rw-r--r-- | src/routes/(static)/register/+page.server.ts | 4 | ||||
| -rw-r--r-- | src/routes/(static)/register/+page.svelte | 46 |
5 files changed, 94 insertions, 42 deletions
diff --git a/src/routes/(static)/+layout.svelte b/src/routes/(static)/+layout.svelte index 44310c97..a0061a19 100644 --- a/src/routes/(static)/+layout.svelte +++ b/src/routes/(static)/+layout.svelte @@ -1,6 +1,7 @@ <script> import { run } from 'svelte/legacy'; + import Nostr from "$comp/Nostr.svelte"; import { SvelteToast } from "@zerodevx/svelte-toast"; import Footer from "$comp/Footer.svelte"; import LandingHeader from "$comp/LandingHeader.svelte"; @@ -16,6 +17,7 @@ <SvelteToast options={{ reversed: true, intro: { y: 192 } }} /> <LandingHeader {user} /> +<Nostr /> <main class="container flex px-4 md:px-40 mx-auto min-h-[600px]" data-theme={theme} diff --git a/src/routes/(static)/login/+page.server.ts b/src/routes/(static)/login/+page.server.ts index 6bf06ad2..f86f62e9 100644 --- a/src/routes/(static)/login/+page.server.ts +++ b/src/routes/(static)/login/+page.server.ts @@ -5,8 +5,8 @@ import { fail, redirect } from "@sveltejs/kit"; export const load = async ({ parent }) => { const { user } = await parent(); if (user?.pubkey) redirect(307, `/${user.username}`); - const { id } = await get("/challenge"); - return { id }; + const { challenge } = await get("/challenge"); + return { challenge }; }; export const actions = { @@ -29,7 +29,7 @@ export const actions = { nostr: async ({ cookies, fetch, request }) => { const form = await fd(request); - let { id, event, loginRedirect } = form; + let { challenge, event, loginRedirect } = form; if (loginRedirect === "null") loginRedirect = undefined; event = JSON.parse(event); @@ -37,7 +37,7 @@ export const actions = { const res = await fetch(`${PUBLIC_COINOS_URL}/nostrLogin`, { method: "POST", - body: JSON.stringify({ id, event }), + body: JSON.stringify({ challenge, event }), 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 7e8234ee..e4dcbf15 100644 --- a/src/routes/(static)/login/+page.svelte +++ b/src/routes/(static)/login/+page.svelte @@ -1,39 +1,42 @@ <script> import { browser } from "$app/environment"; import handler from "$lib/handler"; - - import { onMount } from "svelte"; import { applyAction, deserialize } from "$app/forms"; - import { tick } from "svelte"; + import { onMount, tick } from "svelte"; import { fly } from "svelte/transition"; import { enhance } from "$app/forms"; - import Nostr from "$comp/Nostr.svelte"; import Pinpad from "$comp/Pinpad.svelte"; import PasswordInput from "$comp/PasswordInput.svelte"; + import Spinner from "$comp/Spinner.svelte"; import { focus, fail } from "$lib/utils"; - import { password, pin, loginRedirect } from "$lib/store"; + import { password, signer, pin, loginRedirect } from "$lib/store"; import { t } from "$lib/translations"; import { page } from "$app/stores"; + import { sign } from "$lib/nostr"; import { invalidateAll } from "$app/navigation"; let { data, form } = $props(); - onMount(() => { - $pin = undefined; - localStorage.clear(); - sessionStorage.clear(); + page.subscribe(async () => { + if (browser) { + setTimeout(() => { + password.set(undefined); + pin?.set(undefined); + signer.set(undefined); + localStorage.clear(); + sessionStorage.clear(); + }, 500); + } + }); + onMount(() => { let lang = $page.url.searchParams.get("lang"); if (lang) document.cookie = `lang=${lang} ;`; }); - let { id } = $derived(data); - let nostrSignin = $state(); - + let { challenge } = $derived(data); let token = $state(""); - $password = undefined; - let cancel = () => (need2fa = false); let username = $state(), @@ -81,13 +84,43 @@ let redirect = $derived( $loginRedirect || $page.url.searchParams.get("redirect"), ); + + let nostrLogin = async () => { + let event = { + kind: 1, + created_at: Date.now(), + content: challenge, + tags: [], + }; + + let signedEvent = await sign(event); + + const formData = new FormData(); + + try { + formData.append("loginRedirect", redirect); + formData.append("token", token); + formData.append("event", JSON.stringify(signedEvent)); + formData.append("challenge", challenge); + + let response = await fetch("/login?/nostr", { + method: "POST", + body: formData, + }); + + const result = deserialize(await response.text()); + + applyAction(result); + } catch (e) { + fail(e.message); + } + }; </script> <div class="mx-auto md:shadow-xl rounded-3xl max-w-xl w-full md:w-[480px] md:p-8 mb-20" > <h1 class="text-2xl font-bold text-center">{$t("login.signIn")}</h1> - {#if form?.error && !form?.message.includes("2fa")} <div class="text-red-600 text-center" in:fly> {form.error} @@ -126,10 +159,17 @@ {$t("login.signIn")} </button> - <button type="button" class="btn" onclick={() => (nostrSignin = true)}> - <img src="/images/nostr.png" class="w-8" /> + <button type="button" class="btn" onclick={nostrLogin}> + {#if $signer?.ready} + <div class="shrink"> + <Spinner /> + </div> + {:else} + <img src="/images/nostr.png" class="w-8" /> + {/if} <div class="my-auto">{$t("login.nostr")}</div> </button> + <p class="text-secondary text-center font-medium"> {$t("login.noAccount")} <a @@ -163,5 +203,3 @@ </div> </div> {/if} - -<Nostr {id} bind:nostrSignin {redirect} {token} /> diff --git a/src/routes/(static)/register/+page.server.ts b/src/routes/(static)/register/+page.server.ts index bf908c54..2e5c5f19 100644 --- a/src/routes/(static)/register/+page.server.ts +++ b/src/routes/(static)/register/+page.server.ts @@ -6,9 +6,9 @@ export const load = async ({ parent }) => { if (user) redirect(307, `/${user.username}`); const index = Math.floor(Math.random() * 64) + 1; - const { id } = await get("/challenge"); + const { challenge } = await get("/challenge"); - return { index, id }; + return { index, challenge }; }; export const actions = { diff --git a/src/routes/(static)/register/+page.svelte b/src/routes/(static)/register/+page.svelte index 77394e1a..e7819f4f 100644 --- a/src/routes/(static)/register/+page.svelte +++ b/src/routes/(static)/register/+page.svelte @@ -8,13 +8,12 @@ import { fly } from "svelte/transition"; import { enhance } from "$app/forms"; - import Nostr from "$comp/Nostr.svelte"; import Pin from "$comp/Pin.svelte"; import Spinner from "$comp/Spinner.svelte"; import PasswordInput from "$comp/PasswordInput.svelte"; import { focus, fail } from "$lib/utils"; - import { avatar, password, loginRedirect } from "$lib/store"; + import { avatar, signer, password, loginRedirect } from "$lib/store"; import { t } from "$lib/translations"; import { page } from "$app/stores"; import { @@ -24,11 +23,22 @@ adjectives, animals, } from "unique-names-generator"; + import { sign } from "$lib/nostr"; let { form, data } = $props(); - let { id } = $derived(data); + let { challenge } = $derived(data); - let nostrSignin = $state(); + page.subscribe(async () => { + if (browser) { + setTimeout(() => { + password.set(undefined); + pin.set(undefined); + signer.set(undefined); + localStorage.clear(); + sessionStorage.clear(); + }, 500); + } + }); let username = $state(); let { index } = $state(data); @@ -161,24 +171,22 @@ }); let nostrLogin = async () => { - let pubkey = await window.nostr.getPublicKey(); - const formData = new FormData(); - - let ev = { + let event = { kind: 1, - pubkey, created_at: Date.now(), - content: id, + content: challenge, tags: [], }; - try { - let signedEvent = await window.nostr.signEvent(ev); + let signedEvent = await sign(event); + const formData = new FormData(); + + try { formData.append("loginRedirect", redirect); formData.append("token", token); formData.append("event", JSON.stringify(signedEvent)); - formData.append("id", id); + formData.append("challenge", challenge); let response = await fetch("/login?/nostr", { method: "POST", @@ -309,8 +317,14 @@ {/if} </button> - <button type="button" class="btn" onclick={() => (nostrSignin = true)}> - <img src="/images/nostr.png" class="w-8" /> + <button type="button" class="btn" onclick={nostrLogin}> + {#if $signer?.ready} + <div class="shrink"> + <Spinner /> + </div> + {:else} + <img src="/images/nostr.png" class="w-8" /> + {/if} <div class="my-auto">{$t("login.nostr")}</div> </button> @@ -325,5 +339,3 @@ </p> </form> </div> - -<Nostr {id} {nostrSignin} {redirect} {token} /> |
