Skip to content

Commit

Permalink
Add-pubkey modal, refactor register comps #238
Browse files Browse the repository at this point in the history
  • Loading branch information
joepio committed Jan 17, 2023
1 parent 4b24090 commit ca9c114
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 50 deletions.
1 change: 0 additions & 1 deletion data-browser/src/components/Dialog/useDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export const useDialog = (): UseDialogReturnType => {
}, []);

const close = useCallback(() => {
console.log('close', close);
setShowDialog(false);
}, []);

Expand Down
196 changes: 148 additions & 48 deletions data-browser/src/components/RegisterSignIn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {
import React, { FormEvent, useCallback, useEffect, useState } from 'react';
import { useSettings } from '../helpers/AppSettings';
import { Button } from './Button';
import { nameRegex, register, useServerURL, useStore } from '@tomic/react';
import {
addPublicKey,
nameRegex,
register as createRegistration,
useServerURL,
useStore,
} from '@tomic/react';
import Field from './forms/Field';
import { InputWrapper, InputStyled } from './forms/InputStyles';
import { Row } from './Row';
Expand All @@ -20,6 +26,16 @@ interface RegisterSignInProps {
redirect?: string;
}

/** What is currently showing */
enum PageStateOpts {
none,
signIn,
register,
reset,
mailSentRegistration,
mailSentAddPubkey,
}

/**
* Two buttons: Register / Sign in.
* Opens a Dialog / Modal with the appropriate form.
Expand All @@ -29,7 +45,8 @@ export function RegisterSignIn({
}: React.PropsWithChildren<RegisterSignInProps>): JSX.Element {
const { dialogProps, show, close } = useDialog();
const { agent } = useSettings();
const [isRegistering, setRegister] = useState(true);
const [pageState, setPageState] = useState<PageStateOpts>(PageStateOpts.none);
const [email, setEmail] = useState('');

if (agent) {
return <>{children}</>;
Expand All @@ -39,7 +56,7 @@ export function RegisterSignIn({
<Row>
<Button
onClick={() => {
setRegister(true);
setPageState(PageStateOpts.register);
show();
}}
>
Expand All @@ -48,27 +65,116 @@ export function RegisterSignIn({
<Button
subtle
onClick={() => {
setRegister(false);
setPageState(PageStateOpts.signIn);
show();
}}
>
Sign In
</Button>
</Row>
<Dialog {...dialogProps}>
{isRegistering ? <Register close={close} /> : <SignIn />}
{pageState === PageStateOpts.register && (
<Register
setPageState={setPageState}
email={email}
setEmail={setEmail}
/>
)}
{pageState === PageStateOpts.signIn && (
<SignIn setPageState={setPageState} />
)}
{pageState === PageStateOpts.reset && (
<Reset
email={email}
setEmail={setEmail}
setPageState={setPageState}
/>
)}
{pageState === PageStateOpts.mailSentRegistration && (
<MailSentConfirm
email={email}
close={close}
message={'Your account will be created when you open that link.'}
/>
)}
{pageState === PageStateOpts.mailSentAddPubkey && (
<MailSentConfirm
email={email}
close={close}
message={'Click that link to create a new PassPhrase.'}
/>
)}
</Dialog>
</>
);
}

function Register({ close }) {
function Reset({ email, setEmail, setPageState }) {
const store = useStore();
const [err, setErr] = useState<Error | undefined>(undefined);

const handleRequestReset = useCallback(async () => {
try {
await addPublicKey(store, email);
setPageState(PageStateOpts.mailSentAddPubkey);
} catch (e) {
setErr(e);
}
}, [email]);

return (
<>
<DialogTitle>
<h1>Reset your PassKey</h1>
</DialogTitle>
<DialogContent>
<p>
{
"Lost it? No worries, we'll send a link that let's you create a new one."
}
</p>
<EmailField
email={email}
setEmail={(e: any) => {
setErr(undefined);
setEmail(e);
}}
/>
{err && <ErrorLook>{err.message}</ErrorLook>}
</DialogContent>
<DialogActions>
<Button onClick={handleRequestReset}>Send me</Button>
</DialogActions>
</>
);
}

function MailSentConfirm({ email, close, message }) {
return (
<>
<DialogTitle>
<h1>Go to your email inbox</h1>
</DialogTitle>
<DialogContent>
<p>
{"We've sent a confirmation link to "}
<strong>{email}</strong>
{'.'}
</p>
<p>{message}</p>
</DialogContent>
<DialogActions>
<Button onClick={close}>{"Ok, I'll open my mailbox!"}</Button>
</DialogActions>
</>
);
}

function Register({ setPageState, email, setEmail }) {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [serverUrlStr] = useServerURL();
const [nameErr, setErr] = useState<Error | undefined>(undefined);
const store = useStore();
const [mailSent, setMailSent] = useState(false);

const serverUrl = new URL(serverUrlStr);
serverUrl.host = `${name}.${serverUrl.host}`;
Expand All @@ -93,36 +199,15 @@ function Register({ close }) {
}

try {
await register(store, name, email);
setMailSent(true);
await createRegistration(store, name, email);
setPageState(PageStateOpts.mailSentRegistration);
} catch (er) {
setErr(er);
}
},
[name, email],
);

if (mailSent) {
return (
<>
<DialogTitle>
<h1>Go to your email inbox</h1>
</DialogTitle>
<DialogContent>
<p>
{"We've sent a confirmation link to "}
<strong>{email}</strong>
{'.'}
</p>
<p>Your account will be created when you open that link.</p>
</DialogContent>
<DialogActions>
<Button onClick={close}>Ok, I will!</Button>
</DialogActions>
</>
);
}

return (
<>
<DialogTitle>
Expand All @@ -147,48 +232,63 @@ function Register({ close }) {
/>
</InputWrapper>
</Field>
<Field label='E-mail'>
<InputWrapper>
<InputStyled
autoFocus={true}
type={'email'}
required
value={email}
onChange={e => {
setEmail(e.target.value);
}}
/>
</InputWrapper>
</Field>
<EmailField email={email} setEmail={setEmail} />
{name && nameErr && <ErrorLook>{nameErr.message}</ErrorLook>}
</form>
</DialogContent>
<DialogActions>
<Button subtle onClick={() => setPageState(PageStateOpts.signIn)}>
Sign in
</Button>
<Button
type='submit'
form='register-form'
disabled={!name || !!nameErr}
onClick={handleSubmit}
>
Create
Save
</Button>
</DialogActions>
</>
);
}

function SignIn() {
function SignIn({ setPageState }) {
return (
<>
<DialogTitle>
<h1>Sign in </h1>
<h1>Sign in</h1>
</DialogTitle>
<DialogContent>
<SettingsAgent />
</DialogContent>
<DialogActions>
<p>Lost your passphrase?</p>
<Button subtle onClick={() => setPageState(PageStateOpts.register)}>
Register
</Button>
<Button subtle onClick={() => setPageState(PageStateOpts.reset)}>
I lost my passphrase
</Button>
</DialogActions>
</>
);
}

function EmailField({ setEmail, email }) {
return (
<Field label='E-mail'>
<InputWrapper>
<InputStyled
// This is not properly working atm
autoFocus
type={'email'}
required
value={email}
onChange={e => {
setEmail(e.target.value);
}}
/>
</InputWrapper>
</Field>
);
}
5 changes: 5 additions & 0 deletions data-browser/src/views/ErrorPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ function ErrorPage({ resource }: ResourcePageProps): JSX.Element {
<h1>Unauthorized</h1>
{agent ? (
<>
<p>
{
"You don't have access to this. Try asking for access, or sign in with a different account."
}
</p>
<ErrorBlock error={resource.error!} />
<span>
<Button onClick={() => store.fetchResource(subject)}>
Expand Down
37 changes: 36 additions & 1 deletion lib/src/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,34 @@ export async function register(
const description = resource.get(properties.description) as string;

if (!description.includes('success')) {
throw new Error('ERRORORRRR');
throw new Error('Expected a `success` message, did not receive one');
}

return;
}

/** Asks the server to add a public key to an account. Will lead to a confirmation link being sent */
export async function addPublicKey(store: Store, email: string): Promise<void> {
if (!email) {
throw new Error('No email provided');
}

const url = new URL('/add-public-key', store.getServerUrl());
url.searchParams.set('email', email);
const resource = await store.getResourceAsync(url.toString());

if (!resource) {
throw new Error('No resource received');
}

if (resource.error) {
throw resource.error;
}

const description = resource.get(properties.description) as string;

if (!description.includes('success')) {
throw new Error('Expected a `success` message, did not receive one');
}

return;
Expand Down Expand Up @@ -170,13 +197,21 @@ export async function confirmEmail(

let agent = store.getAgent();

// No agent, create a new one
if (!agent) {
const keypair = await generateKeyPair();
const newAgent = new Agent(keypair.privateKey);
newAgent.subject = `${store.getServerUrl()}/agents/${parsed.name}`;
agent = newAgent;
}

// An agent already exists, make sure it matches the confirm email token
if (!agent?.subject?.includes(parsed.name)) {
throw new Error(
'You cannot confirm this email, you are already logged in as a different user',
);
}

url.searchParams.set('public-key', await agent.getPublicKey());
const resource = await store.getResourceAsync(url.toString());

Expand Down

0 comments on commit ca9c114

Please sign in to comment.