From 4f30506dcacc587586381b1a8fa20c5442784e41 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 23 Jun 2023 10:36:24 -0300 Subject: show qr to import TOTP into other app --- .../paths/instance/templates/create/CreatePage.tsx | 117 ++++++++++++-------- .../paths/instance/templates/update/UpdatePage.tsx | 119 +++++++++++++-------- 2 files changed, 143 insertions(+), 93 deletions(-) (limited to 'packages/merchant-backoffice-ui/src') diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx index 137d50b3e..4dde202c4 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx @@ -24,7 +24,7 @@ import { MerchantTemplateContractDetails, } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { h, VNode } from "preact"; +import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { AsyncButton } from "../../../../components/exception/AsyncButton.js"; import { @@ -44,6 +44,8 @@ import { randomBase32Key, } from "../../../../utils/crypto.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; +import { QR } from "../../../../components/exception/QR.js"; +import { useInstanceContext } from "../../../../context/instance.js"; type Entity = MerchantBackend.Template.TemplateAddDetails; @@ -58,6 +60,8 @@ const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"]; export function CreatePage({ onCreate, onBack }: Props): VNode { const { i18n } = useTranslationContext(); const backend = useBackendContext(); + const { id: instanceId } = useInstanceContext(); + const issuer = new URL(backend.url).hostname; const [showKey, setShowKey] = useState(false); const [state, setState] = useState>({ @@ -120,6 +124,8 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { return onCreate(state as any); }; + const qrText = `otpauth://totp/${instanceId}/${state.template_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${state.pos_key}`; + return (
@@ -175,54 +181,73 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { fromStr={(v) => Number(v)} /> {state.pos_algorithm && state.pos_algorithm > 0 ? ( - - name="pos_key" - label={i18n.str`Point-of-sale key`} - inputType={showKey ? "text" : "password"} - help="Be sure to be very hard to guess or use the random generator" - tooltip={i18n.str`Useful to validate the purchase`} - fromStr={(v) => v.toUpperCase()} - addonAfter={ - - {showKey ? ( - - ) : ( - - )} - - } - side={ - - - - - } - /> + + } + side={ + + + + + } + /> + {showKey && ( + + +
+ {qrText} +
+
+ )} + ) : undefined} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx index 20e40cf9b..30e5502bb 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx @@ -24,7 +24,7 @@ import { MerchantTemplateContractDetails, } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { h, VNode } from "preact"; +import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { AsyncButton } from "../../../../components/exception/AsyncButton.js"; import { @@ -44,6 +44,8 @@ import { randomBase32Key, } from "../../../../utils/crypto.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; +import { QR } from "../../../../components/exception/QR.js"; +import { useInstanceContext } from "../../../../context/instance.js"; type Entity = MerchantBackend.Template.TemplatePatchDetails & WithId; @@ -59,6 +61,8 @@ const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"]; export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { const { i18n } = useTranslationContext(); const backend = useBackendContext(); + const { id: instanceId } = useInstanceContext(); + const issuer = new URL(backend.url).hostname; const [showKey, setShowKey] = useState(false); const [state, setState] = useState>(template); @@ -113,6 +117,8 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { return onUpdate(state as any); }; + const qrText = `otpauth://totp/${instanceId}/${state.id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${state.pos_key}`; + return (
@@ -185,55 +191,74 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { fromStr={(v) => Number(v)} /> {state.pos_algorithm && state.pos_algorithm > 0 ? ( - - name="pos_key" - label={i18n.str`Point-of-sale key`} - inputType={showKey ? "text" : "password"} - help="Be sure to be very hard to guess or use the random generator" - expand - tooltip={i18n.str`Useful to validate the purchase`} - fromStr={(v) => v.toUpperCase()} - addonAfter={ - - {showKey ? ( - - ) : ( - - )} - - } - side={ - - - - - } - /> + + } + side={ + + + + + } + /> + {showKey && ( + + +
+ {qrText} +
+
+ )} + ) : undefined} -- cgit v1.2.3