Skip to content

Commit

Permalink
feat: add secret recovery request form.
Browse files Browse the repository at this point in the history
  • Loading branch information
gdethier committed May 16, 2024
1 parent e8f02c9 commit 423fe6b
Show file tree
Hide file tree
Showing 14 changed files with 440 additions and 45 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"react-date-picker": "^9.1.0",
"react-dev-utils": "^12.0.1",
"react-dom": "^18.2.0",
"react-hook-form": "^7.39.1",
"react-hook-form": "^7.51.4",
"react-refresh": "^0.11.0",
"react-responsive": "^9.0.0",
"react-router-dom": "^6.4.3",
Expand Down
23 changes: 6 additions & 17 deletions src/Login.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.Login {
height: 100vh;
min-height: 100vh;
background: linear-gradient(0deg, #9f1278 0%, #3b6cf4 100%);
display: flex;
position: relative;
Expand Down Expand Up @@ -65,23 +65,12 @@
margin-right: 10px;
}

.Login .node-info {
.Login .recovery-container {
text-align: center;
margin-bottom: 60px;
}

.Login .node-info .name {
font-size: 1.2rem;
font-weight: bold;
}

.Login .node-info .peer-id {
font-size: 0.9rem;
font-weight: bold;
}

.Login .node-info .peer-id img {
margin-right: 10px;
position: relative;
z-index: 1;
padding-top: 3rem;
padding-bottom: 2rem;
}

.Login .left-character {
Expand Down
15 changes: 9 additions & 6 deletions src/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import AbsoluteLogo from './AbsoluteLogo';
import './Login.css';
import Icon from "./common/Icon";
import Alert from "./common/Alert";
import { SECRET_RECOVERY_PATH } from "./PublicPaths";

export const LOGIN_PATH = "/login";

Expand Down Expand Up @@ -129,12 +130,14 @@ export default function Login() {
</Button>
</div>

<div className="node-info">
<p className="name">You are currently connected to the following logion node:</p>
<p className="peer-id">
<img src={process.env.PUBLIC_URL + "/assets/node.svg"} alt="node icon" />
Peer ID: {connectedNodeMetadata.peerId}
</p>
<div className="recovery-container">
<h2>Do you need to recover a secret?</h2>

<Button
onClick={ () => navigate(SECRET_RECOVERY_PATH) }
>
Recover a secret
</Button>
</div>
</Container>
<div className="left-character">
Expand Down
3 changes: 3 additions & 0 deletions src/PublicPaths.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,6 @@ export function fullTokensRecordsCertificate(locId: UUID, recordId: Hash, noRedi
export function getBaseUrl(): string {
return `${ window.location.protocol }//${ window.location.host }`;
}

export const SECRET_RECOVERY_RELATIVE_PATH = "/secret-recovery";
export const SECRET_RECOVERY_PATH = PUBLIC_PATH + SECRET_RECOVERY_RELATIVE_PATH;
3 changes: 3 additions & 0 deletions src/PublicRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import Certificate from "./certificate/Certificate";
import {
CERTIFICATE_RELATIVE_PATH,
COLLECTION_ITEM_CERTIFICATE_RELATIVE_PATH,
SECRET_RECOVERY_RELATIVE_PATH,
TOKENS_RECORD_CERTIFICATE_RELATIVE_PATH
} from "./PublicPaths";
import SecretRecoveryRequestFormPage from "./pages/public/secretrecoveryrequest/SecretRecoveryRequestFormPage";

export default function PublicRouter() {
return (
Expand All @@ -14,6 +16,7 @@ export default function PublicRouter() {
<Route path={ COLLECTION_ITEM_CERTIFICATE_RELATIVE_PATH } element={ <Certificate /> } />
<Route path={ TOKENS_RECORD_CERTIFICATE_RELATIVE_PATH } element={ <Certificate /> } />
</Route>
<Route path={ SECRET_RECOVERY_RELATIVE_PATH } element={ <SecretRecoveryRequestFormPage /> } />
</Routes>
)
}
22 changes: 8 additions & 14 deletions src/__snapshots__/Login.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,16 @@ exports[`Login renders 1`] = `
</Button>
</div>
<div
className="node-info"
className="recovery-container"
>
<p
className="name"
>
You are currently connected to the following logion node:
</p>
<p
className="peer-id"
<h2>
Do you need to recover a secret?
</h2>
<Button
onClick={[Function]}
>
<img
alt="node icon"
src="/assets/node.svg"
/>
Peer ID:
</p>
Recover a secret
</Button>
</div>
</Container>
<div
Expand Down
1 change: 0 additions & 1 deletion src/components/identity/IdentityForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ export default function IdentityForm(props: Props) {
</Col>
</Row>

<h3>Address</h3>
<FormGroup
id="line1"
label="Line1"
Expand Down
10 changes: 10 additions & 0 deletions src/landing/LandingPage.css
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
display: block;
top: -87px;
right: 0;
z-index: -1;
}

.LandingPage .recovery-container .recovery-process {
Expand All @@ -292,6 +293,15 @@
box-shadow: none;
}

.LandingPage .recovery-buttons {
display: flex;
justify-content: center;
}

.LandingPage .recovery-buttons :nth-child(n+2) {
margin-left: 20px;
}

.LandingPage .protection-container {
text-align: center;
position: relative;
Expand Down
8 changes: 7 additions & 1 deletion src/landing/LandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useCallback, useState } from 'react';
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import { useNavigate } from 'react-router-dom';

import Button from '../common/Button';
import Dialog from '../common/Dialog';
Expand All @@ -12,6 +13,7 @@ import AbsoluteLogo from '../AbsoluteLogo';
import './LandingPage.css';
import { Tutorial } from './Tutorial';
import { useLogionChain } from '../logion-chain';
import { SECRET_RECOVERY_PATH } from 'src/PublicPaths';

export interface Props {
activeStep: Step,
Expand All @@ -24,6 +26,7 @@ type DialogType = 'install' | 'create' | 'recover' | null;
export default function LandingPage(props: Props) {
const { tryEnableMetaMask } = useLogionChain();
const [ showDialog, setShowDialog ] = useState<DialogType>(null);
const navigate = useNavigate();

let stepsLeft;
if(props.activeStep === 'install') {
Expand Down Expand Up @@ -170,7 +173,10 @@ export default function LandingPage(props: Props) {
Inaccessible wallet?&nbsp;
<strong>recovery process</strong>
</p>
<Button onClick={ () => setShowDialog("recover") }>Start my recovery process</Button>
<div className="recovery-buttons">
<Button onClick={ () => setShowDialog("recover") }>Start my recovery process</Button>
<Button onClick={ () => navigate(SECRET_RECOVERY_PATH) }>Request secret recovery</Button>
</div>
<Dialog
actions={[
{
Expand Down
118 changes: 118 additions & 0 deletions src/pages/public/secretrecoveryrequest/SecretRecoveryRequestForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { UUID } from "@logion/node-api";
import { useCallback } from "react";
import { Form } from "react-bootstrap";
import { Control, Controller, FieldErrors, UseFormSetValue } from "react-hook-form";
import Button from "src/common/Button";
import { BackgroundAndForegroundColors } from "src/common/ColorTheme";
import FormGroup from "src/common/FormGroup";
import { useLogionChain } from "src/logion-chain";

export interface FormValues {
locId: string;
secretName: string;
challenge: string;
}

export interface Props {
control: Control<FormValues>;
errors: FieldErrors<FormValues>;
colors: BackgroundAndForegroundColors;
setValue: UseFormSetValue<FormValues>;
}

export default function SecretRecoveryRequestForm(props: Props) {
const { control, errors, colors, setValue } = props;
const { client } = useLogionChain();

const validateLocId = useCallback(async (locIdString: string) => {
const locId = UUID.fromAnyString(locIdString);
if(locId === undefined) {
return "Invalid LOC ID";
}
if(client) {
const loc = await client?.public.findLocById({ locId });
if(loc === undefined) {
return "LOC not found";
}
if(loc.data.locType !== "Identity") {
return "LOC is not an Identity LOC";
}
if(loc.data.status !== "CLOSED") {
return "LOC is not closed";
}
}
}, [ client ]);

return (
<div className="SecretRecoveryRequestForm">
<FormGroup
id="locId"
label="Identity LOC ID"
control={
<Controller
name="locId"
control={ control }
defaultValue=""
rules={ { validate: validateLocId } }
render={ ({ field }) => (
<Form.Control
isInvalid={ !!errors.locId?.message }
type="text"
{ ...field }
/>
) }
/>
}
feedback={ errors.locId?.message }
colors={ colors }
/>
<FormGroup
id="secretName"
label="Secret name"
control={
<Controller
name="secretName"
control={ control }
defaultValue=""
rules={ { required: "Secret name is required" } }
render={ ({ field }) => (
<Form.Control
isInvalid={ !!errors.secretName?.message }
type="text"
{ ...field }
/>
) }
/>
}
feedback={ errors.secretName?.message }
colors={ colors }
/>
<FormGroup
id="challenge"
label="Challenge"
control={
<Controller
name="challenge"
control={ control }
defaultValue=""
rules={ { required: "A challenge is required" } }
render={ ({ field }) => (
<Form.Control
isInvalid={ !!errors.challenge?.message }
type="text"
{ ...field }
/>
) }
/>
}
feedback={ errors.challenge?.message }
colors={ colors }
/>

<p>The challenge is a random value that is attached to your request. <strong>You will have to provide it again
upon actual secret retrieval. Make sure to keep it in a safe place until the procedure is over.</strong></p>

<Button onClick={ () => setValue("challenge", window.crypto.randomUUID()) }>Generate random challenge</Button>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.SecretRecoveryRequestFormPage {
background-color: #0c163d;
padding-top: 30px;
padding-bottom: 30px;
min-height: 100vh;
}

.SecretRecoveryRequestFormPage .container > .row {
margin-bottom: 30px;
}

.SecretRecoveryRequestFormPage .container > .row:last-child {
margin-bottom: 0;
}

.SecretRecoveryRequestFormPage .alert-container {
display: flex;
justify-content: center;
}

.SecretRecoveryRequestFormPage .success-frame {
text-align: center;
}

.SecretRecoveryRequestFormPage .success-icon {
margin-bottom: 30px;
}

.SecretRecoveryRequestFormPage .challenge {
text-align: center;
font-weight: bold;
font-size: 1.5rem;
}
Loading

0 comments on commit 423fe6b

Please sign in to comment.