Use local QR rendering for MFA setup (#458)
This commit is contained in:
parent
3a6e03b60c
commit
f6d93323f7
@ -7,6 +7,11 @@ import { getAccountServiceBaseUrl } from '@lib/serviceConfig'
|
||||
const ACCOUNT_SERVICE_URL = getAccountServiceBaseUrl()
|
||||
const ACCOUNT_API_BASE = `${ACCOUNT_SERVICE_URL}/api/auth`
|
||||
|
||||
// This Next.js route proxies MFA provisioning requests to the account service.
|
||||
// The UI calls /api/auth/mfa/setup, which in turn forwards to the Go backend
|
||||
// at /api/auth/mfa/totp/provision, keeping browser credentials opaque to the
|
||||
// external service and letting us manage cookies centrally.
|
||||
|
||||
type SetupPayload = {
|
||||
token?: string
|
||||
issuer?: string
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import Image from 'next/image'
|
||||
import { FormEvent, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import QRCode from 'react-qr-code'
|
||||
import { toDataURL as generateQrCode } from 'qrcode'
|
||||
|
||||
import Card from '../components/Card'
|
||||
import { useLanguage } from '@i18n/LanguageProvider'
|
||||
@ -94,6 +95,7 @@ export default function MfaSetupPanel() {
|
||||
const [issuer, setIssuer] = useState('')
|
||||
const [accountLabel, setAccountLabel] = useState('')
|
||||
const [code, setCode] = useState('')
|
||||
const [qrCodeDataUrl, setQrCodeDataUrl] = useState('')
|
||||
const [isProvisioning, setIsProvisioning] = useState(false)
|
||||
const [isVerifying, setIsVerifying] = useState(false)
|
||||
const [isDisabling, setIsDisabling] = useState(false)
|
||||
@ -166,6 +168,39 @@ export default function MfaSetupPanel() {
|
||||
}
|
||||
}, [setupRequested])
|
||||
|
||||
useEffect(() => {
|
||||
let active = true
|
||||
|
||||
const renderQr = async () => {
|
||||
if (!uri) {
|
||||
setQrCodeDataUrl('')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const dataUrl = await generateQrCode(uri, {
|
||||
errorCorrectionLevel: 'M',
|
||||
margin: 1,
|
||||
scale: 6,
|
||||
})
|
||||
if (active) {
|
||||
setQrCodeDataUrl(dataUrl)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to generate MFA QR code', error)
|
||||
if (active) {
|
||||
setQrCodeDataUrl('')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renderQr()
|
||||
|
||||
return () => {
|
||||
active = false
|
||||
}
|
||||
}, [uri])
|
||||
|
||||
const handleProvision = useCallback(async () => {
|
||||
setIsProvisioning(true)
|
||||
setError(null)
|
||||
@ -515,15 +550,17 @@ export default function MfaSetupPanel() {
|
||||
<h4 className="text-sm font-semibold text-gray-900">{copy.guide.step2Title}</h4>
|
||||
<p className="mt-2 text-sm text-gray-600">{copy.guide.step2Description}</p>
|
||||
<div className="mt-4 flex flex-col gap-6 lg:flex-row lg:items-start">
|
||||
{uri ? (
|
||||
{qrCodeDataUrl ? (
|
||||
<div className="flex justify-center lg:w-60 lg:justify-start">
|
||||
<div className="rounded-xl border border-purple-100 bg-purple-50 p-3">
|
||||
<div className="flex items-center justify-center rounded-lg border border-purple-200 bg-white p-2 shadow-sm">
|
||||
<QRCode
|
||||
value={uri}
|
||||
size={200}
|
||||
<Image
|
||||
src={qrCodeDataUrl}
|
||||
alt={copy.qrLabel}
|
||||
width={176}
|
||||
height={176}
|
||||
className="h-44 w-44"
|
||||
aria-label={copy.qrLabel}
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -23,11 +23,11 @@
|
||||
"pdfjs-dist": "^4.2.67",
|
||||
"prop-types": "^15.8.1",
|
||||
"qr.js": "0.0.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-grid-layout": "^1.4.4",
|
||||
"react-pdf": "^9.1.0",
|
||||
"react-qr-code": "^2.0.18",
|
||||
"react-resizable": "^3.0.4",
|
||||
"swr": "^2.3.0",
|
||||
"zustand": "^4.5.4"
|
||||
|
||||
@ -2025,6 +2025,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"camelcase@npm:^5.0.0":
|
||||
version: 5.3.1
|
||||
resolution: "camelcase@npm:5.3.1"
|
||||
checksum: 10/e6effce26b9404e3c0f301498184f243811c30dfe6d0b9051863bd8e4034d09c8c2923794f280d6827e5aa055f6c434115ff97864a16a963366fb35fd673024b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001702, caniuse-lite@npm:^1.0.30001746":
|
||||
version: 1.0.30001748
|
||||
resolution: "caniuse-lite@npm:1.0.30001748"
|
||||
@ -2117,6 +2124,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cliui@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "cliui@npm:6.0.0"
|
||||
dependencies:
|
||||
string-width: "npm:^4.2.0"
|
||||
strip-ansi: "npm:^6.0.0"
|
||||
wrap-ansi: "npm:^6.2.0"
|
||||
checksum: 10/44afbcc29df0899e87595590792a871cd8c4bc7d6ce92832d9ae268d141a77022adafca1aeaeccff618b62a613b8354e57fe22a275c199ec04baf00d381ef6ab
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cloudnative-homepage@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "cloudnative-homepage@workspace:."
|
||||
@ -2140,11 +2158,11 @@ __metadata:
|
||||
postcss: "npm:^8.4.32"
|
||||
prop-types: "npm:^15.8.1"
|
||||
qr.js: "npm:0.0.0"
|
||||
qrcode: "npm:^1.5.4"
|
||||
react: "npm:18.2.0"
|
||||
react-dom: "npm:18.2.0"
|
||||
react-grid-layout: "npm:^1.4.4"
|
||||
react-pdf: "npm:^9.1.0"
|
||||
react-qr-code: "npm:^2.0.18"
|
||||
react-resizable: "npm:^3.0.4"
|
||||
swr: "npm:^2.3.0"
|
||||
tailwindcss: "npm:^3.4.3"
|
||||
@ -2323,6 +2341,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"decamelize@npm:^1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "decamelize@npm:1.2.0"
|
||||
checksum: 10/ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"decimal.js@npm:^10.4.3":
|
||||
version: 10.6.0
|
||||
resolution: "decimal.js@npm:10.6.0"
|
||||
@ -2445,6 +2470,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dijkstrajs@npm:^1.0.1":
|
||||
version: 1.0.3
|
||||
resolution: "dijkstrajs@npm:1.0.3"
|
||||
checksum: 10/0d8429699a6d5897ed371de494ef3c7072e8052b42abbd978e686a9b8689e70af005fa3e93e93263ee3653673ff5f89c36db830a57ae7c2e088cb9c496307507
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dlv@npm:^1.1.3":
|
||||
version: 1.1.3
|
||||
resolution: "dlv@npm:1.1.3"
|
||||
@ -3325,6 +3357,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"find-up@npm:^4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "find-up@npm:4.1.0"
|
||||
dependencies:
|
||||
locate-path: "npm:^5.0.0"
|
||||
path-exists: "npm:^4.0.0"
|
||||
checksum: 10/4c172680e8f8c1f78839486e14a43ef82e9decd0e74145f40707cc42e7420506d5ec92d9a11c22bd2c48fb0c384ea05dd30e10dd152fefeec6f2f75282a8b844
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"find-up@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "find-up@npm:5.0.0"
|
||||
@ -3469,6 +3511,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-caller-file@npm:^2.0.1":
|
||||
version: 2.0.5
|
||||
resolution: "get-caller-file@npm:2.0.5"
|
||||
checksum: 10/b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "get-func-name@npm:2.0.2"
|
||||
@ -4333,6 +4382,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"locate-path@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "locate-path@npm:5.0.0"
|
||||
dependencies:
|
||||
p-locate: "npm:^4.1.0"
|
||||
checksum: 10/83e51725e67517287d73e1ded92b28602e3ae5580b301fe54bfb76c0c723e3f285b19252e375712316774cf52006cb236aed5704692c32db0d5d089b69696e30
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"locate-path@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "locate-path@npm:6.0.0"
|
||||
@ -4988,6 +5046,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-limit@npm:^2.2.0":
|
||||
version: 2.3.0
|
||||
resolution: "p-limit@npm:2.3.0"
|
||||
dependencies:
|
||||
p-try: "npm:^2.0.0"
|
||||
checksum: 10/84ff17f1a38126c3314e91ecfe56aecbf36430940e2873dadaa773ffe072dc23b7af8e46d4b6485d302a11673fe94c6b67ca2cfbb60c989848b02100d0594ac1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-limit@npm:^3.0.2":
|
||||
version: 3.1.0
|
||||
resolution: "p-limit@npm:3.1.0"
|
||||
@ -5006,6 +5073,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-locate@npm:^4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "p-locate@npm:4.1.0"
|
||||
dependencies:
|
||||
p-limit: "npm:^2.2.0"
|
||||
checksum: 10/513bd14a455f5da4ebfcb819ef706c54adb09097703de6aeaa5d26fe5ea16df92b48d1ac45e01e3944ce1e6aa2a66f7f8894742b8c9d6e276e16cd2049a2b870
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-locate@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "p-locate@npm:5.0.0"
|
||||
@ -5022,6 +5098,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-try@npm:^2.0.0":
|
||||
version: 2.2.0
|
||||
resolution: "p-try@npm:2.2.0"
|
||||
checksum: 10/f8a8e9a7693659383f06aec604ad5ead237c7a261c18048a6e1b5b85a5f8a067e469aa24f5bc009b991ea3b058a87f5065ef4176793a200d4917349881216cae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"package-json-from-dist@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "package-json-from-dist@npm:1.0.1"
|
||||
@ -5193,6 +5276,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pngjs@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "pngjs@npm:5.0.0"
|
||||
checksum: 10/345781644740779752505af2fea3e9043f6c7cc349b18e1fb8842796360d1624791f0c24d33c0f27b05658373f90ffaa177a849e932e5fea1f540cef3975f3c9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"possible-typed-array-names@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "possible-typed-array-names@npm:1.1.0"
|
||||
@ -5409,6 +5499,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"qrcode@npm:^1.5.4":
|
||||
version: 1.5.4
|
||||
resolution: "qrcode@npm:1.5.4"
|
||||
dependencies:
|
||||
dijkstrajs: "npm:^1.0.1"
|
||||
pngjs: "npm:^5.0.0"
|
||||
yargs: "npm:^15.3.1"
|
||||
bin:
|
||||
qrcode: bin/qrcode
|
||||
checksum: 10/9a1b61760e4ea334545a0f54bbc11c537aba0a17cf52cab9fa1b07f8a1337eed0bc6f7fde41b197f2c82c249bc48728983bfaf861bb7ecb29dc597b2ae33c424
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"querystringify@npm:^2.1.1":
|
||||
version: 2.2.0
|
||||
resolution: "querystringify@npm:2.2.0"
|
||||
@ -5523,18 +5626,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-qr-code@npm:^2.0.18":
|
||||
version: 2.0.18
|
||||
resolution: "react-qr-code@npm:2.0.18"
|
||||
dependencies:
|
||||
prop-types: "npm:^15.8.1"
|
||||
qr.js: "npm:0.0.0"
|
||||
peerDependencies:
|
||||
react: "*"
|
||||
checksum: 10/c90164aac6c124d6a7aba2d9426859072d2233bb8d06102d23ef0c1831048e94cebc7608b821ff2b79f3a1cadf454305c727fd3e1d7742fd71f816cdeac04053
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-resizable@npm:^3.0.4, react-resizable@npm:^3.0.5":
|
||||
version: 3.0.5
|
||||
resolution: "react-resizable@npm:3.0.5"
|
||||
@ -5625,6 +5716,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"require-directory@npm:^2.1.1":
|
||||
version: 2.1.1
|
||||
resolution: "require-directory@npm:2.1.1"
|
||||
checksum: 10/a72468e2589270d91f06c7d36ec97a88db53ae5d6fe3787fadc943f0b0276b10347f89b363b2a82285f650bdcc135ad4a257c61bdd4d00d6df1fa24875b0ddaf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"require-main-filename@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "require-main-filename@npm:2.0.0"
|
||||
checksum: 10/8604a570c06a69c9d939275becc33a65676529e1c3e5a9f42d58471674df79357872b96d70bb93a0380a62d60dc9031c98b1a9dad98c946ffdd61b7ac0c8cedd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"requires-port@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "requires-port@npm:1.0.0"
|
||||
@ -5918,6 +6023,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"set-blocking@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "set-blocking@npm:2.0.0"
|
||||
checksum: 10/8980ebf7ae9eb945bb036b6e283c547ee783a1ad557a82babf758a065e2fb6ea337fd82cac30dd565c1e606e423f30024a19fff7afbf4977d784720c4026a8ef
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"set-function-length@npm:^1.2.2":
|
||||
version: 1.2.2
|
||||
resolution: "set-function-length@npm:1.2.2"
|
||||
@ -6133,7 +6245,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0":
|
||||
"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0":
|
||||
version: 4.2.3
|
||||
resolution: "string-width@npm:4.2.3"
|
||||
dependencies:
|
||||
@ -7069,6 +7181,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which-module@npm:^2.0.0":
|
||||
version: 2.0.1
|
||||
resolution: "which-module@npm:2.0.1"
|
||||
checksum: 10/1967b7ce17a2485544a4fdd9063599f0f773959cca24176dbe8f405e55472d748b7c549cd7920ff6abb8f1ab7db0b0f1b36de1a21c57a8ff741f4f1e792c52be
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19":
|
||||
version: 1.1.19
|
||||
resolution: "which-typed-array@npm:1.1.19"
|
||||
@ -7136,6 +7255,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"wrap-ansi@npm:^6.2.0":
|
||||
version: 6.2.0
|
||||
resolution: "wrap-ansi@npm:6.2.0"
|
||||
dependencies:
|
||||
ansi-styles: "npm:^4.0.0"
|
||||
string-width: "npm:^4.1.0"
|
||||
strip-ansi: "npm:^6.0.0"
|
||||
checksum: 10/0d64f2d438e0b555e693b95aee7b2689a12c3be5ac458192a1ce28f542a6e9e59ddfecc37520910c2c88eb1f82a5411260566dba5064e8f9895e76e169e76187
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"wrap-ansi@npm:^8.1.0":
|
||||
version: 8.1.0
|
||||
resolution: "wrap-ansi@npm:8.1.0"
|
||||
@ -7183,6 +7313,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"y18n@npm:^4.0.0":
|
||||
version: 4.0.3
|
||||
resolution: "y18n@npm:4.0.3"
|
||||
checksum: 10/392870b2a100bbc643bc035fe3a89cef5591b719c7bdc8721bcdb3d27ab39fa4870acdca67b0ee096e146d769f311d68eda6b8195a6d970f227795061923013f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yallist@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "yallist@npm:4.0.0"
|
||||
@ -7197,6 +7334,35 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yargs-parser@npm:^18.1.2":
|
||||
version: 18.1.3
|
||||
resolution: "yargs-parser@npm:18.1.3"
|
||||
dependencies:
|
||||
camelcase: "npm:^5.0.0"
|
||||
decamelize: "npm:^1.2.0"
|
||||
checksum: 10/235bcbad5b7ca13e5abc54df61d42f230857c6f83223a38e4ed7b824681875b7f8b6ed52139d88a3ad007050f28dc0324b3c805deac7db22ae3b4815dae0e1bf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yargs@npm:^15.3.1":
|
||||
version: 15.4.1
|
||||
resolution: "yargs@npm:15.4.1"
|
||||
dependencies:
|
||||
cliui: "npm:^6.0.0"
|
||||
decamelize: "npm:^1.2.0"
|
||||
find-up: "npm:^4.1.0"
|
||||
get-caller-file: "npm:^2.0.1"
|
||||
require-directory: "npm:^2.1.1"
|
||||
require-main-filename: "npm:^2.0.0"
|
||||
set-blocking: "npm:^2.0.0"
|
||||
string-width: "npm:^4.2.0"
|
||||
which-module: "npm:^2.0.0"
|
||||
y18n: "npm:^4.0.0"
|
||||
yargs-parser: "npm:^18.1.2"
|
||||
checksum: 10/bbcc82222996c0982905b668644ca363eebe6ffd6a572fbb52f0c0e8146661d8ce5af2a7df546968779bb03d1e4186f3ad3d55dfaadd1c4f0d5187c0e3a5ba16
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yocto-queue@npm:^0.1.0":
|
||||
version: 0.1.0
|
||||
resolution: "yocto-queue@npm:0.1.0"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user