diff options
Diffstat (limited to 'packages/anastasis-webui/src/pages/home/authMethod')
30 files changed, 1379 insertions, 929 deletions
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx index da87b7a8b..080a7ab31 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx @@ -15,51 +15,67 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/email', + title: "Pages/backup/AuthorizationMethod/AuthMethods/email", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'email' +const type: KnownAuthMethods = "email"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Email to sebasjm@email.com ', - remove: () => null - }] -}); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Email to sebasjm@email.com ", + remove: () => null, + }, + ], + }, +); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Email to sebasjm@email.com', - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: 'Email to someone@sebasjm.com', - remove: () => null - }] -}); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Email to sebasjm@email.com", + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: "Email to someone@sebasjm.com", + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.tsx index 27a0685b2..61c66c8c8 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.tsx @@ -1,57 +1,90 @@ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; +import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { EmailInput } from "../../../components/fields/EmailInput"; import { AnastasisClientFrame } from "../index"; import { AuthMethodSetupProps } from "./index"; -const EMAIL_PATTERN = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ +const EMAIL_PATTERN = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; -export function AuthMethodEmailSetup({ cancel, addAuthMethod, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodEmailSetup({ + cancel, + addAuthMethod, + configured, +}: AuthMethodSetupProps): VNode { const [email, setEmail] = useState(""); - const addEmailAuth = (): void => addAuthMethod({ - authentication_method: { - type: "email", - instructions: `Email to ${email}`, - challenge: encodeCrock(stringToBytes(email)), - }, - }); - const emailError = !EMAIL_PATTERN.test(email) ? 'Email address is not valid' : undefined - const errors = !email ? 'Add your email' : emailError + const addEmailAuth = (): void => + addAuthMethod({ + authentication_method: { + type: "email", + instructions: `Email to ${email}`, + challenge: encodeCrock(stringToBytes(email)), + }, + }); + const emailError = !EMAIL_PATTERN.test(email) + ? "Email address is not valid" + : undefined; + const errors = !email ? "Add your email" : emailError; return ( <AnastasisClientFrame hideNav title="Add email authentication"> <p> For email authentication, you need to provide an email address. When recovering your secret, you will need to enter the code you receive by - email. + email. Add the uuid from the challenge </p> <div> <EmailInput label="Email address" error={emailError} placeholder="email@domain.com" - bind={[email, setEmail]} /> + bind={[email, setEmail]} + /> </div> - {configured.length > 0 && <section class="section"> - <div class="block"> - Your emails: - </div><div class="block"> - {configured.map((c, i) => { - return <div key={i} class="box" style={{ display: 'flex', justifyContent: 'space-between' }}> - <p style={{ marginBottom: 'auto', marginTop: 'auto' }}>{c.instructions}</p> - <div><button class="button is-danger" onClick={c.remove} >Delete</button></div> - </div> - })} - </div></section>} + {configured.length > 0 && ( + <section class="section"> + <div class="block">Your emails:</div> + <div class="block"> + {configured.map((c, i) => { + return ( + <div + key={i} + class="box" + style={{ display: "flex", justifyContent: "space-between" }} + > + <p style={{ marginBottom: "auto", marginTop: "auto" }}> + {c.instructions} + </p> + <div> + <button class="button is-danger" onClick={c.remove}> + Delete + </button> + </div> + </div> + ); + })} + </div> + </section> + )} <div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={cancel}>Cancel</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={cancel}> + Cancel + </button> <span data-tooltip={errors}> - <button class="button is-info" disabled={errors !== undefined} onClick={addEmailAuth}>Add</button> + <button + class="button is-info" + disabled={errors !== undefined} + onClick={addEmailAuth} + > + Add + </button> </span> </div> </div> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx index 525cd2b07..6a8a2a347 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx @@ -15,66 +15,76 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/email', + title: "Pages/recovery/SolveChallenge/AuthMethods/email", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'email' +const type: KnownAuthMethods = "email"; -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); +); -export const PaymentFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +export const PaymentFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + challenge_feedback: { + "uuid-1": { + state: ChallengeFeedbackStatus.Payment, + taler_pay_uri: "taler://pay/...", + provider: "https://localhost:8080/", + payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG", + }, + }, + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', - challenge_feedback: { - 'uuid-1': { - state: ChallengeFeedbackStatus.Payment, - taler_pay_uri: "taler://pay/...", - provider: "https://localhost:8080/", - payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" - } - } -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx index bd4f43740..ff6c51d1c 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode { return ( <AnastasisClientFrame hideNav title="Recovery problem"> <div>invalid state</div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={() => reducer.back()}>Back</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={() => reducer.back()}> + Back + </button> </div> </AnastasisClientFrame> ); @@ -62,8 +70,7 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise<void> { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,19 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( <AnastasisClientFrame hideNav title="Add email authentication"> <SolveOverviewFeedbackDisplay feedback={feedback} /> <p> - An email has been sent to "<b>{selectedChallenge.instructions}</b>". Type the - code below + An email has been sent to "<b>{selectedChallenge.instructions}</b>". + Type the code below. + <b>Here we need to add the code "{selectedUuid}"</b> </p> <TextInput label="Answer" grabFocus bind={[answer, setAnswer]} /> @@ -97,9 +105,11 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}> - Confirm - </AsyncButton>} + {!shouldHideConfirm && ( + <AsyncButton class="button is-info" onClick={onNext}> + Confirm + </AsyncButton> + )} </div> </AnastasisClientFrame> ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx index be0a04847..c521e18fd 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx @@ -15,50 +15,66 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/IBAN', + title: "Pages/backup/AuthorizationMethod/AuthMethods/IBAN", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'iban' +const type: KnownAuthMethods = "iban"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Wire transfer from QWEASD123123 with holder Sebastian', - remove: () => null - }] -}); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Wire transfer from QWEASD123123 with holder Javier', - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: 'Wire transfer from QWEASD123123 with holder Sebastian', - remove: () => null - }] -},); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Wire transfer from QWEASD123123 with holder Sebastian", + remove: () => null, + }, + ], + }, +); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Wire transfer from QWEASD123123 with holder Javier", + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: "Wire transfer from QWEASD123123 with holder Sebastian", + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.tsx index 87969ab27..dee550e5b 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.tsx @@ -1,7 +1,7 @@ import { canonicalJson, encodeCrock, - stringToBytes + stringToBytes, } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; @@ -9,56 +9,98 @@ import { AuthMethodSetupProps } from "."; import { TextInput } from "../../../components/fields/TextInput"; import { AnastasisClientFrame } from "../index"; -export function AuthMethodIbanSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodIbanSetup({ + addAuthMethod, + cancel, + configured, +}: AuthMethodSetupProps): VNode { const [name, setName] = useState(""); const [account, setAccount] = useState(""); - const addIbanAuth = (): void => addAuthMethod({ - authentication_method: { - type: "iban", - instructions: `Wire transfer from ${account} with holder ${name}`, - challenge: encodeCrock(stringToBytes(canonicalJson({ - name, account - }))), - }, - }); - const errors = !name ? 'Add an account name' : ( - !account ? 'Add an account IBAN number' : undefined - ) + const addIbanAuth = (): void => + addAuthMethod({ + authentication_method: { + type: "iban", + instructions: `Wire transfer from ${account} with holder ${name}`, + challenge: encodeCrock( + stringToBytes( + canonicalJson({ + name, + account, + }), + ), + ), + }, + }); + const errors = !name + ? "Add an account name" + : !account + ? "Add an account IBAN number" + : undefined; return ( <AnastasisClientFrame hideNav title="Add bank transfer authentication"> <p> - For bank transfer authentication, you need to provide a bank - account (account holder name and IBAN). When recovering your - secret, you will be asked to pay the recovery fee via bank - transfer from the account you provided here. + For bank transfer authentication, you need to provide a bank account + (account holder name and IBAN). When recovering your secret, you will be + asked to pay the recovery fee via bank transfer from the account you + provided here. </p> <div> <TextInput label="Bank account holder name" grabFocus placeholder="John Smith" - bind={[name, setName]} /> + bind={[name, setName]} + /> <TextInput label="IBAN" placeholder="DE91100000000123456789" - bind={[account, setAccount]} /> + bind={[account, setAccount]} + /> </div> - {configured.length > 0 && <section class="section"> - <div class="block"> - Your bank accounts: - </div><div class="block"> - {configured.map((c, i) => { - return <div key={i} class="box" style={{ display: 'flex', justifyContent: 'space-between' }}> - <p style={{ marginBottom: 'auto', marginTop: 'auto' }}>{c.instructions}</p> - <div><button class="button is-danger" onClick={c.remove} >Delete</button></div> - </div> - })} - </div></section>} + {configured.length > 0 && ( + <section class="section"> + <div class="block">Your bank accounts:</div> + <div class="block"> + {configured.map((c, i) => { + return ( + <div + key={i} + class="box" + style={{ display: "flex", justifyContent: "space-between" }} + > + <p style={{ marginBottom: "auto", marginTop: "auto" }}> + {c.instructions} + </p> + <div> + <button class="button is-danger" onClick={c.remove}> + Delete + </button> + </div> + </div> + ); + })} + </div> + </section> + )} <div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={cancel}>Cancel</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={cancel}> + Cancel + </button> <span data-tooltip={errors}> - <button class="button is-info" disabled={errors !== undefined} onClick={addIbanAuth}>Add</button> + <button + class="button is-info" + disabled={errors !== undefined} + onClick={addIbanAuth} + > + Add + </button> </span> </div> </div> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx index df73a9214..cbbc253e9 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx @@ -15,42 +15,46 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/Iban', + title: "Pages/recovery/SolveChallenge/AuthMethods/Iban", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'iban' - -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +const type: KnownAuthMethods = "iban"; + +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx index 1e4353da6..46cf0502c 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodIbanSolve({ id }: AuthMethodSolveProps): VNode { return ( <AnastasisClientFrame hideNav title="Recovery problem"> <div>invalid state</div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={() => reducer.back()}>Back</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={() => reducer.back()}> + Back + </button> </div> </AnastasisClientFrame> ); @@ -62,8 +70,7 @@ export function AuthMethodIbanSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise<void> { return reducer?.transition("solve_challenge", { answer }); @@ -72,19 +79,17 @@ export function AuthMethodIbanSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( <AnastasisClientFrame hideNav title="Add email authentication"> <SolveOverviewFeedbackDisplay feedback={feedback} /> - <p> - Send a wire transfer to the address - </p> - <TextInput label="Answer" grabFocus bind={[answer, setAnswer]} /> + <p>Send a wire transfer to the address,</p> + <button class="button">Check</button> <div style={{ @@ -96,9 +101,11 @@ export function AuthMethodIbanSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}> - Confirm - </AsyncButton>} + {!shouldHideConfirm && ( + <AsyncButton class="button is-info" onClick={onNext}> + Confirm + </AsyncButton> + )} </div> </AnastasisClientFrame> ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx index adc83d6fe..2977586ac 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx @@ -16,51 +16,67 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/Post', + title: "Pages/backup/AuthorizationMethod/AuthMethods/Post", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'post' +const type: KnownAuthMethods = "post"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Letter to address in postal code QWE456', - remove: () => null - }] -}); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Letter to address in postal code QWE456", + remove: () => null, + }, + ], + }, +); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Letter to address in postal code QWE456', - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: 'Letter to address in postal code ABC123', - remove: () => null - }] -}); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Letter to address in postal code QWE456", + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: "Letter to address in postal code ABC123", + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.tsx index 692421d74..6c8d36bcf 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.tsx @@ -1,6 +1,7 @@ import { - canonicalJson, encodeCrock, - stringToBytes + canonicalJson, + encodeCrock, + stringToBytes, } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; @@ -8,7 +9,11 @@ import { AnastasisClientFrame } from ".."; import { TextInput } from "../../../components/fields/TextInput"; import { AuthMethodSetupProps } from "./index"; -export function AuthMethodPostSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodPostSetup({ + addAuthMethod, + cancel, + configured, +}: AuthMethodSetupProps): VNode { const [fullName, setFullName] = useState(""); const [street, setStreet] = useState(""); const [city, setCity] = useState(""); @@ -32,68 +37,83 @@ export function AuthMethodPostSetup({ addAuthMethod, cancel, configured }: AuthM }); }; - const errors = !fullName ? 'The full name is missing' : ( - !street ? 'The street is missing' : ( - !city ? 'The city is missing' : ( - !postcode ? 'The postcode is missing' : ( - !country ? 'The country is missing' : undefined - ) - ) - ) - ) + const errors = !fullName + ? "The full name is missing" + : !street + ? "The street is missing" + : !city + ? "The city is missing" + : !postcode + ? "The postcode is missing" + : !country + ? "The country is missing" + : undefined; return ( <AnastasisClientFrame hideNav title="Add postal authentication"> <p> - For postal letter authentication, you need to provide a postal - address. When recovering your secret, you will be asked to enter a - code that you will receive in a letter to that address. + For postal letter authentication, you need to provide a postal address. + When recovering your secret, you will be asked to enter a code that you + will receive in a letter to that address. </p> <div> - <TextInput - grabFocus - label="Full Name" - bind={[fullName, setFullName]} - /> + <TextInput grabFocus label="Full Name" bind={[fullName, setFullName]} /> </div> <div> - <TextInput - label="Street" - bind={[street, setStreet]} - /> + <TextInput label="Street" bind={[street, setStreet]} /> </div> <div> - <TextInput - label="City" bind={[city, setCity]} - /> + <TextInput label="City" bind={[city, setCity]} /> </div> <div> - <TextInput - label="Postal Code" bind={[postcode, setPostcode]} - /> + <TextInput label="Postal Code" bind={[postcode, setPostcode]} /> </div> <div> - <TextInput - label="Country" - bind={[country, setCountry]} - /> + <TextInput label="Country" bind={[country, setCountry]} /> </div> - {configured.length > 0 && <section class="section"> - <div class="block"> - Your postal code: - </div><div class="block"> - {configured.map((c, i) => { - return <div key={i} class="box" style={{ display: 'flex', justifyContent: 'space-between' }}> - <p style={{ marginBottom: 'auto', marginTop: 'auto' }}>{c.instructions}</p> - <div><button class="button is-danger" onClick={c.remove} >Delete</button></div> - </div> - })} - </div> - </section>} - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={cancel}>Cancel</button> + {configured.length > 0 && ( + <section class="section"> + <div class="block">Your postal code:</div> + <div class="block"> + {configured.map((c, i) => { + return ( + <div + key={i} + class="box" + style={{ display: "flex", justifyContent: "space-between" }} + > + <p style={{ marginBottom: "auto", marginTop: "auto" }}> + {c.instructions} + </p> + <div> + <button class="button is-danger" onClick={c.remove}> + Delete + </button> + </div> + </div> + ); + })} + </div> + </section> + )} + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={cancel}> + Cancel + </button> <span data-tooltip={errors}> - <button class="button is-info" disabled={errors !== undefined} onClick={addPostAuth}>Add</button> + <button + class="button is-info" + disabled={errors !== undefined} + onClick={addPostAuth} + > + Add + </button> </span> </div> </AnastasisClientFrame> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx index 99451090b..3b67ee884 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx @@ -15,42 +15,46 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/post', + title: "Pages/recovery/SolveChallenge/AuthMethods/post", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'post' - -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +const type: KnownAuthMethods = "post"; + +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx index 7e3c45abe..ee001ebe9 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode { return ( <AnastasisClientFrame hideNav title="Recovery problem"> <div>invalid state</div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={() => reducer.back()}>Back</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={() => reducer.back()}> + Back + </button> </div> </AnastasisClientFrame> ); @@ -62,8 +70,7 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise<void> { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,16 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( <AnastasisClientFrame hideNav title="Add email authentication"> <SolveOverviewFeedbackDisplay feedback={feedback} /> - <p> - Wait for the answer - </p> + <p>Wait for the answer</p> <TextInput label="Answer" grabFocus bind={[answer, setAnswer]} /> <div @@ -96,9 +101,11 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}> - Confirm - </AsyncButton>} + {!shouldHideConfirm && ( + <AsyncButton class="button is-info" onClick={onNext}> + Confirm + </AsyncButton> + )} </div> </AnastasisClientFrame> ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx index 0c3ee2b77..991301cbf 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx @@ -16,51 +16,69 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/Question', + title: "Pages/backup/AuthorizationMethod/AuthMethods/Question", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'question' +const type: KnownAuthMethods = "question"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Is integer factorization polynomial? (non-quantum computer)', - remove: () => null - }] -}); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: + "Is integer factorization polynomial? (non-quantum computer)", + remove: () => null, + }, + ], + }, +); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Does P equal NP?', - remove: () => null - },{ - challenge: 'asd', - type, - instructions: 'Are continuous groups automatically differential groups?', - remove: () => null - }] -}); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "Does P equal NP?", + remove: () => null, + }, + { + challenge: "asd", + type, + instructions: + "Are continuous groups automatically differential groups?", + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx index 780bfcb82..0a14021dd 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.tsx @@ -1,27 +1,31 @@ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; +import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { AuthMethodSetupProps } from "./index"; import { AnastasisClientFrame } from "../index"; import { TextInput } from "../../../components/fields/TextInput"; -export function AuthMethodQuestionSetup({ cancel, addAuthMethod, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodQuestionSetup({ + cancel, + addAuthMethod, + configured, +}: AuthMethodSetupProps): VNode { const [questionText, setQuestionText] = useState(""); const [answerText, setAnswerText] = useState(""); - const addQuestionAuth = (): void => addAuthMethod({ - authentication_method: { - type: "question", - instructions: questionText, - challenge: encodeCrock(stringToBytes(answerText)), - }, - }); + const addQuestionAuth = (): void => + addAuthMethod({ + authentication_method: { + type: "question", + instructions: questionText, + challenge: encodeCrock(stringToBytes(answerText)), + }, + }); - const errors = !questionText ? "Add your security question" : ( - !answerText ? 'Add the answer to your question' : undefined - ) + const errors = !questionText + ? "Add your security question" + : !answerText + ? "Add the answer to your question" + : undefined; return ( <AnastasisClientFrame hideNav title="Add Security Question"> <div> @@ -36,7 +40,8 @@ export function AuthMethodQuestionSetup({ cancel, addAuthMethod, configured }: A label="Security question" grabFocus placeholder="Your question" - bind={[questionText, setQuestionText]} /> + bind={[questionText, setQuestionText]} + /> </div> <div> <TextInput @@ -46,25 +51,53 @@ export function AuthMethodQuestionSetup({ cancel, addAuthMethod, configured }: A /> </div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={cancel}>Cancel</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={cancel}> + Cancel + </button> <span data-tooltip={errors}> - <button class="button is-info" disabled={errors !== undefined} onClick={addQuestionAuth}>Add</button> + <button + class="button is-info" + disabled={errors !== undefined} + onClick={addQuestionAuth} + > + Add + </button> </span> </div> - {configured.length > 0 && <section class="section"> - <div class="block"> - Your security questions: - </div><div class="block"> - {configured.map((c, i) => { - return <div key={i} class="box" style={{ display: 'flex', justifyContent: 'space-between' }}> - <p style={{ marginBottom: 'auto', marginTop: 'auto' }}>{c.instructions}</p> - <div><button class="button is-danger" onClick={c.remove} >Delete</button></div> - </div> - })} - </div></section>} + {configured.length > 0 && ( + <section class="section"> + <div class="block">Your security questions:</div> + <div class="block"> + {configured.map((c, i) => { + return ( + <div + key={i} + class="box" + style={{ display: "flex", justifyContent: "space-between" }} + > + <p style={{ marginBottom: "auto", marginTop: "auto" }}> + {c.instructions} + </p> + <div> + <button class="button is-danger" onClick={c.remove}> + Delete + </button> + </div> + </div> + ); + })} + </div> + </section> + )} </div> - </AnastasisClientFrame > + </AnastasisClientFrame> ); } diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx index f0ec92d4d..1fa9fd6ec 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx @@ -15,186 +15,205 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/question', + title: "Pages/recovery/SolveChallenge/AuthMethods/question", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'question' +const type: KnownAuthMethods = "question"; -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); +); export const MessageFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.Message, - message: 'Challenge should be solved' - } - } - -} as ReducerState); - -export const ServerFailureFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], - policies: [], + message: "Challenge should be solved", + }, }, - selected_challenge_uuid: 'ASDASDSAD!1', - challenge_feedback: { - 'ASDASDSAD!1': { - state: ChallengeFeedbackStatus.ServerFailure, - http_status: 500, - error_response: "Couldn't connect to mysql" - } - } - } as ReducerState); +export const ServerFailureFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "ASDASDSAD!1", + challenge_feedback: { + "ASDASDSAD!1": { + state: ChallengeFeedbackStatus.ServerFailure, + http_status: 500, + error_response: "Couldn't connect to mysql", + }, + }, + } as ReducerState, +); + export const RedirectFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.Redirect, http_status: 302, - redirect_url: 'http://video.taler.net' - } - } - -} as ReducerState); - -export const MessageRateLimitExceededFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], - policies: [], + redirect_url: "http://video.taler.net", + }, }, - selected_challenge_uuid: 'ASDASDSAD!1', - challenge_feedback: { - 'ASDASDSAD!1': { - state: ChallengeFeedbackStatus.RateLimitExceeded, - } - } - } as ReducerState); +export const MessageRateLimitExceededFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "ASDASDSAD!1", + challenge_feedback: { + "ASDASDSAD!1": { + state: ChallengeFeedbackStatus.RateLimitExceeded, + }, + }, + } as ReducerState, +); + export const UnsupportedFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.Unsupported, http_status: 500, - unsupported_method: 'Question' - } - } - + unsupported_method: "Question", + }, + }, } as ReducerState); export const TruthUnknownFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.TruthUnknown, - } - } - + }, + }, } as ReducerState); export const AuthIbanFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.AuthIban, challenge_amount: "EUR:1", credit_iban: "DE12345789000", @@ -210,30 +229,30 @@ export const AuthIbanFeedback = createExample(TestedComponent[type].solve, { wire_transfer_subject: "foo", }, method: "iban", - } - } - + }, + }, } as ReducerState); export const PaymentFeedback = createExample(TestedComponent[type].solve, { ...reducerStatesExample.challengeSolving, recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'ASDASDSAD!1' - }], + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "ASDASDSAD!1", + }, + ], policies: [], }, - selected_challenge_uuid: 'ASDASDSAD!1', + selected_challenge_uuid: "ASDASDSAD!1", challenge_feedback: { - 'ASDASDSAD!1': { + "ASDASDSAD!1": { state: ChallengeFeedbackStatus.Payment, - taler_pay_uri : "taler://pay/...", - provider : "https://localhost:8080/", - payment_secret : "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG" - } - } + taler_pay_uri: "taler://pay/...", + provider: "https://localhost:8080/", + payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG", + }, + }, } as ReducerState); - diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx index ee1c0028f..222789507 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode { return ( <AnastasisClientFrame hideNav title="Recovery problem"> <div>invalid state</div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={() => reducer.back()}>Back</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={() => reducer.back()}> + Back + </button> </div> </AnastasisClientFrame> ); @@ -62,8 +70,7 @@ export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise<void> { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,16 @@ export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( <AnastasisClientFrame hideNav title="Add email authentication"> <SolveOverviewFeedbackDisplay feedback={feedback} /> - <p> - Answer the question please - </p> + <p>Answer the question please</p> <TextInput label="Answer" grabFocus bind={[answer, setAnswer]} /> <div @@ -96,9 +101,11 @@ export function AuthMethodQuestionSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}> - Confirm - </AsyncButton>} + {!shouldHideConfirm && ( + <AsyncButton class="button is-info" onClick={onNext}> + Confirm + </AsyncButton> + )} </div> </AnastasisClientFrame> ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx index da2087ce1..3a44c7ad0 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx @@ -16,51 +16,67 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/Sms', + title: "Pages/backup/AuthorizationMethod/AuthMethods/Sms", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'sms' +const type: KnownAuthMethods = "sms"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'SMS to +11-1234-2345', - remove: () => null - }] -}); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "SMS to +11-1234-2345", + remove: () => null, + }, + ], + }, +); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'SMS to +11-1234-2345', - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: 'SMS to +11-5555-2345', - remove: () => null - }] -}); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: "SMS to +11-1234-2345", + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: "SMS to +11-5555-2345", + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.tsx index 9a0459d78..056b1b175 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.tsx @@ -1,14 +1,15 @@ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; +import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { useLayoutEffect, useRef, useState } from "preact/hooks"; import { AuthMethodSetupProps } from "."; import { PhoneNumberInput } from "../../../components/fields/NumberInput"; import { AnastasisClientFrame } from "../index"; -export function AuthMethodSmsSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodSmsSetup({ + addAuthMethod, + cancel, + configured, +}: AuthMethodSetupProps): VNode { const [mobileNumber, setMobileNumber] = useState(""); const addSmsAuth = (): void => { addAuthMethod({ @@ -23,7 +24,7 @@ export function AuthMethodSmsSetup({ addAuthMethod, cancel, configured }: AuthMe useLayoutEffect(() => { inputRef.current?.focus(); }, []); - const errors = !mobileNumber ? 'Add a mobile number' : undefined + const errors = !mobileNumber ? "Add a mobile number" : undefined; return ( <AnastasisClientFrame hideNav title="Add SMS authentication"> <div> @@ -37,23 +38,52 @@ export function AuthMethodSmsSetup({ addAuthMethod, cancel, configured }: AuthMe label="Mobile number" placeholder="Your mobile number" grabFocus - bind={[mobileNumber, setMobileNumber]} /> + bind={[mobileNumber, setMobileNumber]} + /> </div> - {configured.length > 0 && <section class="section"> - <div class="block"> - Your mobile numbers: - </div><div class="block"> - {configured.map((c, i) => { - return <div key={i} class="box" style={{ display: 'flex', justifyContent: 'space-between' }}> - <p style={{ marginTop: 'auto', marginBottom: 'auto' }}>{c.instructions}</p> - <div><button class="button is-danger" onClick={c.remove}>Delete</button></div> - </div> - })} - </div></section>} - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={cancel}>Cancel</button> + {configured.length > 0 && ( + <section class="section"> + <div class="block">Your mobile numbers:</div> + <div class="block"> + {configured.map((c, i) => { + return ( + <div + key={i} + class="box" + style={{ display: "flex", justifyContent: "space-between" }} + > + <p style={{ marginTop: "auto", marginBottom: "auto" }}> + {c.instructions} + </p> + <div> + <button class="button is-danger" onClick={c.remove}> + Delete + </button> + </div> + </div> + ); + })} + </div> + </section> + )} + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={cancel}> + Cancel + </button> <span data-tooltip={errors}> - <button class="button is-info" disabled={errors !== undefined} onClick={addSmsAuth}>Add</button> + <button + class="button is-info" + disabled={errors !== undefined} + onClick={addSmsAuth} + > + Add + </button> </span> </div> </div> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx index 76e769303..3dc3adb2b 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx @@ -15,42 +15,46 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/sms', + title: "Pages/recovery/SolveChallenge/AuthMethods/sms", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'sms' - -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +const type: KnownAuthMethods = "sms"; + +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx index ce7159bd0..8ee4d600a 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode { return ( <AnastasisClientFrame hideNav title="Recovery problem"> <div>invalid state</div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={() => reducer.back()}>Back</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={() => reducer.back()}> + Back + </button> </div> </AnastasisClientFrame> ); @@ -62,8 +70,7 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise<void> { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,18 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( <AnastasisClientFrame hideNav title="Add email authentication"> <SolveOverviewFeedbackDisplay feedback={feedback} /> <p> - An sms has been sent to "<b>{selectedChallenge.instructions}</b>". Type the code - below + An sms has been sent to "<b>{selectedChallenge.instructions}</b>". Type + the code below </p> <TextInput label="Answer" grabFocus bind={[answer, setAnswer]} /> @@ -97,9 +104,11 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}> - Confirm - </AsyncButton>} + {!shouldHideConfirm && ( + <AsyncButton class="button is-info" onClick={onNext}> + Confirm + </AsyncButton> + )} </div> </AnastasisClientFrame> ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx index c0a52924c..bc4628828 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx @@ -16,49 +16,65 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/TOTP', + title: "Pages/backup/AuthorizationMethod/AuthMethods/TOTP", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'totp' +const type: KnownAuthMethods = "totp"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Enter 8 digits code for "Anastasis"', - remove: () => null - }] -}); -export const WithMoreExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: 'Enter 8 digits code for "Anastasis1"', - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: 'Enter 8 digits code for "Anastasis2"', - remove: () => null - }] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: 'Enter 8 digits code for "Anastasis"', + remove: () => null, + }, + ], + }, +); +export const WithMoreExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: 'Enter 8 digits code for "Anastasis1"', + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: 'Enter 8 digits code for "Anastasis2"', + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx index a8ac499b2..1451aadc8 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx @@ -1,7 +1,4 @@ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; +import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useMemo, useState } from "preact/hooks"; import { AuthMethodSetupProps } from "./index"; @@ -10,30 +7,37 @@ import { TextInput } from "../../../components/fields/TextInput"; import { QR } from "../../../components/QR"; import { base32enc, computeTOTPandCheck } from "./totp"; -export function AuthMethodTotpSetup({ addAuthMethod, cancel, configured }: AuthMethodSetupProps): VNode { +export function AuthMethodTotpSetup({ + addAuthMethod, + cancel, + configured, +}: AuthMethodSetupProps): VNode { const [name, setName] = useState("anastasis"); const [test, setTest] = useState(""); - const digits = 8 + const digits = 8; const secretKey = useMemo(() => { - const array = new Uint8Array(32) - return window.crypto.getRandomValues(array) - }, []) + const array = new Uint8Array(32); + return window.crypto.getRandomValues(array); + }, []); const secret32 = base32enc(secretKey); - const totpURL = `otpauth://totp/${name}?digits=${digits}&secret=${secret32}` + const totpURL = `otpauth://totp/${name}?digits=${digits}&secret=${secret32}`; - const addTotpAuth = (): void => addAuthMethod({ - authentication_method: { - type: "totp", - instructions: `Enter ${digits} digits code for "${name}"`, - challenge: encodeCrock(stringToBytes(totpURL)), - }, - }); + const addTotpAuth = (): void => + addAuthMethod({ + authentication_method: { + type: "totp", + instructions: `Enter ${digits} digits code for "${name}"`, + challenge: encodeCrock(stringToBytes(totpURL)), + }, + }); const testCodeMatches = computeTOTPandCheck(secretKey, 8, parseInt(test, 10)); - const errors = !name ? 'The TOTP name is missing' : ( - !testCodeMatches ? 'The test code doesnt match' : undefined - ); + const errors = !name + ? "The TOTP name is missing" + : !testCodeMatches + ? "The test code doesnt match" + : undefined; return ( <AnastasisClientFrame hideNav title="Add TOTP authentication"> <p> @@ -42,10 +46,7 @@ export function AuthMethodTotpSetup({ addAuthMethod, cancel, configured }: AuthM with your TOTP App to import the TOTP secret into your TOTP App. </p> <div class="block"> - <TextInput - label="TOTP Name" - grabFocus - bind={[name, setName]} /> + <TextInput label="TOTP Name" grabFocus bind={[name, setName]} /> </div> <div style={{ height: 300 }}> <QR text={totpURL} /> @@ -53,25 +54,51 @@ export function AuthMethodTotpSetup({ addAuthMethod, cancel, configured }: AuthM <p> After scanning the code with your TOTP App, test it in the input below. </p> - <TextInput - label="Test code" - bind={[test, setTest]} /> - {configured.length > 0 && <section class="section"> - <div class="block"> - Your TOTP numbers: - </div><div class="block"> - {configured.map((c, i) => { - return <div key={i} class="box" style={{ display: 'flex', justifyContent: 'space-between' }}> - <p style={{ marginTop: 'auto', marginBottom: 'auto' }}>{c.instructions}</p> - <div><button class="button is-danger" onClick={c.remove}>Delete</button></div> - </div> - })} - </div></section>} + <TextInput label="Test code" bind={[test, setTest]} /> + {configured.length > 0 && ( + <section class="section"> + <div class="block">Your TOTP numbers:</div> + <div class="block"> + {configured.map((c, i) => { + return ( + <div + key={i} + class="box" + style={{ display: "flex", justifyContent: "space-between" }} + > + <p style={{ marginTop: "auto", marginBottom: "auto" }}> + {c.instructions} + </p> + <div> + <button class="button is-danger" onClick={c.remove}> + Delete + </button> + </div> + </div> + ); + })} + </div> + </section> + )} <div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={cancel}>Cancel</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={cancel}> + Cancel + </button> <span data-tooltip={errors}> - <button class="button is-info" disabled={errors !== undefined} onClick={addTotpAuth}>Add</button> + <button + class="button is-info" + disabled={errors !== undefined} + onClick={addTotpAuth} + > + Add + </button> </span> </div> </div> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx index a301931b2..8743c5a73 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx @@ -15,42 +15,46 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/totp', + title: "Pages/recovery/SolveChallenge/AuthMethods/totp", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'totp' - -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +const type: KnownAuthMethods = "totp"; + +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx index 30fc44f0e..98c2e51df 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodTotpSolve({ id }: AuthMethodSolveProps): VNode { return ( <AnastasisClientFrame hideNav title="Recovery problem"> <div>invalid state</div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={() => reducer.back()}>Back</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={() => reducer.back()}> + Back + </button> </div> </AnastasisClientFrame> ); @@ -62,8 +70,7 @@ export function AuthMethodTotpSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise<void> { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,16 @@ export function AuthMethodTotpSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( <AnastasisClientFrame hideNav title="Add email authentication"> <SolveOverviewFeedbackDisplay feedback={feedback} /> - <p> - enter the totp solution - </p> + <p>enter the totp solution</p> <TextInput label="Answer" grabFocus bind={[answer, setAnswer]} /> <div @@ -96,9 +101,11 @@ export function AuthMethodTotpSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}> - Confirm - </AsyncButton>} + {!shouldHideConfirm && ( + <AsyncButton class="button is-info" onClick={onNext}> + Confirm + </AsyncButton> + )} </div> </AnastasisClientFrame> ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.stories.tsx index 52e897c60..4aad0a097 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.stories.tsx @@ -16,51 +16,68 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; -import logoImage from '../../../assets/logo.jpeg' +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; +import logoImage from "../../../assets/logo.jpeg"; export default { - title: 'Pages/backup/AuthorizationMethod/AuthMethods/Video', + title: "Pages/backup/AuthorizationMethod/AuthMethods/Video", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'video' +const type: KnownAuthMethods = "video"; -export const Empty = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [] -}); +export const Empty = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [], + }, +); -export const WithOneExample = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: logoImage, - remove: () => null - }] -}); +export const WithOneExample = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: logoImage, + remove: () => null, + }, + ], + }, +); -export const WithMoreExamples = createExample(TestedComponent[type].setup, reducerStatesExample.authEditing, { - configured: [{ - challenge: 'qwe', - type, - instructions: logoImage, - remove: () => null - },{ - challenge: 'qwe', - type, - instructions: logoImage, - remove: () => null - }] -}); +export const WithMoreExamples = createExample( + TestedComponent[type].setup, + reducerStatesExample.authEditing, + { + configured: [ + { + challenge: "qwe", + type, + instructions: logoImage, + remove: () => null, + }, + { + challenge: "qwe", + type, + instructions: logoImage, + remove: () => null, + }, + ], + }, +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.tsx index 22abe4a49..672b23500 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSetup.tsx @@ -1,53 +1,86 @@ -import { - encodeCrock, - stringToBytes -} from "@gnu-taler/taler-util"; +import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { ImageInput } from "../../../components/fields/ImageInput"; import { AuthMethodSetupProps } from "./index"; import { AnastasisClientFrame } from "../index"; -export function AuthMethodVideoSetup({cancel, addAuthMethod, configured}: AuthMethodSetupProps): VNode { +export function AuthMethodVideoSetup({ + cancel, + addAuthMethod, + configured, +}: AuthMethodSetupProps): VNode { const [image, setImage] = useState(""); const addVideoAuth = (): void => { addAuthMethod({ authentication_method: { type: "video", - instructions: 'Join a video call', + instructions: "Join a video call", challenge: encodeCrock(stringToBytes(image)), }, - }) + }); }; return ( <AnastasisClientFrame hideNav title="Add video authentication"> <p> - For video identification, you need to provide a passport-style - photograph. When recovering your secret, you will be asked to join a - video call. During that call, a human will use the photograph to - verify your identity. + For video identification, you need to provide a passport-style + photograph. When recovering your secret, you will be asked to join a + video call. During that call, a human will use the photograph to verify + your identity. </p> - <div style={{textAlign:'center'}}> + <div style={{ textAlign: "center" }}> <ImageInput label="Choose photograph" grabFocus - bind={[image, setImage]} /> + bind={[image, setImage]} + /> </div> - {configured.length > 0 && <section class="section"> + {configured.length > 0 && ( + <section class="section"> + <div class="block">Your photographs:</div> <div class="block"> - Your photographs: - </div><div class="block"> {configured.map((c, i) => { - return <div key={i} class="box" style={{ display: 'flex', justifyContent: 'space-between' }}> - <img style={{ marginTop: 'auto', marginBottom: 'auto', width: 100, height:100, border: 'solid 1px black' }} src={c.instructions} /> - <div style={{marginTop: 'auto', marginBottom: 'auto'}}><button class="button is-danger" onClick={c.remove}>Delete</button></div> - </div> + return ( + <div + key={i} + class="box" + style={{ display: "flex", justifyContent: "space-between" }} + > + <img + style={{ + marginTop: "auto", + marginBottom: "auto", + width: 100, + height: 100, + border: "solid 1px black", + }} + src={c.instructions} + /> + <div style={{ marginTop: "auto", marginBottom: "auto" }}> + <button class="button is-danger" onClick={c.remove}> + Delete + </button> + </div> + </div> + ); })} - </div></section>} + </div> + </section> + )} <div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={cancel}>Cancel</button> - <button class="button is-info" onClick={addVideoAuth}>Add</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={cancel}> + Cancel + </button> + <button class="button is-info" onClick={addVideoAuth}> + Add + </button> </div> </div> </AnastasisClientFrame> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.stories.tsx index 5c4976b87..7c5511c5a 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.stories.tsx @@ -15,42 +15,46 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { ChallengeFeedbackStatus, ReducerState } from 'anastasis-core'; -import { createExample, reducerStatesExample } from '../../../utils'; -import { authMethods as TestedComponent, KnownAuthMethods } from './index'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { ChallengeFeedbackStatus, ReducerState } from "anastasis-core"; +import { createExample, reducerStatesExample } from "../../../utils"; +import { authMethods as TestedComponent, KnownAuthMethods } from "./index"; export default { - title: 'Pages/recovery/SolveChallenge/AuthMethods/video', + title: "Pages/recovery/SolveChallenge/AuthMethods/video", component: TestedComponent, args: { order: 5, }, argTypes: { - onUpdate: { action: 'onUpdate' }, - onBack: { action: 'onBack' }, + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, }, }; -const type: KnownAuthMethods = 'video' - -export const WithoutFeedback = createExample(TestedComponent[type].solve, { - ...reducerStatesExample.challengeSolving, - recovery_information: { - challenges: [{ - cost: 'USD:1', - instructions: 'does P equals NP?', - type: 'question', - uuid: 'uuid-1' - }], - policies: [], +const type: KnownAuthMethods = "video"; + +export const WithoutFeedback = createExample( + TestedComponent[type].solve, + { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [ + { + cost: "USD:1", + instructions: "does P equals NP?", + type: "question", + uuid: "uuid-1", + }, + ], + policies: [], + }, + selected_challenge_uuid: "uuid-1", + } as ReducerState, + { + id: "uuid-1", }, - selected_challenge_uuid: 'uuid-1', -} as ReducerState, { - id: 'uuid-1', -}); - +); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.tsx index 79401028a..efadb9a9a 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodVideoSolve.tsx @@ -44,8 +44,16 @@ export function AuthMethodVideoSolve({ id }: AuthMethodSolveProps): VNode { return ( <AnastasisClientFrame hideNav title="Recovery problem"> <div>invalid state</div> - <div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> - <button class="button" onClick={() => reducer.back()}>Back</button> + <div + style={{ + marginTop: "2em", + display: "flex", + justifyContent: "space-between", + }} + > + <button class="button" onClick={() => reducer.back()}> + Back + </button> </div> </AnastasisClientFrame> ); @@ -62,8 +70,7 @@ export function AuthMethodVideoSolve({ id }: AuthMethodSolveProps): VNode { challenges[ch.uuid] = ch; } const selectedChallenge = challenges[selectedUuid]; - const feedback = challengeFeedback[selectedUuid] - + const feedback = challengeFeedback[selectedUuid]; async function onNext(): Promise<void> { return reducer?.transition("solve_challenge", { answer }); @@ -72,18 +79,16 @@ export function AuthMethodVideoSolve({ id }: AuthMethodSolveProps): VNode { reducer?.back(); } - - const shouldHideConfirm = feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded - || feedback?.state === ChallengeFeedbackStatus.Redirect - || feedback?.state === ChallengeFeedbackStatus.Unsupported - || feedback?.state === ChallengeFeedbackStatus.TruthUnknown + const shouldHideConfirm = + feedback?.state === ChallengeFeedbackStatus.RateLimitExceeded || + feedback?.state === ChallengeFeedbackStatus.Redirect || + feedback?.state === ChallengeFeedbackStatus.Unsupported || + feedback?.state === ChallengeFeedbackStatus.TruthUnknown; return ( <AnastasisClientFrame hideNav title="Add email authentication"> <SolveOverviewFeedbackDisplay feedback={feedback} /> - <p> - You are gonna be called to check your identity - </p> + <p>You are gonna be called to check your identity</p> <TextInput label="Answer" grabFocus bind={[answer, setAnswer]} /> <div @@ -96,9 +101,11 @@ export function AuthMethodVideoSolve({ id }: AuthMethodSolveProps): VNode { <button class="button" onClick={onCancel}> Cancel </button> - {!shouldHideConfirm && <AsyncButton class="button is-info" onClick={onNext}> - Confirm - </AsyncButton>} + {!shouldHideConfirm && ( + <AsyncButton class="button is-info" onClick={onNext}> + Confirm + </AsyncButton> + )} </div> </AnastasisClientFrame> ); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/index.tsx b/packages/anastasis-webui/src/pages/home/authMethod/index.tsx index 8b0126ce7..b4f649488 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/index.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/index.tsx @@ -1,9 +1,9 @@ import { AuthMethod } from "anastasis-core"; import { h, VNode } from "preact"; -import postalIcon from '../../../assets/icons/auth_method/postal.svg'; -import questionIcon from '../../../assets/icons/auth_method/question.svg'; -import smsIcon from '../../../assets/icons/auth_method/sms.svg'; -import videoIcon from '../../../assets/icons/auth_method/video.svg'; +import postalIcon from "../../../assets/icons/auth_method/postal.svg"; +import questionIcon from "../../../assets/icons/auth_method/question.svg"; +import smsIcon from "../../../assets/icons/auth_method/sms.svg"; +import videoIcon from "../../../assets/icons/auth_method/video.svg"; import { AuthMethodEmailSetup as EmailSetup } from "./AuthMethodEmailSetup"; import { AuthMethodEmailSolve as EmailSolve } from "./AuthMethodEmailSolve"; import { AuthMethodIbanSetup as IbanSetup } from "./AuthMethodIbanSetup"; @@ -20,8 +20,7 @@ import { AuthMethodSmsSolve as SmsSolve } from "./AuthMethodSmsSolve"; import { AuthMethodTotpSolve as TotpSolve } from "./AuthMethodTotpSolve"; import { AuthMethodVideoSolve as VideoSolve } from "./AuthMethodVideoSolve"; - -export type AuthMethodWithRemove = AuthMethod & { remove: () => void } +export type AuthMethodWithRemove = AuthMethod & { remove: () => void }; export interface AuthMethodSetupProps { method: string; @@ -43,10 +42,18 @@ interface AuthMethodConfiguration { } // export type KnownAuthMethods = "sms" | "email" | "post" | "question" | "video" | "totp" | "iban"; -const ALL_METHODS = ['sms', 'email', 'post', 'question', 'video' , 'totp', 'iban'] as const; -export type KnownAuthMethods = (typeof ALL_METHODS)[number]; +const ALL_METHODS = [ + "sms", + "email", + "post", + "question", + "video", + "totp", + "iban", +] as const; +export type KnownAuthMethods = typeof ALL_METHODS[number]; export function isKnownAuthMethods(value: string): value is KnownAuthMethods { - return ALL_METHODS.includes(value as KnownAuthMethods) + return ALL_METHODS.includes(value as KnownAuthMethods); } type KnowMethodConfig = { @@ -96,5 +103,5 @@ export const authMethods: KnowMethodConfig = { setup: VideoSetup, solve: VideoSolve, skip: true, - } -}
\ No newline at end of file + }, +}; diff --git a/packages/anastasis-webui/src/pages/home/authMethod/totp.ts b/packages/anastasis-webui/src/pages/home/authMethod/totp.ts index 0bc3feaf8..c2288671c 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/totp.ts +++ b/packages/anastasis-webui/src/pages/home/authMethod/totp.ts @@ -1,54 +1,61 @@ /* eslint-disable @typescript-eslint/camelcase */ -import jssha from 'jssha' +import jssha from "jssha"; -const SEARCH_RANGE = 16 -const timeStep = 30 +const SEARCH_RANGE = 16; +const timeStep = 30; -export function computeTOTPandCheck(secretKey: Uint8Array, digits: number, code: number): boolean { - const now = new Date().getTime() +export function computeTOTPandCheck( + secretKey: Uint8Array, + digits: number, + code: number, +): boolean { + const now = new Date().getTime(); const epoch = Math.floor(Math.round(now / 1000.0) / timeStep); for (let ms = -SEARCH_RANGE; ms < SEARCH_RANGE; ms++) { const movingFactor = (epoch + ms).toString(16).padStart(16, "0"); - const hmacSha = new jssha('SHA-1', 'HEX', { hmacKey: { value: secretKey, format: 'UINT8ARRAY' } }); + const hmacSha = new jssha("SHA-1", "HEX", { + hmacKey: { value: secretKey, format: "UINT8ARRAY" }, + }); hmacSha.update(movingFactor); - const hmac_text = hmacSha.getHMAC('UINT8ARRAY'); + const hmac_text = hmacSha.getHMAC("UINT8ARRAY"); - const offset = (hmac_text[hmac_text.length - 1] & 0xf) + const offset = hmac_text[hmac_text.length - 1] & 0xf; - const otp = (( - (hmac_text[offset + 0] << 24) + - (hmac_text[offset + 1] << 16) + - (hmac_text[offset + 2] << 8) + - (hmac_text[offset + 3]) - ) & 0x7fffffff) % Math.pow(10, digits) + const otp = + (((hmac_text[offset + 0] << 24) + + (hmac_text[offset + 1] << 16) + + (hmac_text[offset + 2] << 8) + + hmac_text[offset + 3]) & + 0x7fffffff) % + Math.pow(10, digits); - if (otp == code) return true + if (otp == code) return true; } - return false + return false; } -const encTable__ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".split('') +const encTable__ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".split(""); export function base32enc(buffer: Uint8Array): string { - let rpos = 0 - let bits = 0 - let vbit = 0 + let rpos = 0; + let bits = 0; + let vbit = 0; - let result = "" - while ((rpos < buffer.length) || (vbit > 0)) { - if ((rpos < buffer.length) && (vbit < 5)) { + let result = ""; + while (rpos < buffer.length || vbit > 0) { + if (rpos < buffer.length && vbit < 5) { bits = (bits << 8) | buffer[rpos++]; vbit += 8; } if (vbit < 5) { - bits <<= (5 - vbit); + bits <<= 5 - vbit; vbit = 5; } result += encTable__[(bits >> (vbit - 5)) & 31]; vbit -= 5; } - return result + return result; } // const array = new Uint8Array(256) |