Add password to identity (#94)
This commit is contained in:
parent
1f71792d56
commit
093b6ca7be
|
@ -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 ?? "",
|
||||||
|
|
|
@ -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} />;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
`;
|
|
@ -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}
|
||||||
|
|
|
@ -28,7 +28,6 @@ export function useMessages(chatId: string) {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
console.log(`increase noti ${id}`);
|
|
||||||
incNotification(id);
|
incNotification(id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -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(
|
||||||
|
(result) => {
|
||||||
setCommunity(result.community);
|
setCommunity(result.community);
|
||||||
setMessenger(result.messenger);
|
setMessenger(result.messenger);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -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 }) => {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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";
|
||||||
|
|
Loading…
Reference in New Issue