diff options
author | Florian Dold <florian@dold.me> | 2022-04-13 19:32:12 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2022-04-13 19:32:17 +0200 |
commit | 5054ff6c6d200e8d4d7658e001b0bcb1936d6876 (patch) | |
tree | 325c7ad151554cc5927c266f0331eb4fdad57516 | |
parent | ec9aed276a5c837a9a93bae2a4c2e61d77256cc1 (diff) |
anastasis-webui: make TOTP work again
11 files changed, 93 insertions, 42 deletions
diff --git a/packages/anastasis-core/src/index.ts b/packages/anastasis-core/src/index.ts index 68ecc5173..98aba2ce6 100644 --- a/packages/anastasis-core/src/index.ts +++ b/packages/anastasis-core/src/index.ts @@ -14,7 +14,6 @@ import { getRandomBytes, hash, HttpStatusCode, - j2s, Logger, parsePayUri, stringToBytes, @@ -27,8 +26,7 @@ import { import { anastasisData } from "./anastasis-data.js"; import { EscrowConfigurationResponse, - IbanExternalAuthResponse, - RecoveryMetaResponse as RecoveryMetaResponse, + RecoveryMetaResponse, TruthUploadRequest, } from "./provider-types.js"; import { diff --git a/packages/anastasis-webui/package.json b/packages/anastasis-webui/package.json index 96ff60b41..2327b5e12 100644 --- a/packages/anastasis-webui/package.json +++ b/packages/anastasis-webui/package.json @@ -15,6 +15,7 @@ "pretty": "prettier --write src", "storybook": "start-storybook -p 6006" }, + "type": "module", "eslintConfig": { "parser": "@typescript-eslint/parser", "extends": [ diff --git a/packages/anastasis-webui/preact.config.js b/packages/anastasis-webui/preact.config.js index f9a8d6cba..3f6b69016 100644 --- a/packages/anastasis-webui/preact.config.js +++ b/packages/anastasis-webui/preact.config.js @@ -28,7 +28,7 @@ const commitHash = cp.execSync("git rev-parse --short HEAD").toString(); export default { webpack(config, env, helpers) { - // add __VERSION__ to be use in the html + // add __VERSION__ to be used in the html config.plugins.push( new DefinePlugin({ "process.env.__VERSION__": JSON.stringify( diff --git a/packages/anastasis-webui/src/components/menu/LangSelector.tsx b/packages/anastasis-webui/src/components/menu/LangSelector.tsx index fa22a29c0..3566f0c26 100644 --- a/packages/anastasis-webui/src/components/menu/LangSelector.tsx +++ b/packages/anastasis-webui/src/components/menu/LangSelector.tsx @@ -23,7 +23,7 @@ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import langIcon from "../../assets/icons/languageicon.svg"; import { useTranslationContext } from "../../context/translation"; -import { strings as messages } from "../../i18n/strings"; +import { strings as messages } from "../../i18n/strings.js"; type LangsNames = { [P in keyof typeof messages]: string; diff --git a/packages/anastasis-webui/src/components/menu/SideBar.tsx b/packages/anastasis-webui/src/components/menu/SideBar.tsx index 023b2b63c..e265a817d 100644 --- a/packages/anastasis-webui/src/components/menu/SideBar.tsx +++ b/packages/anastasis-webui/src/components/menu/SideBar.tsx @@ -78,8 +78,7 @@ export function Sidebar({ mobile }: Props): VNode { </div> </li> )} - {reducer.currentReducerState && - reducer.currentReducerState.backup_state ? ( + {reducer.currentReducerState?.reducer_type === "backup" ? ( <Fragment> <li class={ @@ -191,8 +190,7 @@ export function Sidebar({ mobile }: Props): VNode { </li> </Fragment> ) : ( - reducer.currentReducerState && - reducer.currentReducerState?.recovery_state && ( + reducer.currentReducerState?.reducer_type === "recovery" && ( <Fragment> <li class={ diff --git a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts index b1c17eb96..7274c3d03 100644 --- a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts +++ b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts @@ -199,7 +199,7 @@ export function useAnastasisReducer(): AnastasisReducerApi { s = await reduceAction(anastasisState.reducerState!, action, args); } console.log("got response from reducer", s); - if (s.code) { + if (s.reducer_type === "error") { console.log("response is an error"); setAnastasisState({ ...anastasisState, currentError: s }); } else { @@ -223,7 +223,7 @@ export function useAnastasisReducer(): AnastasisReducerApi { } else { s = await getBackupStartState(); } - if (s.code !== undefined) { + if (s.reducer_type === "error") { setAnastasisState({ ...anastasisState, currentError: s, @@ -274,7 +274,7 @@ export function useAnastasisReducer(): AnastasisReducerApi { } else { s = await getRecoveryStartState(); } - if (s.code !== undefined) { + if (s.reducer_type === "error") { setAnastasisState({ ...anastasisState, currentError: s, @@ -296,8 +296,10 @@ export function useAnastasisReducer(): AnastasisReducerApi { return; } if ( - reducerState.backup_state === BackupStates.ContinentSelecting || - reducerState.recovery_state === RecoveryStates.ContinentSelecting + (reducerState.reducer_type === "backup" && + reducerState.backup_state === BackupStates.ContinentSelecting) || + (reducerState.reducer_type === "recovery" && + reducerState.recovery_state === RecoveryStates.ContinentSelecting) ) { setAnastasisState({ ...anastasisState, @@ -327,7 +329,7 @@ export function useAnastasisReducer(): AnastasisReducerApi { } const s = txHandle.transactionState; console.log("transaction finished, new state", s); - if (s.code !== undefined) { + if (s.reducer_type === "error") { setAnastasisState({ ...anastasisState, currentError: txHandle.transactionState, @@ -355,7 +357,7 @@ class ReducerTxImpl implements ReducerTransactionHandle { console.log("making transition in transaction", action); this.transactionState = s; // Abort transaction as soon as we transition into an error state. - if (this.transactionState.code !== undefined) { + if (this.transactionState.reducer_type === "error") { throw Error("transition resulted in error"); } return this.transactionState; diff --git a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx index 501415c40..257c28075 100644 --- a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx @@ -1,6 +1,6 @@ import { UserAttributeSpec, validators } from "@gnu-taler/anastasis-core"; import { isAfter, parse } from "date-fns"; -import { Fragment, h, VNode } from "preact"; +import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { DateInput } from "../../components/fields/DateInput"; import { PhoneNumberInput } from "../../components/fields/NumberInput"; @@ -72,7 +72,10 @@ export function AttributeEntryScreen(): VNode { const doConfirm = async () => { await reducer.transition("enter_user_attributes", { - identity_attributes: attrs, + identity_attributes: { + application_id: "anastasis-standalone", + ...attrs, + }, }); }; diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx index e0f0cc5e2..a191fb9e6 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx @@ -7,6 +7,11 @@ import { TextInput } from "../../../components/fields/TextInput"; import { QR } from "../../../components/QR"; import { base32enc, computeTOTPandCheck } from "./totp"; +/** + * This is hard-coded in the protocol for TOTP auth. + */ +const ANASTASIS_TOTP_DIGITS = 8; + export function AuthMethodTotpSetup({ addAuthMethod, cancel, @@ -14,20 +19,20 @@ export function AuthMethodTotpSetup({ }: AuthMethodSetupProps): VNode { const [name, setName] = useState("anastasis"); const [test, setTest] = useState(""); - const digits = 8; const secretKey = useMemo(() => { 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=${ANASTASIS_TOTP_DIGITS}&secret=${secret32}`; const addTotpAuth = (): void => addAuthMethod({ authentication_method: { type: "totp", - instructions: `Enter ${digits} digits code for "${name}"`, - challenge: encodeCrock(stringToBytes(totpURL)), + instructions: `Enter ${ANASTASIS_TOTP_DIGITS} digits code for "${name}"`, + challenge: encodeCrock(secretKey), }, }); diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx index ce7b2e545..ee4937441 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.tsx @@ -11,7 +11,7 @@ import { AnastasisClientFrame } from "../index"; import { SolveOverviewFeedbackDisplay } from "../SolveScreen"; import { AuthMethodSolveProps } from "./index"; -export function AuthMethodTotpSolve({ id }: AuthMethodSolveProps): VNode { +export function AuthMethodTotpSolve(props: AuthMethodSolveProps): VNode { const [answerCode, setAnswerCode] = useState(""); const reducer = useAnastasisContext(); @@ -74,7 +74,7 @@ export function AuthMethodTotpSolve({ id }: AuthMethodSolveProps): VNode { async function onNext(): Promise<void> { console.log(`sending TOTP code '${answerCode}'`); return reducer?.transition("solve_challenge", { - pin: Number.parseInt(answerCode), + answer: answerCode, }); } function onCancel(): void { diff --git a/packages/anastasis-webui/src/pages/home/index.tsx b/packages/anastasis-webui/src/pages/home/index.tsx index ca6da8474..7d191ae04 100644 --- a/packages/anastasis-webui/src/pages/home/index.tsx +++ b/packages/anastasis-webui/src/pages/home/index.tsx @@ -34,7 +34,7 @@ import { StartScreen } from "./StartScreen"; import { TruthsPayingScreen } from "./TruthsPayingScreen"; function isBackup(reducer: AnastasisReducerApi): boolean { - return !!reducer.currentReducerState?.backup_state; + return reducer.currentReducerState?.reducer_type === "backup"; } export function withProcessLabel( @@ -171,57 +171,96 @@ function AnastasisClientImpl(): VNode { console.log("state", reducer.currentReducerState); if ( - state.backup_state === BackupStates.ContinentSelecting || - state.recovery_state === RecoveryStates.ContinentSelecting || - state.backup_state === BackupStates.CountrySelecting || - state.recovery_state === RecoveryStates.CountrySelecting + (state.reducer_type === "backup" && + state.backup_state === BackupStates.ContinentSelecting) || + (state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.ContinentSelecting) || + (state.reducer_type === "backup" && + state.backup_state === BackupStates.CountrySelecting) || + (state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.CountrySelecting) ) { return <ContinentSelectionScreen />; } if ( - state.backup_state === BackupStates.UserAttributesCollecting || - state.recovery_state === RecoveryStates.UserAttributesCollecting + (state.reducer_type === "backup" && + state.backup_state === BackupStates.UserAttributesCollecting) || + (state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.UserAttributesCollecting) ) { return <AttributeEntryScreen />; } - if (state.backup_state === BackupStates.AuthenticationsEditing) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.AuthenticationsEditing + ) { return <AuthenticationEditorScreen />; } - if (state.backup_state === BackupStates.PoliciesReviewing) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.PoliciesReviewing + ) { return <ReviewPoliciesScreen />; } - if (state.backup_state === BackupStates.SecretEditing) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.SecretEditing + ) { return <SecretEditorScreen />; } - if (state.backup_state === BackupStates.BackupFinished) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.BackupFinished + ) { return <BackupFinishedScreen />; } - if (state.backup_state === BackupStates.TruthsPaying) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.TruthsPaying + ) { return <TruthsPayingScreen />; } - if (state.backup_state === BackupStates.PoliciesPaying) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.PoliciesPaying + ) { return <PoliciesPayingScreen />; } - if (state.recovery_state === RecoveryStates.SecretSelecting) { + if ( + state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.SecretSelecting + ) { return <SecretSelectionScreen />; } - if (state.recovery_state === RecoveryStates.ChallengeSelecting) { + if ( + state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.ChallengeSelecting + ) { return <ChallengeOverviewScreen />; } - if (state.recovery_state === RecoveryStates.ChallengeSolving) { + if ( + state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.ChallengeSolving + ) { return <SolveScreen />; } - if (state.recovery_state === RecoveryStates.RecoveryFinished) { + if ( + state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.RecoveryFinished + ) { return <RecoveryFinishedScreen />; } - if (state.recovery_state === RecoveryStates.ChallengePaying) { + if ( + state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.ChallengePaying + ) { return <ChallengePayingScreen />; } console.log("unknown state", reducer.currentReducerState); diff --git a/packages/anastasis-webui/src/utils/index.tsx b/packages/anastasis-webui/src/utils/index.tsx index 2209fd974..51a074fe9 100644 --- a/packages/anastasis-webui/src/utils/index.tsx +++ b/packages/anastasis-webui/src/utils/index.tsx @@ -17,6 +17,11 @@ export function createExample<Props>( <AnastasisProvider value={{ currentReducerState, + discoverMore: async () => {}, + discoverStart: async () => {}, + discoveryState: { + state: "none", + }, currentError: undefined, back: async () => { null; |