From 5054ff6c6d200e8d4d7658e001b0bcb1936d6876 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 13 Apr 2022 19:32:12 +0200 Subject: [PATCH] anastasis-webui: make TOTP work again --- packages/anastasis-core/src/index.ts | 4 +- packages/anastasis-webui/package.json | 1 + packages/anastasis-webui/preact.config.js | 2 +- .../src/components/menu/LangSelector.tsx | 2 +- .../src/components/menu/SideBar.tsx | 6 +- .../src/hooks/use-anastasis-reducer.ts | 16 ++-- .../src/pages/home/AttributeEntryScreen.tsx | 7 +- .../home/authMethod/AuthMethodTotpSetup.tsx | 13 +++- .../home/authMethod/AuthMethodTotpSolve.tsx | 4 +- .../anastasis-webui/src/pages/home/index.tsx | 75 ++++++++++++++----- packages/anastasis-webui/src/utils/index.tsx | 5 ++ 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 { )} - {reducer.currentReducerState && - reducer.currentReducerState.backup_state ? ( + {reducer.currentReducerState?.reducer_type === "backup" ? (
  • ) : ( - reducer.currentReducerState && - reducer.currentReducerState?.recovery_state && ( + reducer.currentReducerState?.reducer_type === "recovery" && (
  • { 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 { 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 ; } 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 ; } - if (state.backup_state === BackupStates.AuthenticationsEditing) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.AuthenticationsEditing + ) { return ; } - if (state.backup_state === BackupStates.PoliciesReviewing) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.PoliciesReviewing + ) { return ; } - if (state.backup_state === BackupStates.SecretEditing) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.SecretEditing + ) { return ; } - if (state.backup_state === BackupStates.BackupFinished) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.BackupFinished + ) { return ; } - if (state.backup_state === BackupStates.TruthsPaying) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.TruthsPaying + ) { return ; } - if (state.backup_state === BackupStates.PoliciesPaying) { + if ( + state.reducer_type === "backup" && + state.backup_state === BackupStates.PoliciesPaying + ) { return ; } - if (state.recovery_state === RecoveryStates.SecretSelecting) { + if ( + state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.SecretSelecting + ) { return ; } - if (state.recovery_state === RecoveryStates.ChallengeSelecting) { + if ( + state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.ChallengeSelecting + ) { return ; } - if (state.recovery_state === RecoveryStates.ChallengeSolving) { + if ( + state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.ChallengeSolving + ) { return ; } - if (state.recovery_state === RecoveryStates.RecoveryFinished) { + if ( + state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.RecoveryFinished + ) { return ; } - if (state.recovery_state === RecoveryStates.ChallengePaying) { + if ( + state.reducer_type === "recovery" && + state.recovery_state === RecoveryStates.ChallengePaying + ) { return ; } 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( {}, + discoverStart: async () => {}, + discoveryState: { + state: "none", + }, currentError: undefined, back: async () => { null;