FEATURE: use raster image and autofill in 2FA input (#15429)

- switches to a raster image QR code so it can be long-pressed (or right
clicked) and added to iCloud keychain
- adds `autocomplete="one-time-code"` to the 2FA input for better
discoverability
This commit is contained in:
Penar Musaraj 2022-01-03 23:31:46 -05:00 committed by GitHub
parent ed83d7573e
commit be599513e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 12 additions and 23 deletions

View File

@ -4,7 +4,9 @@
maxlength=maxlength maxlength=maxlength
class="second-factor-token-input" class="second-factor-token-input"
id=inputId id=inputId
autocorrect="off"
autocapitalize="off" autocapitalize="off"
autocomplete="one-time-code"
autocorrect="off"
autofocus="autofocus" autofocus="autofocus"
placeholder=placeholder}} placeholder=placeholder
}}

View File

@ -16,12 +16,9 @@
<div class="control-group"> <div class="control-group">
<div class="controls"> <div class="controls">
<div class="qr-code-container">
<div class="qr-code"> <div class="qr-code">
{{html-safe secondFactorImage}} <img src={{html-safe secondFactorImage}}>
</div> </div>
</div>
<p> <p>
{{#if showSecondFactorKey}} {{#if showSecondFactorKey}}
{{secondFactorKey}} {{secondFactorKey}}

View File

@ -28,7 +28,7 @@ function preferencesPretender(server, helper) {
server.post("/u/create_second_factor_totp.json", () => { server.post("/u/create_second_factor_totp.json", () => {
return helper.response({ return helper.response({
key: "rcyryaqage3jexfj", key: "rcyryaqage3jexfj",
qr: '<div id="test-qr">qr-code</div>', qr: "data:image/png;base64,notarealimage",
}); });
}); });
@ -207,7 +207,7 @@ acceptance("User Preferences", function (needs) {
assert.notOk(exists("#password"), "it hides the password input"); assert.notOk(exists("#password"), "it hides the password input");
await click(".new-totp"); await click(".new-totp");
assert.ok(exists("#test-qr"), "shows qr code"); assert.ok(exists(".qr-code img"), "shows qr code image");
await click(".add-totp"); await click(".add-totp");

View File

@ -747,14 +747,6 @@
} }
} }
.qr-code-container {
display: flex;
.qr-code {
padding: 5px 5px 0 5px;
background-color: white;
}
}
.second-factor { .second-factor {
&.instructions { &.instructions {
color: var(--primary-medium); color: var(--primary-medium);

View File

@ -1412,16 +1412,14 @@ class UsersController < ApplicationController
require 'rotp' if !defined? ROTP require 'rotp' if !defined? ROTP
totp_data = ROTP::Base32.random totp_data = ROTP::Base32.random
secure_session["staged-totp-#{current_user.id}"] = totp_data secure_session["staged-totp-#{current_user.id}"] = totp_data
qrcode_svg = RQRCode::QRCode.new(current_user.totp_provisioning_uri(totp_data)).as_svg( qrcode_png = RQRCode::QRCode.new(current_user.totp_provisioning_uri(totp_data)).as_png(
offset: 0, border_modules: 1,
color: '000', size: 240
shape_rendering: 'crispEdges',
module_size: 4
) )
render json: success_json.merge( render json: success_json.merge(
key: totp_data.scan(/.{4}/).join(" "), key: totp_data.scan(/.{4}/).join(" "),
qr: qrcode_svg qr: qrcode_png.to_data_url
) )
end end