From c6148ba4f21a6969c51a21728f912a6319a68f71 Mon Sep 17 00:00:00 2001 From: jubnl Date: Sun, 5 Apr 2026 17:14:36 +0200 Subject: [PATCH] fix(mfa): generate SVG QR code Replace the rasterized 180px PNG QR code with a crisp 250px SVG --- client/src/components/Settings/AccountTab.tsx | 6 +++--- server/src/routes/auth.ts | 4 ++-- server/src/services/authService.ts | 2 +- server/tests/integration/auth.test.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/src/components/Settings/AccountTab.tsx b/client/src/components/Settings/AccountTab.tsx index b8a45ff..81bf491 100644 --- a/client/src/components/Settings/AccountTab.tsx +++ b/client/src/components/Settings/AccountTab.tsx @@ -253,8 +253,8 @@ export default function AccountTab(): React.ReactElement { onClick={async () => { setMfaLoading(true) try { - const data = await authApi.mfaSetup() as { qr_data_url: string; secret: string } - setMfaQr(data.qr_data_url) + const data = await authApi.mfaSetup() as { qr_svg: string; secret: string } + setMfaQr(data.qr_svg) setMfaSecret(data.secret) setMfaSetupCode('') } catch (err: unknown) { @@ -274,7 +274,7 @@ export default function AccountTab(): React.ReactElement { {!user?.mfa_enabled && mfaQr && (

{t('settings.mfa.scanQr')}

- +
{mfaSecret} diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index dd977df..f3846ce 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -260,8 +260,8 @@ router.post('/mfa/setup', authenticate, (req: Request, res: Response) => { const result = setupMfa(authReq.user.id, authReq.user.email); if (result.error) return res.status(result.status!).json({ error: result.error }); result.qrPromise! - .then((qr_data_url: string) => { - res.json({ secret: result.secret, otpauth_url: result.otpauth_url, qr_data_url }); + .then((qr_svg: string) => { + res.json({ secret: result.secret, otpauth_url: result.otpauth_url, qr_svg }); }) .catch((err: unknown) => { console.error('[MFA] QR code generation error:', err); diff --git a/server/src/services/authService.ts b/server/src/services/authService.ts index 5cbb236..d1f2571 100644 --- a/server/src/services/authService.ts +++ b/server/src/services/authService.ts @@ -816,7 +816,7 @@ export function setupMfa(userId: number, userEmail: string): { error?: string; s console.error('[MFA] Setup error:', err); return { error: 'MFA setup failed', status: 500 }; } - return { secret, otpauth_url, qrPromise: QRCode.toDataURL(otpauth_url) }; + return { secret, otpauth_url, qrPromise: QRCode.toString(otpauth_url, { type: 'svg', width: 250 }) }; } export function enableMfa(userId: number, code?: string): { error?: string; status?: number; success?: boolean; mfa_enabled?: boolean; backup_codes?: string[] } { diff --git a/server/tests/integration/auth.test.ts b/server/tests/integration/auth.test.ts index de5de94..f935c70 100644 --- a/server/tests/integration/auth.test.ts +++ b/server/tests/integration/auth.test.ts @@ -312,7 +312,7 @@ describe('MFA', () => { expect(res.status).toBe(200); expect(res.body.secret).toBeDefined(); expect(res.body.otpauth_url).toContain('otpauth://'); - expect(res.body.qr_data_url).toMatch(/^data:image/); + expect(res.body.qr_svg).toMatch(/^ {