From 093b6ca7be5f86f08d1087f27f582822712b63b1 Mon Sep 17 00:00:00 2001 From: Szymon Szlachtowicz <38212223+Szymx95@users.noreply.github.com> Date: Fri, 22 Oct 2021 14:27:26 +0200 Subject: [PATCH] Add password to identity (#94) --- packages/react-chat/src/components/Chat.tsx | 12 +- .../react-chat/src/components/ChatLoader.tsx | 35 +++++ .../src/components/Form/IdentityLoader.tsx | 130 ++++++++++++++++++ .../react-chat/src/components/ReactChat.tsx | 4 +- .../src/hooks/messenger/useMessages.ts | 1 - .../src/hooks/messenger/useMessenger.ts | 18 ++- .../src/utils/createCommunityMessenger.ts | 12 +- .../react-chat/src/utils/identityStorage.ts | 26 ++-- packages/react-chat/src/utils/index.ts | 6 +- 9 files changed, 205 insertions(+), 39 deletions(-) create mode 100644 packages/react-chat/src/components/ChatLoader.tsx create mode 100644 packages/react-chat/src/components/Form/IdentityLoader.tsx diff --git a/packages/react-chat/src/components/Chat.tsx b/packages/react-chat/src/components/Chat.tsx index c06255a0..e9669fb7 100644 --- a/packages/react-chat/src/components/Chat.tsx +++ b/packages/react-chat/src/components/Chat.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useMemo, useState } from "react"; +import { Identity } from "status-communities/dist/cjs"; import styled from "styled-components"; import { useNarrow } from "../contexts/narrowProvider"; @@ -19,9 +20,15 @@ interface ChatProps { theme: Theme; communityKey: string; fetchMetadata?: (url: string) => Promise; + identity: Identity; } -export function Chat({ theme, communityKey, fetchMetadata }: ChatProps) { +export function Chat({ + theme, + communityKey, + fetchMetadata, + identity, +}: ChatProps) { const [activeChannel, setActiveChannel] = useState({ id: "", name: "", @@ -40,14 +47,13 @@ export function Chat({ theme, communityKey, fetchMetadata }: ChatProps) { loadPrevDay, loadingMessages, community, - } = useMessenger(activeChannel?.id ?? "", communityKey); + } = useMessenger(activeChannel?.id ?? "", communityKey, identity); const [isModalVisible, setIsModalVisible] = useState(false); const showModal = () => setIsModalVisible(true); const communityData = useMemo(() => { if (community?.description) { - console.log(Object.keys(community.description.proto.members)); return { id: 1, name: community.description.identity?.displayName ?? "", diff --git a/packages/react-chat/src/components/ChatLoader.tsx b/packages/react-chat/src/components/ChatLoader.tsx new file mode 100644 index 00000000..dd46609e --- /dev/null +++ b/packages/react-chat/src/components/ChatLoader.tsx @@ -0,0 +1,35 @@ +import React, { useState } from "react"; +import { Identity } from "status-communities/dist/cjs"; + +import { Metadata } from "../models/Metadata"; +import { Theme } from "../styles/themes"; + +import { Chat } from "./Chat"; +import { IdentityLoader } from "./Form/IdentityLoader"; + +interface ChatLoaderProps { + theme: Theme; + communityKey: string; + fetchMetadata?: (url: string) => Promise; +} + +export function ChatLoader({ + theme, + communityKey, + fetchMetadata, +}: ChatLoaderProps) { + const [identity, setIdentity] = useState(undefined); + + if (identity) { + return ( + + ); + } else { + return ; + } +} diff --git a/packages/react-chat/src/components/Form/IdentityLoader.tsx b/packages/react-chat/src/components/Form/IdentityLoader.tsx new file mode 100644 index 00000000..75f20856 --- /dev/null +++ b/packages/react-chat/src/components/Form/IdentityLoader.tsx @@ -0,0 +1,130 @@ +import React, { useCallback, useEffect, useState } from "react"; +import { Identity } from "status-communities/dist/cjs"; +import styled from "styled-components"; + +import { + decryptIdentity, + loadEncryptedIdentity, + saveIdentity, +} from "../../utils"; + +interface IdentityLoaderProps { + setIdentity: (e: Identity) => void; +} + +export function IdentityLoader({ setIdentity }: IdentityLoaderProps) { + const [password, setPassword] = useState(""); + // Test password for now + // Need design for password input + const [encryptedIdentity, setEncryptedIdentity] = useState( + loadEncryptedIdentity() ?? "" + ); + const [identityInMemory, setIdentityInMemory] = useState(false); + const [wrongPassword, setWrongPassword] = useState(false); + + useEffect(() => { + if (encryptedIdentity) { + setIdentityInMemory(true); + } + setWrongPassword(false); + }, [encryptedIdentity]); + + const loadIdentity = useCallback(async () => { + const identity = await decryptIdentity(encryptedIdentity, password); + if (!identity) { + setWrongPassword(true); + } else { + setIdentity(identity); + } + }, [encryptedIdentity, password]); + + const createIdentity = useCallback(async () => { + const identity = Identity.generate(); + await saveIdentity(identity, password); + setIdentity(identity); + }, [encryptedIdentity, password]); + + return ( + + {encryptedIdentity ? ( + + Please provide password for your identity + {wrongPassword &&
Wrong password
} + setPassword(e.target.value)} + onKeyPress={(e) => { + if (e.key === "Enter") { + loadIdentity(); + } + }} + /> + + +
+ ) : ( + + Please provide password for your identity + setPassword(e.target.value)} + onKeyPress={(e) => { + if (e.key === "Enter") { + createIdentity(); + } + }} + /> + + {identityInMemory && ( + + )} + + )} +
+ ); +} + +const Input = styled.input` + border-radius: 5px; + padding: 5px; + margin: 5px; +`; + +const Button = styled.button` + padding: 5px; + margin: 5px; + + box-shadow: 5px 5px 10px -4px rgba(197, 197, 255, 1); + border-radius: 5px; + background-color: ${({ theme }) => theme.buttonBg}; +`; + +const Wrapper = styled.div` + height: 100%; + width: 100%; + display: flex; +`; + +const FormWrappers = styled.div` + margin: auto; +`; + +const InfoDiv = styled.div` + margin: 5px; + font-weight: 500; + font-size: 16px; +`; diff --git a/packages/react-chat/src/components/ReactChat.tsx b/packages/react-chat/src/components/ReactChat.tsx index 26c927c8..10260f27 100644 --- a/packages/react-chat/src/components/ReactChat.tsx +++ b/packages/react-chat/src/components/ReactChat.tsx @@ -7,7 +7,7 @@ import { Metadata } from "../models/Metadata"; import { GlobalStyle } from "../styles/GlobalStyle"; import { Theme } from "../styles/themes"; -import { Chat } from "./Chat"; +import { ChatLoader } from "./ChatLoader"; interface ReactChatProps { theme: Theme; @@ -26,7 +26,7 @@ export function ReactChat({ - (undefined); const { addMessage, clearNotifications, notifications, messages } = useMessages(chatId); @@ -15,10 +19,12 @@ export function useMessenger(chatId: string, communityKey: string) { const { loadPrevDay, loadingMessages } = useLoadPrevDay(chatId, messenger); useEffect(() => { - createCommunityMessenger(communityKey, addMessage).then((result) => { - setCommunity(result.community); - setMessenger(result.messenger); - }); + createCommunityMessenger(communityKey, addMessage, identity).then( + (result) => { + setCommunity(result.community); + setMessenger(result.messenger); + } + ); }, []); useEffect(() => { diff --git a/packages/react-chat/src/utils/createCommunityMessenger.ts b/packages/react-chat/src/utils/createCommunityMessenger.ts index 9161da1e..bda50eef 100644 --- a/packages/react-chat/src/utils/createCommunityMessenger.ts +++ b/packages/react-chat/src/utils/createCommunityMessenger.ts @@ -2,8 +2,6 @@ import { StoreCodec } from "js-waku"; import { Community, Identity, Messenger } from "status-communities/dist/cjs"; import { ApplicationMetadataMessage } from "status-communities/dist/cjs"; -import { loadIdentity, saveIdentity } from "./"; - const WAKU_OPTIONS = { libp2p: { config: { @@ -17,15 +15,9 @@ const WAKU_OPTIONS = { export async function createCommunityMessenger( communityKey: string, - addMessage: (msg: ApplicationMetadataMessage, id: string, date: Date) => void + addMessage: (msg: ApplicationMetadataMessage, id: string, date: Date) => void, + identity: Identity ) { - // Test password for now - // Need design for password input - let identity = await loadIdentity("test"); - if (!identity) { - identity = Identity.generate(); - await saveIdentity(identity, "test"); - } const messenger = await Messenger.create(identity, WAKU_OPTIONS); await new Promise((resolve) => { messenger.waku.libp2p.peerStore.on("change:protocols", ({ protocols }) => { diff --git a/packages/react-chat/src/utils/identityStorage.ts b/packages/react-chat/src/utils/identityStorage.ts index fbe69c93..02889968 100644 --- a/packages/react-chat/src/utils/identityStorage.ts +++ b/packages/react-chat/src/utils/identityStorage.ts @@ -24,18 +24,8 @@ export async function saveIdentity(identity: Identity, password: string) { localStorage.setItem("cipherIdentity", JSON.stringify(data)); } -export async function loadIdentity( - password: string -): Promise { - const str = localStorage.getItem("cipherIdentity"); - if (!str) return; - const data = JSON.parse(str); - - const salt = hexToBuf(data.salt); - const iv = hexToBuf(data.iv); - const cipher = hexToBuf(data.cipher); - - return await decryptIdentity(salt, iv, cipher, password); +export function loadEncryptedIdentity(): string | null { + return localStorage.getItem("cipherIdentity"); } async function getWrapKey(password: string, salt: Uint8Array) { @@ -61,12 +51,16 @@ async function getWrapKey(password: string, salt: Uint8Array) { ); } -async function decryptIdentity( - salt: Buffer, - iv: Buffer, - cipherKeyPair: Buffer, +export async function decryptIdentity( + encryptedIdentity: string, password: string ): Promise { + const data = JSON.parse(encryptedIdentity); + + const salt = hexToBuf(data.salt); + const iv = hexToBuf(data.iv); + const cipherKeyPair = hexToBuf(data.cipher); + const key = await getWrapKey(password, salt); try { diff --git a/packages/react-chat/src/utils/index.ts b/packages/react-chat/src/utils/index.ts index d3ea8080..096eb09f 100644 --- a/packages/react-chat/src/utils/index.ts +++ b/packages/react-chat/src/utils/index.ts @@ -2,6 +2,10 @@ export { binarySetInsert } from "./binarySetInsert"; export { copy } from "./copy"; export { copyImg } from "./copyImg"; export { downloadImg } from "./downloadImg"; -export { saveIdentity, loadIdentity } from "./identityStorage"; +export { + saveIdentity, + loadEncryptedIdentity, + decryptIdentity, +} from "./identityStorage"; export { reduceString } from "./reduceString"; export { uintToImgUrl } from "./uintToImgUrl";