Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the TOTP setup page to render a QR code from the server without relying on an external QR-image service, and updates various npm dependencies/lockfile entries.
Changes:
- Generate a QR code SVG data URI server-side (via the new
qrdependency) and pass it to the TOTP setup view. - Update the Pug template to use the provided
qrImageinstead of building an external QR service URL. - Bump multiple dependencies and regenerate
package-lock.json.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
controllers/user.js |
Generates an SVG QR code (data URI) for the TOTP setup page and passes it to the view. |
views/account/totp-setup.pug |
Switches QR rendering from an external URL to qrImage provided by the controller. |
package.json |
Adds qr dependency and bumps multiple dependencies (including Mocha to next). |
package-lock.json |
Lockfile updated to reflect dependency changes/additions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Ensure the SVG has an explicit rendered size (was rendering very large). | ||
| const size = 200; | ||
| const svgWithSize = svg.replace('<svg ', `<svg width="${size}" height="${size}" `); | ||
| const qrImage = `data:image/svg+xml;utf8,${encodeURIComponent(svgWithSize)}`; |
There was a problem hiding this comment.
data:image/svg+xml;utf8, uses a non-standard parameter (utf8). For better browser compatibility and standards compliance, prefer data:image/svg+xml;charset=utf-8, (or base64-encode the SVG) when constructing the data URI.
| const qrImage = `data:image/svg+xml;utf8,${encodeURIComponent(svgWithSize)}`; | |
| const qrImage = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgWithSize)}`; |
| const svg = encodeQR(totp.toString(), 'svg'); | ||
| // Ensure the SVG has an explicit rendered size (was rendering very large). | ||
| const size = 200; | ||
| const svgWithSize = svg.replace('<svg ', `<svg width="${size}" height="${size}" `); |
There was a problem hiding this comment.
The SVG sizing is applied via a plain string replacement on '<svg ', which is brittle (it won’t match <svg> with no space, newline after <svg, or other variations, and may duplicate width/height if they already exist). Consider using a more robust approach (e.g., a regex that handles <svg plus optional whitespace/attrs, or QR library options for setting size) so the output is consistently sized.
| const svgWithSize = svg.replace('<svg ', `<svg width="${size}" height="${size}" `); | |
| const sizeAttr = `width="${size}" height="${size}"`; | |
| let svgWithSize = svg; | |
| const svgTagMatch = svg.match(/<svg\b([^>]*)>/i); | |
| if (svgTagMatch) { | |
| const attrs = svgTagMatch[1] || ''; | |
| const hasWidth = /\bwidth\s*=\s*["'][^"']*["']/i.test(attrs); | |
| const hasHeight = /\bheight\s*=\s*["'][^"']*["']/i.test(attrs); | |
| if (!hasWidth && !hasHeight) { | |
| const newSvgTag = `<svg${attrs} ${sizeAttr}>`; | |
| svgWithSize = svg.replace(/<svg\b[^>]*>/i, newSvgTag); | |
| } | |
| } |
| const qrModule = await import('qr'); | ||
| const encodeQR = qrModule.default || qrModule; |
There was a problem hiding this comment.
await import('qr') is executed inside the request handler. Even though Node caches modules, doing this per request adds async overhead and can surface runtime import failures only when this route is hit. Prefer importing once at module scope (or memoizing the import) so failures happen at startup and the handler stays synchronous aside from DB work.
| // Generate QR image (SVG) data URI using the `qr` package | ||
| const qrModule = await import('qr'); | ||
| const encodeQR = qrModule.default || qrModule; | ||
| const svg = encodeQR(totp.toString(), 'svg'); | ||
| // Ensure the SVG has an explicit rendered size (was rendering very large). | ||
| const size = 200; | ||
| const svgWithSize = svg.replace('<svg ', `<svg width="${size}" height="${size}" `); | ||
| const qrImage = `data:image/svg+xml;utf8,${encodeURIComponent(svgWithSize)}`; | ||
| res.render('account/totp-setup', { | ||
| title: 'Setup Authenticator', | ||
| qrCode: totp.toString(), | ||
| qrImage, |
There was a problem hiding this comment.
This adds a new QR generation code path for the TOTP setup page, but there doesn’t appear to be test coverage asserting the page renders a QR image (e.g., an authenticated request expecting qrImage/data:image/svg+xml in the response). Adding an integration test for the authenticated /account/2fa/totp/setup route would help catch runtime issues with the qr dependency and SVG/data-URI generation.
Checklist
--no-verifywas not used for the commit(s)npm run lintpassed locally without any errorsnpm testpassed locally without any errorsnpm run test:e2e:replaypassed locally without any errorsnpm run test:e2e:custom -- --project=chromium-nokey-livepassed locally without any errorsDescription
Screenshots of UI changes (browser) and logs/test results (console, terminal, shell, cmd)