Add password to identity (#94)

This commit is contained in:
Szymon Szlachtowicz 2021-10-22 14:27:26 +02:00 committed by GitHub
parent 1f71792d56
commit 093b6ca7be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 205 additions and 39 deletions

View File

@ -1,4 +1,5 @@
import React, { useEffect, useMemo, useState } from "react"; import React, { useEffect, useMemo, useState } from "react";
import { Identity } from "status-communities/dist/cjs";
import styled from "styled-components"; import styled from "styled-components";
import { useNarrow } from "../contexts/narrowProvider"; import { useNarrow } from "../contexts/narrowProvider";
@ -19,9 +20,15 @@ interface ChatProps {
theme: Theme; theme: Theme;
communityKey: string; communityKey: string;
fetchMetadata?: (url: string) => Promise<Metadata | undefined>; fetchMetadata?: (url: string) => Promise<Metadata | undefined>;
identity: Identity;
} }
export function Chat({ theme, communityKey, fetchMetadata }: ChatProps) { export function Chat({
theme,
communityKey,
fetchMetadata,
identity,
}: ChatProps) {
const [activeChannel, setActiveChannel] = useState<ChannelData>({ const [activeChannel, setActiveChannel] = useState<ChannelData>({
id: "", id: "",
name: "", name: "",
@ -40,14 +47,13 @@ export function Chat({ theme, communityKey, fetchMetadata }: ChatProps) {
loadPrevDay, loadPrevDay,
loadingMessages, loadingMessages,
community, community,
} = useMessenger(activeChannel?.id ?? "", communityKey); } = useMessenger(activeChannel?.id ?? "", communityKey, identity);
const [isModalVisible, setIsModalVisible] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false);
const showModal = () => setIsModalVisible(true); const showModal = () => setIsModalVisible(true);
const communityData = useMemo(() => { const communityData = useMemo(() => {
if (community?.description) { if (community?.description) {
console.log(Object.keys(community.description.proto.members));
return { return {
id: 1, id: 1,
name: community.description.identity?.displayName ?? "", name: community.description.identity?.displayName ?? "",

View File

@ -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<Metadata | undefined>;
}
export function ChatLoader({
theme,
communityKey,
fetchMetadata,
}: ChatLoaderProps) {
const [identity, setIdentity] = useState<Identity | undefined>(undefined);
if (identity) {
return (
<Chat
communityKey={communityKey}
fetchMetadata={fetchMetadata}
theme={theme}
identity={identity}
/>
);
} else {
return <IdentityLoader setIdentity={setIdentity} />;
}
}

View File

@ -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 (
<Wrapper>
{encryptedIdentity ? (
<FormWrappers>
<InfoDiv>Please provide password for your identity</InfoDiv>
{wrongPassword && <div>Wrong password</div>}
<Input
value={password}
type={"password"}
onChange={(e) => setPassword(e.target.value)}
onKeyPress={(e) => {
if (e.key === "Enter") {
loadIdentity();
}
}}
/>
<Button onClick={loadIdentity}>LOAD</Button>
<Button
onClick={() => {
setEncryptedIdentity("");
}}
>
Create new identity
</Button>
</FormWrappers>
) : (
<FormWrappers>
<InfoDiv>Please provide password for your identity</InfoDiv>
<Input
value={password}
type={"password"}
onChange={(e) => setPassword(e.target.value)}
onKeyPress={(e) => {
if (e.key === "Enter") {
createIdentity();
}
}}
/>
<Button onClick={createIdentity}>Create</Button>
{identityInMemory && (
<Button
onClick={() =>
setEncryptedIdentity(loadEncryptedIdentity() ?? "")
}
>
Go back
</Button>
)}
</FormWrappers>
)}
</Wrapper>
);
}
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;
`;

View File

@ -7,7 +7,7 @@ import { Metadata } from "../models/Metadata";
import { GlobalStyle } from "../styles/GlobalStyle"; import { GlobalStyle } from "../styles/GlobalStyle";
import { Theme } from "../styles/themes"; import { Theme } from "../styles/themes";
import { Chat } from "./Chat"; import { ChatLoader } from "./ChatLoader";
interface ReactChatProps { interface ReactChatProps {
theme: Theme; theme: Theme;
@ -26,7 +26,7 @@ export function ReactChat({
<NarrowProvider myRef={ref}> <NarrowProvider myRef={ref}>
<Wrapper ref={ref}> <Wrapper ref={ref}>
<GlobalStyle /> <GlobalStyle />
<Chat <ChatLoader
communityKey={communityKey} communityKey={communityKey}
fetchMetadata={fetchMetadata} fetchMetadata={fetchMetadata}
theme={theme} theme={theme}

View File

@ -28,7 +28,6 @@ export function useMessages(chatId: string) {
), ),
}; };
}); });
console.log(`increase noti ${id}`);
incNotification(id); incNotification(id);
} }
}, },

View File

@ -1,13 +1,17 @@
// import { StoreCodec } from "js-waku"; // import { StoreCodec } from "js-waku";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { Community, Messenger } from "status-communities/dist/cjs"; import { Community, Identity, Messenger } from "status-communities/dist/cjs";
import { createCommunityMessenger } from "../../utils/createCommunityMessenger"; import { createCommunityMessenger } from "../../utils/createCommunityMessenger";
import { useLoadPrevDay } from "./useLoadPrevDay"; import { useLoadPrevDay } from "./useLoadPrevDay";
import { useMessages } from "./useMessages"; import { useMessages } from "./useMessages";
export function useMessenger(chatId: string, communityKey: string) { export function useMessenger(
chatId: string,
communityKey: string,
identity: Identity
) {
const [messenger, setMessenger] = useState<Messenger | undefined>(undefined); const [messenger, setMessenger] = useState<Messenger | undefined>(undefined);
const { addMessage, clearNotifications, notifications, messages } = const { addMessage, clearNotifications, notifications, messages } =
useMessages(chatId); useMessages(chatId);
@ -15,10 +19,12 @@ export function useMessenger(chatId: string, communityKey: string) {
const { loadPrevDay, loadingMessages } = useLoadPrevDay(chatId, messenger); const { loadPrevDay, loadingMessages } = useLoadPrevDay(chatId, messenger);
useEffect(() => { useEffect(() => {
createCommunityMessenger(communityKey, addMessage).then((result) => { createCommunityMessenger(communityKey, addMessage, identity).then(
setCommunity(result.community); (result) => {
setMessenger(result.messenger); setCommunity(result.community);
}); setMessenger(result.messenger);
}
);
}, []); }, []);
useEffect(() => { useEffect(() => {

View File

@ -2,8 +2,6 @@ import { StoreCodec } from "js-waku";
import { Community, Identity, Messenger } from "status-communities/dist/cjs"; import { Community, Identity, Messenger } from "status-communities/dist/cjs";
import { ApplicationMetadataMessage } from "status-communities/dist/cjs"; import { ApplicationMetadataMessage } from "status-communities/dist/cjs";
import { loadIdentity, saveIdentity } from "./";
const WAKU_OPTIONS = { const WAKU_OPTIONS = {
libp2p: { libp2p: {
config: { config: {
@ -17,15 +15,9 @@ const WAKU_OPTIONS = {
export async function createCommunityMessenger( export async function createCommunityMessenger(
communityKey: string, 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); const messenger = await Messenger.create(identity, WAKU_OPTIONS);
await new Promise((resolve) => { await new Promise((resolve) => {
messenger.waku.libp2p.peerStore.on("change:protocols", ({ protocols }) => { messenger.waku.libp2p.peerStore.on("change:protocols", ({ protocols }) => {

View File

@ -24,18 +24,8 @@ export async function saveIdentity(identity: Identity, password: string) {
localStorage.setItem("cipherIdentity", JSON.stringify(data)); localStorage.setItem("cipherIdentity", JSON.stringify(data));
} }
export async function loadIdentity( export function loadEncryptedIdentity(): string | null {
password: string return localStorage.getItem("cipherIdentity");
): Promise<Identity | undefined> {
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);
} }
async function getWrapKey(password: string, salt: Uint8Array) { async function getWrapKey(password: string, salt: Uint8Array) {
@ -61,12 +51,16 @@ async function getWrapKey(password: string, salt: Uint8Array) {
); );
} }
async function decryptIdentity( export async function decryptIdentity(
salt: Buffer, encryptedIdentity: string,
iv: Buffer,
cipherKeyPair: Buffer,
password: string password: string
): Promise<Identity | undefined> { ): Promise<Identity | undefined> {
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); const key = await getWrapKey(password, salt);
try { try {

View File

@ -2,6 +2,10 @@ export { binarySetInsert } from "./binarySetInsert";
export { copy } from "./copy"; export { copy } from "./copy";
export { copyImg } from "./copyImg"; export { copyImg } from "./copyImg";
export { downloadImg } from "./downloadImg"; export { downloadImg } from "./downloadImg";
export { saveIdentity, loadIdentity } from "./identityStorage"; export {
saveIdentity,
loadEncryptedIdentity,
decryptIdentity,
} from "./identityStorage";
export { reduceString } from "./reduceString"; export { reduceString } from "./reduceString";
export { uintToImgUrl } from "./uintToImgUrl"; export { uintToImgUrl } from "./uintToImgUrl";