diff options
| author | Adam Soltys <[email protected]> | 2022-12-24 05:22:50 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2022-12-24 05:22:50 -0800 |
| commit | 10c20c0c5b130c389680e37e4e4aba8e2abdbf1e (patch) | |
| tree | 6862c7f94c899b3dad177b16698c08abc678eefb /src | |
| parent | e1a34bbc86600c6dfb782ac936030379deb6365a (diff) | |
| download | coinos-ui-10c20c0c5b130c389680e37e4e4aba8e2abdbf1e.tar.gz coinos-ui-10c20c0c5b130c389680e37e4e4aba8e2abdbf1e.zip | |
password prompt and pin fixes
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/Feed.svelte | 74 | ||||
| -rw-r--r-- | src/components/Password.svelte | 77 | ||||
| -rw-r--r-- | src/components/Pin.svelte | 3 | ||||
| -rw-r--r-- | src/components/index.js | 1 | ||||
| -rw-r--r-- | src/lib/nostr.js | 30 | ||||
| -rw-r--r-- | src/lib/store.js | 1 | ||||
| -rw-r--r-- | src/lib/utils.js | 5 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/generate/+page.svelte | 6 | ||||
| -rw-r--r-- | src/routes/(app)/[username]/settings/[...tab]/+page.svelte | 8 | ||||
| -rw-r--r-- | src/routes/(app)/login/+server.js | 6 | ||||
| -rw-r--r-- | src/routes/(app)/password/+server.js | 8 |
11 files changed, 173 insertions, 46 deletions
diff --git a/src/components/Feed.svelte b/src/components/Feed.svelte index 00226692..0ccda7b8 100644 --- a/src/components/Feed.svelte +++ b/src/components/Feed.svelte @@ -1,55 +1,51 @@ <script> + import { onMount } from 'svelte'; import { Avatar } from '$comp'; import { browser } from '$app/environment'; import VirtualScroll from 'svelte-virtual-scroll-list'; - import { punk } from '$lib/utils'; - import { Buffer } from 'buffer'; - import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39'; import { enhance } from '$app/forms'; - import { bech32m } from 'bech32'; - import { calculateId, signId } from 'nostr'; - import { bip32, stretch, post } from '$lib/utils'; + import { post, punk, failure } from '$lib/utils'; + import { Password } from '$comp'; + import { password, passwordPrompt } from '$lib/store'; + import { send } from '$lib/nostr'; export let events; export let user; - const { decode, fromWords } = bech32m; - - let message; + let message, submitting; + $: submitting && $password && submit(); let submit = async () => { - let mnemonic, key, password, seed, entropy, child, privkey; - entropy = Buffer.from( - await crypto.subtle.decrypt( - { name: 'AES-GCM', iv: new Uint8Array(16) }, - await stretch(password, Buffer.from(user.salt, 'hex')), - Uint8Array.from(fromWords(decode(user.cipher, 180).words)) - ), - 'hex' - ).toString('hex'); + submitting = true; + try { + let mnemonic, key, seed, entropy, child, privkey; + if (!$password) return ($passwordPrompt = true); + + try { + await post('/password', { password: $password }); + } catch (e) { + return ($passwordPrompt = true); + } - mnemonic = entropyToMnemonic(entropy); - seed = mnemonicToSeedSync(mnemonic); - key = bip32.fromSeed(seed); - child = key.derivePath("m/44'/1237'/0'/0/0"); - privkey = child.privateKey; + let event = { + pubkey: user.pubkey, + created_at: Math.floor(Date.now() / 1000), + kind: 1, + content: message, + tags: [] + }; - let event = { - pubkey: user.pubkey, - created_at: Math.floor(Date.now() / 1000), - kind: 1, - content: message, - tags: [] - }; + await send(event, user, $password); - event.id = await calculateId(event); - event.sig = await signId(privkey, event.id); - await post(`/${user.username}`, { event }); + event.user = user; + event.seen = event.created_at; - event.user = user; - event.seen = event.created_at; - events[event.id] = event; - events = events; + events[event.id] = event; + events = events; + } catch (e) { + failure('Problem posting event'); + console.log(e); + } }; let distance = (date) => { @@ -78,6 +74,10 @@ <svelte:window bind:innerWidth={w} /> +{#if $passwordPrompt} + <Password {user} /> +{/if} + <form on:submit|preventDefault={submit} class="flex justify-center gap-4 max-w-2xl mx-auto"> <div class="grow"> <input diff --git a/src/components/Password.svelte b/src/components/Password.svelte new file mode 100644 index 00000000..c9318201 --- /dev/null +++ b/src/components/Password.svelte @@ -0,0 +1,77 @@ +<script> + import { Icon } from "$comp"; + import { failure, post } from '$lib/utils'; + import { password as pw, passwordPrompt } from '$lib/store'; + + export let user; + + let password; + let { username } = user; + let revealPassword = false; + + let cancel = () => ($passwordPrompt = false); + + let submit = async () => { + try { + await post('/password', { password }); + $passwordPrompt = false; + $pw = password; + } catch (e) { + failure('Invalid password, try again'); + } + }; +</script> + +<div class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-20"> + <div class="relative top-1/3 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white space-y-5"> + <h1 class="text-center text-2xl font-semibold">Please enter your password</h1> + <form on:submit|preventDefault={submit}> + <div class="relative mb-5"> + {#if revealPassword} + <input + name="password" + type="text" + required + class="bg-primary" + bind:value={password} + autocapitalize="none" + autofocus + /> + {:else} + <input + name="password" + type="password" + required + class="bg-primary" + bind:value={password} + autocapitalize="none" + autofocus + /> + {/if} + <button + type="button" + on:click={() => (revealPassword = !revealPassword)} + class="absolute right-5 top-5" + > + <Icon icon={revealPassword ? 'eye' : 'eye-off'} /> + </button> + </div> + <div class="w-full flex"> + <button + type="button" + class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto" + on:click={cancel} + on:keydown={cancel} + > + <div class="my-auto">Cancel</div> + </button> + <button + type="submit" + class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto" + > + <div class="my-auto">Submit</div> + </button> + </div> + </form> + </div> +</div> diff --git a/src/components/Pin.svelte b/src/components/Pin.svelte index 80e003b9..9b1bb4ea 100644 --- a/src/components/Pin.svelte +++ b/src/components/Pin.svelte @@ -4,6 +4,8 @@ let pinCode = ''; let pinInput; + let cancel = () => ($pin = '000000'); + $: pinCode.length > 5 && ($pin = pinCode); </script> @@ -15,6 +17,7 @@ <button type="submit" class="border-2 border-black rounded-xl font-semibold mx-auto py-3 w-40 hover:opacity-80 mx-auto" + on:click={cancel} > <div class="my-auto">Cancel</div> </button> diff --git a/src/components/index.js b/src/components/index.js index 90bc1e7e..b0802c25 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,3 +1,4 @@ +export { default as Password } from './Password.svelte'; export { default as Feed } from './Feed.svelte'; export { default as Invoice } from './Invoice.svelte'; export { default as Request } from './Request.svelte'; diff --git a/src/lib/nostr.js b/src/lib/nostr.js new file mode 100644 index 00000000..df63d5d8 --- /dev/null +++ b/src/lib/nostr.js @@ -0,0 +1,30 @@ +import { entropyToMnemonic, mnemonicToSeedSync } from 'bip39'; +import { Buffer } from 'buffer'; +import { bech32m } from 'bech32'; +import { bip32, post, stretch } from '$lib/utils'; +import { calculateId, signId } from 'nostr'; + +const { decode, fromWords } = bech32m; + +export let send = async (event, { cipher, username, salt }, password) => { + let mnemonic, key, seed, entropy, child, privkey; + entropy = Buffer.from( + await crypto.subtle.decrypt( + { name: 'AES-GCM', iv: new Uint8Array(16) }, + await stretch(password, Buffer.from(salt, 'hex')), + Uint8Array.from(fromWords(decode(cipher, 180).words)) + ), + 'hex' + ).toString('hex'); + + mnemonic = entropyToMnemonic(entropy); + seed = mnemonicToSeedSync(mnemonic); + key = bip32.fromSeed(seed); + child = key.derivePath("m/44'/1237'/0'/0/0"); + privkey = child.privateKey; + + event.id = await calculateId(event); + event.sig = await signId(privkey, event.id); + + await post(`/${username}`, { event }); +}; diff --git a/src/lib/store.js b/src/lib/store.js index 1a1a88c2..9660bf55 100644 --- a/src/lib/store.js +++ b/src/lib/store.js @@ -56,3 +56,4 @@ export const invoice = writable(); export const txns = persistLocal('transactions', []); export const events = writable({}); export const password = writable(); +export const passwordPrompt = writable(); diff --git a/src/lib/utils.js b/src/lib/utils.js index 79509cc1..d4d78007 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -49,9 +49,10 @@ export const post = (url, body, headers) => { try { let { message } = JSON.parse(text); if (message) return JSON.stringify({ name: 'Error', message }); - } catch (e) {} + } catch (e) { + } - return text; + return { error: text }; }) .then((body) => { try { diff --git a/src/routes/(app)/[username]/generate/+page.svelte b/src/routes/(app)/[username]/generate/+page.svelte index 559d0d14..330ccedb 100644 --- a/src/routes/(app)/[username]/generate/+page.svelte +++ b/src/routes/(app)/[username]/generate/+page.svelte @@ -7,7 +7,7 @@ import * as ecc from 'tiny-secp256k1'; import { bip32, stretch, post } from '$lib/utils'; import { goto, invalidate } from '$app/navigation'; - import { loginRedirect } from '$lib/store'; + import { loginRedirect, password } from '$lib/store'; import { Pin } from '$comp'; import { pin } from '$lib/store'; @@ -16,7 +16,7 @@ const { encode, toWords } = bech32m; - let loaded, mnemonic, key, password, seed, entropy, cipher, pubkey, salt, child; + let loaded, mnemonic, key, seed, entropy, cipher, pubkey, salt, child; onMount(async () => { setTimeout(() => (loaded = true), 50); @@ -35,7 +35,7 @@ let bytes = new Uint8Array( await crypto.subtle.encrypt( { name: 'AES-GCM', iv: new Uint8Array(16) }, - await stretch(password, salt), + await stretch($password, salt), Uint8Array.from(Buffer.from(entropy, 'hex')) ) ); diff --git a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte b/src/routes/(app)/[username]/settings/[...tab]/+page.svelte index d47ce0de..3b213a11 100644 --- a/src/routes/(app)/[username]/settings/[...tab]/+page.svelte +++ b/src/routes/(app)/[username]/settings/[...tab]/+page.svelte @@ -28,10 +28,10 @@ $: form?.success && success('Settings saved!'); - // $: if (form?.message?.startsWith('Pin')) { - // failure('Wrong pin, try again'); - // $pin = ''; - // } + $: if (form?.message?.startsWith('Pin')) { + failure('Wrong pin, try again'); + $pin = ''; + } let tabs = [ { name: 'account', key: 'ACCOUNT', comp: Account }, diff --git a/src/routes/(app)/login/+server.js b/src/routes/(app)/login/+server.js new file mode 100644 index 00000000..e08356a0 --- /dev/null +++ b/src/routes/(app)/login/+server.js @@ -0,0 +1,6 @@ +import { json } from "@sveltejs/kit"; +import { post } from "$lib/utils"; + +export async function POST({ request }) { + return json(await post('/checkPassword', await request.json())); +} diff --git a/src/routes/(app)/password/+server.js b/src/routes/(app)/password/+server.js new file mode 100644 index 00000000..ed9b4dbb --- /dev/null +++ b/src/routes/(app)/password/+server.js @@ -0,0 +1,8 @@ +import { json } from '@sveltejs/kit'; +import { auth, post } from '$lib/utils'; + +export async function POST({ cookies, request }) { + let result = await post('/checkPassword', await request.json(), auth(cookies)); + return json(result); +} + |
