aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes/(static)
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2025-01-09 23:44:40 -0800
committerAdam Soltys <[email protected]>2025-01-09 23:44:40 -0800
commit8588de25f16486b56ef0ad533013bd8cb48ff120 (patch)
tree4c1e39704e2edb332c5fe8c58fb0d81c8154af2b /src/routes/(static)
parent2358d1bf39c34a508db6c50f0f6312ee411a8a17 (diff)
downloadcoinos-ui-8588de25f16486b56ef0ad533013bd8cb48ff120.tar.gz
coinos-ui-8588de25f16486b56ef0ad533013bd8cb48ff120.zip
nostr signing
Diffstat (limited to 'src/routes/(static)')
-rw-r--r--src/routes/(static)/+layout.svelte2
-rw-r--r--src/routes/(static)/login/+page.server.ts8
-rw-r--r--src/routes/(static)/login/+page.svelte76
-rw-r--r--src/routes/(static)/register/+page.server.ts4
-rw-r--r--src/routes/(static)/register/+page.svelte46
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} />