aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2024-12-29 11:59:02 -0800
committerAdam Soltys <[email protected]>2024-12-29 18:19:39 -0800
commit6ec41bac23d941b81b93883d00602c8ccec9ff35 (patch)
treeea597e5d9d13b6a6b457eedcdad69cf8387efc0b /src
parentd0219f1f5da2a9d98464986b9bcfb7a3c3a7300b (diff)
downloadcoinos-ui-nostr-login.tar.gz
coinos-ui-nostr-login.zip
nostr loginnostr-login
Diffstat (limited to 'src')
-rw-r--r--src/routes/(app)/[username]/(profile)/+page.svelte9
-rw-r--r--src/routes/(static)/login/+page.server.ts44
-rw-r--r--src/routes/(static)/login/+page.svelte73
3 files changed, 114 insertions, 12 deletions
diff --git a/src/routes/(app)/[username]/(profile)/+page.svelte b/src/routes/(app)/[username]/(profile)/+page.svelte
index 05273bc5..53a994ad 100644
--- a/src/routes/(app)/[username]/(profile)/+page.svelte
+++ b/src/routes/(app)/[username]/(profile)/+page.svelte
@@ -1,4 +1,6 @@
<script>
+ import { browser } from "$app/environment";
+ import { onMount } from "svelte";
import { scale } from "svelte/transition";
import { btc, f, sat } from "$lib/utils";
import Account from "$comp/Account.svelte";
@@ -23,6 +25,13 @@
};
if (user) user.savings = 0;
+
+ let pk = $state();;
+ onMount(async () => {
+ if (browser) {
+ pk = await window.nostr.getPublicKey();
+ }
+ });
</script>
<div class="space-y-2">
diff --git a/src/routes/(static)/login/+page.server.ts b/src/routes/(static)/login/+page.server.ts
index 6e268c92..740f02d8 100644
--- a/src/routes/(static)/login/+page.server.ts
+++ b/src/routes/(static)/login/+page.server.ts
@@ -1,13 +1,15 @@
-import { fd, login } from "$lib/utils";
+import { fd, get, login } from "$lib/utils";
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 };
};
export const actions = {
- default: async ({ cookies, request }) => {
+ login: async ({ cookies, request }) => {
const form = await fd(request);
let { username, password, token, loginRedirect } = form;
const user = { username, password, token };
@@ -23,4 +25,42 @@ export const actions = {
redirect(307, loginRedirect || `/${user.username}`);
},
+
+ nostr: async ({ cookies, fetch, request }) => {
+ const form = await fd(request);
+ let { id, event, loginRedirect } = form;
+ if (loginRedirect === "null") loginRedirect = undefined;
+ event = JSON.parse(event);
+
+ const maxAge = 380 * 24 * 60 * 60;
+
+ const res = await fetch("/api/nostrLogin", {
+ method: "POST",
+ body: JSON.stringify({ id, event }),
+ headers: {
+ "content-type": "application/json",
+ accept: "application/json",
+ "cf-connecting-ip": request.headers.get("cf-connecting-ip"),
+ },
+ });
+
+ if (res.status === 401) {
+ const text = await res.text();
+ if (text.startsWith("2fa")) throw new Error("2fa");
+ }
+
+ const { user, token } = await res.json();
+ if (!token) throw new Error("Login failed");
+ const { username, language } = user;
+
+ const expires = new Date();
+ expires.setSeconds(expires.getSeconds() + maxAge);
+
+ const opts = { path: "/", expires };
+ if (language) cookies.set("lang", language, opts);
+ cookies.set("username", username, opts);
+ cookies.set("token", token, opts);
+
+ redirect(307, loginRedirect || `/${username}`);
+ },
};
diff --git a/src/routes/(static)/login/+page.svelte b/src/routes/(static)/login/+page.svelte
index 5caf48fb..b49ba036 100644
--- a/src/routes/(static)/login/+page.svelte
+++ b/src/routes/(static)/login/+page.svelte
@@ -1,5 +1,5 @@
<script>
- import { run, preventDefault } from "svelte/legacy";
+ import { browser } from "$app/environment";
import handler from "$lib/handler";
import { onMount } from "svelte";
@@ -16,7 +16,9 @@
import { generate } from "$lib/nostr";
import { invalidateAll } from "$app/navigation";
+ let nostr = $state();
onMount(() => {
+ if (browser && window.nostr) nostr = true;
$pin = undefined;
localStorage.clear();
sessionStorage.clear();
@@ -25,11 +27,11 @@
if (lang) document.cookie = `lang=${lang} ;`;
});
- let { form } = $props();
+ let { data, form } = $props();
+ let { id } = $derived(data);
let token = $state("");
let code = [];
- let redirect;
$password = undefined;
@@ -48,6 +50,7 @@
let revealPassword = $state(false);
async function handleSubmit(e) {
+ e.preventDefault();
let data = new FormData(this);
let user = Object.fromEntries(data);
@@ -55,7 +58,7 @@
data.set(k, user[k]);
}
- const response = await fetch("/login", {
+ const response = await fetch("?/login", {
method: "POST",
body: data,
});
@@ -75,6 +78,47 @@
btn.click();
});
});
+
+ let nostrLogin = async () => {
+ let pubkey = await window.nostr.getPublicKey();
+ const formData = new FormData();
+
+ let ev = {
+ kind: 1,
+ pubkey,
+ created_at: Date.now(),
+ content: id,
+ tags: [],
+ };
+
+ try {
+ let signedEvent = await window.nostr.signEvent(ev);
+
+ formData.append("loginRedirect", redirect);
+ formData.append("token", token);
+ formData.append("event", JSON.stringify(signedEvent));
+ formData.append("id", id);
+
+ let response = await fetch("?/nostr", {
+ method: "POST",
+ body: formData,
+ });
+
+ const result = deserialize(await response.text());
+
+ if (result.type === "success") {
+ await invalidateAll();
+ }
+
+ applyAction(result);
+ } catch (e) {
+ fail(e.message);
+ }
+ };
+
+ let redirect = $derived(
+ $loginRedirect || $page.url.searchParams.get("redirect"),
+ );
</script>
<div
@@ -88,12 +132,14 @@
</div>
{/if}
- <form use:enhance class="space-y-5" onsubmit={handleSubmit} method="POST">
- <input
- type="hidden"
- name="loginRedirect"
- value={$loginRedirect || $page.url.searchParams.get("redirect")}
- />
+ <form
+ use:enhance
+ class="space-y-5"
+ onsubmit={handleSubmit}
+ method="POST"
+ action="?/login"
+ >
+ <input type="hidden" name="loginRedirect" value={redirect} />
<input type="hidden" name="token" value={token} />
<input
@@ -118,6 +164,13 @@
{$t("login.signIn")}
</button>
+ {#if nostr}
+ <button type="button" class="btn" onclick={nostrLogin}>
+ <img src="/images/nostr.png" class="w-8" />
+ <div class="my-auto">{$t("user.settings.nostr")}</div>
+ </button>
+ {/if}
+
<p class="text-secondary text-center font-medium">
{$t("login.noAccount")}
<a