Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
38fd3cf
edit and add content to new password page
jmdembe Mar 28, 2023
3e0f49a
lint yml files
jmdembe Mar 28, 2023
b888fea
replace current state text with new copy
jmdembe Mar 28, 2023
b716b5d
change event listener to focusout
jmdembe Mar 28, 2023
4ad5cf6
fix failing lint tests
jmdembe Mar 28, 2023
5732d63
analyze password on keydown and touchout
jmdembe Mar 29, 2023
e7b2629
fix for proper syntax
jmdembe Mar 29, 2023
0674d91
remove focusout and touchout events
jmdembe Mar 31, 2023
1154cbc
Fix test
jmdembe Apr 3, 2023
ab7b10f
add once option to event listener
jmdembe Apr 3, 2023
3051105
trying something: what if there is another class on page load?
jmdembe Apr 3, 2023
810f0a5
Empty commit
jmdembe Apr 3, 2023
ac8e5cd
roll back test class
jmdembe Apr 3, 2023
bbe7f03
change how password strength bar appears
jmdembe Apr 4, 2023
5017ed9
add test for modified pw strenth bar
jmdembe Apr 4, 2023
dacd3d6
update user spec password
jmdembe Apr 4, 2023
a6b41bd
Update spec/features/users/user_profile_spec.rb
jmdembe Apr 4, 2023
a30fbfb
Update spec/features/visitors/set_password_spec.rb
jmdembe Apr 4, 2023
c84f8df
Update spec/features/visitors/set_password_spec.rb
jmdembe Apr 4, 2023
afe3f7a
fix lint error
jmdembe Apr 4, 2023
8695f9b
bold "12 characters"
jmdembe Apr 5, 2023
f971cd2
WIP: Figure out insertion of character count
jmdembe Apr 5, 2023
de38308
add password length data
jmdembe Apr 5, 2023
1a27234
pass minimum pw length to js
jmdembe Apr 5, 2023
f9e8d31
fix test for character limit test
jmdembe Apr 5, 2023
7df63da
lint fixes
jmdembe Apr 5, 2023
b0527b5
edit assignment, change class from so->average
jmdembe Apr 6, 2023
aaf32d6
iterate on password strength
jmdembe Apr 6, 2023
8cfdfee
turn string value into a number
jmdembe Apr 6, 2023
edab00a
add code review comments; WIP: first draft of js test
jmdembe Apr 7, 2023
629fec9
give test a hard coded password to calculate from
jmdembe Apr 10, 2023
60f1d40
remove score check, use zxcvbn instead of manually created result
jmdembe Apr 10, 2023
31fe18d
cleanup
jmdembe Apr 10, 2023
3aff79d
update copy for password length
jmdembe Apr 11, 2023
b30b1ca
update tests (round 1)
jmdembe Apr 11, 2023
d77bdb8
Edit test round 2
jmdembe Apr 11, 2023
aa6d486
improve readability
jmdembe Apr 11, 2023
3e3f60e
change one password hint for consistency
jmdembe Apr 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/assets/stylesheets/components/_password.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// password strength module

$weak: #e80e0e;
$so-so: #ffac00;
$average: #ffac00;
$good: #9ac056;
$great: #00b200;

Expand All @@ -20,9 +20,9 @@ $great: #00b200;
}
}

.pw-so-so {
.pw-average {
.pw-bar:nth-child(-n + 2) {
background-color: $so-so;
background-color: $average;
}
}

Expand Down
26 changes: 14 additions & 12 deletions app/javascript/packs/pw-strength.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { t } from '@18f/identity-i18n';
const scale = {
0: ['pw-very-weak', t('instructions.password.strength.i')],
1: ['pw-weak', t('instructions.password.strength.ii')],
2: ['pw-so-so', t('instructions.password.strength.iii')],
2: ['pw-average', t('instructions.password.strength.iii')],
3: ['pw-good', t('instructions.password.strength.iv')],
4: ['pw-great', t('instructions.password.strength.v')],
};
Expand All @@ -26,13 +26,21 @@ function getStrength(z) {
return z && z.password.length ? scale[z.score] : fallback;
}

export function getFeedback(z) {
if (!z || !z.password || z.score > 2) {
export function getFeedback(z, { minimumLength }) {
if (!z || !z.password) {
return ' ';
}

const { warning, suggestions } = z.feedback;

if (!warning && !suggestions.length) {
if (z.password.length < minimumLength) {
return t('errors.attributes.password.too_short.other', { count: minimumLength });
}

return '&nbsp;';
}

function lookup(str) {
// i18n-tasks-use t('zxcvbn.feedback.a_word_by_itself_is_easy_to_guess')
// i18n-tasks-use t('zxcvbn.feedback.add_another_word_or_two_uncommon_words_are_better')
Expand Down Expand Up @@ -66,9 +74,6 @@ export function getFeedback(z) {
return t(`zxcvbn.feedback.${snakeCase(str)}`);
}

if (!warning && !suggestions.length) {
return '&nbsp;';
}
if (warning) {
return lookup(warning);
}
Expand Down Expand Up @@ -96,11 +101,7 @@ function analyzePw() {
const pwFeedback = document.getElementById('pw-strength-feedback');
const forbiddenPasswordsElement = document.querySelector('[data-forbidden]');
const forbiddenPasswords = getForbiddenPasswords(forbiddenPasswordsElement);

// the pw strength module is hidden by default ("display-none" CSS class)
// (so that javascript disabled browsers won't see it)
// thus, first step is unhiding it
pwCntnr.className = '';
const minPasswordLength = +pwCntnr.getAttribute('data-pw-min-length');

function updatePasswordFeedback(cls, strength, feedback) {
pwCntnr.className = cls;
Expand All @@ -118,8 +119,9 @@ function analyzePw() {

function checkPasswordStrength(password) {
const z = zxcvbn(password, forbiddenPasswords);

const [cls, strength] = getStrength(z);
const feedback = getFeedback(z);
const feedback = getFeedback(z, { minimumLength: minPasswordLength });

validatePasswordField(z.score);
updatePasswordFeedback(cls, strength, feedback);
Expand Down
2 changes: 1 addition & 1 deletion app/views/devise/shared/_password_strength.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div id='pw-strength-cntnr' class='display-none' aria-live='polite' aria-atomic='true'>
<div id='pw-strength-cntnr' class='display-none' aria-live='polite' aria-atomic='true' data-pw-min-length='<%= Devise.password_length.min %>'>
<div class="margin-bottom-4">
<div class='clearfix margin-x-neg-05 padding-top-05'>
<div class='pw-bar'></div>
Expand Down
2 changes: 1 addition & 1 deletion app/views/sign_up/passwords/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<%= render PageHeadingComponent.new.with_content(t('forms.confirmation.show_hdr')) %>

<p class="margin-top-2 margin-bottom-0" id="password-description">
<%= t('instructions.password.info.lead', min_length: Devise.password_length.first) %>
<%= t('instructions.password.info.lead_html', min_length: Devise.password_length.first) %>
</p>
<%= simple_form_for(
@password_form,
Expand Down
2 changes: 1 addition & 1 deletion app/views/users/passwords/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<%= render PageHeadingComponent.new.with_content(t('headings.edit_info.password')) %>

<p id="password-description">
<%= t('instructions.password.info.lead', min_length: Devise.password_length.first) %>
<%= t('instructions.password.info.lead_html', min_length: Devise.password_length.first) %>
</p>

<%= simple_form_for(
Expand Down
4 changes: 2 additions & 2 deletions config/locales/errors/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ en:
attributes:
password:
too_short:
one: This password is too short (minimum is 1 character)
other: This password is too short (minimum is %{count} characters)
one: Password must be at least one character long
other: Password must be at least %{count} characters long
capture_doc:
invalid_link: This link is expired or not valid. Please request another link to
verify your identity on a mobile phone.
Expand Down
4 changes: 2 additions & 2 deletions config/locales/errors/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ es:
attributes:
password:
too_short:
one: Esta contraseña es demasiado corta (debe tener 1 carácter como mínimo)
other: Esta contraseña es demasiado corta (%{count} caracteres como mínimo)
one: La contraseña debe tener al menos un carácter
other: La contraseña debe tener al menos %{count} caracteres de longitud.
capture_doc:
invalid_link: Este enlace ha caducado o no es válido. Solicite otro enlace para
verificar su identidad en un teléfono móvil.
Expand Down
4 changes: 2 additions & 2 deletions config/locales/errors/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ fr:
attributes:
password:
too_short:
one: Ce mot de passe est trop court (le minimum est de 1 caractère)
other: Ce mot de passe est trop court (le minimum est de %{count} caractères)
one: Le mot de passe doit comporter au moins un caractère
other: Le mot de passe doit comporter au moins %{count} caractères
capture_doc:
invalid_link: Ce lien a expiré ou n’est pas valide. Veuillez demander un autre
lien pour vérifier votre identité sur un téléphone mobile.
Expand Down
14 changes: 7 additions & 7 deletions config/locales/instructions/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,23 @@ en:
wrong_number: Entered the wrong phone number?
password:
forgot: Don’t know your password? Reset it after confirming your email address.
help_text: The longer and more unusual the password, the harder it is to guess.
So avoid using common phrases. Also avoid repeating passwords from other
online accounts such as banks, email and social media.
help_text: Avoid reusing passwords from your other accounts, such as your banks,
email and social media. Don’t include words from your email address.
help_text_header: Password safety tips
info:
lead: It must be at least %{min_length} characters long and not be a commonly
used password. That’s it!
lead_html: Your password must be <strong> %{min_length} characters </strong> or
longer. Don’t use common phrases or repeated characters, like abc or
111.
password_key: You need your 16-character personal key to reset your password if
you verified your identity with this account. If you don’t have it, you
can still reset your password and then reverify your identity.
strength:
i: Very weak
ii: Weak
iii: So-so
iii: Average
intro: 'Password strength: '
iv: Good
v: Great!
v: Great
sp_handoff_bounced: Your sign in was successful, but %{sp_name} sent you back to
%{app_name}. Please contact %{sp_link} for help.
sp_handoff_bounced_with_no_sp: your service provider
16 changes: 8 additions & 8 deletions config/locales/instructions/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,25 +91,25 @@ es:
wrong_number: '¿Ingresó el número de teléfono equivocado?'
password:
forgot: '¿No sabe su contraseña? Restablézcala después de confirmar su email.'
help_text: Cuanto más larga y más inusual sea la contraseña, más difícil es de
adivinarla. Así que evite usar frases comunes. También evite repetir
contraseñas de otras cuentas en línea, por ejemplo de sus bancos, email
y medios sociales.
help_text: Evite reutilizar las contraseñas de sus otras cuentas, como las
bancarias, las de correo electrónico y las de redes sociales. No incluya
palabras de su dirección de correo electrónico.
help_text_header: Consejos de seguridad de contraseña
info:
lead: Su contraseña debe tener al menos %{min_length} caracteres y no ser una
contraseña común. ¡Eso es todo!
lead_html: Su contraseña deberá tener <strong>%{min_length} caracteres</strong>
o más. No use expresiones comunes ni caracteres repetidos, como “abc”
o “111″.
password_key: Si verificó su identidad con esta cuenta, necesita su clave
personal de 16 caracteres para restablecer su contraseña. Si no cuenta
con ella, de todos modos puede restablecer su contraseña y luego volver
a verificar su identidad.
strength:
i: Muy débil
ii: Débil
iii: Más o menos
iii: Promedio
intro: 'Seguridad de la contraseña:'
iv: Buena
v: '¡Muy buena!'
v: 'Muy buena'
sp_handoff_bounced: Su inicio de sesión fue exitoso, pero %{sp_name} lo envió de
regreso a %{app_name}. Póngase en contacto con %{sp_link} para obtener
ayuda.
Expand Down
17 changes: 9 additions & 8 deletions config/locales/instructions/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,25 +100,26 @@ fr:
password:
forgot: Vous ne connaissez pas votre mot de passe? Réinitialisez-le après avoir
confirmé votre adresse courriel.
help_text: Plus long et inhabituel est le mot de passe, plus difficile il sera à
trouver. Évitez donc d’utiliser des phrases communes. Évitez aussi de
répéter des mots de passe d’autres comptes en ligne comme les comptes
bancaires, les comptes courriel et les comptes de médias sociaux.
help_text: Évitez de réutiliser les mots de passe de vos autres comptes, tels
que ceux de vos banques, de vos comptes de courriel et de vos réseaux
sociaux. N’incluez pas les mots de votre adresse de courriel. bancaires,
les comptes courriel et les comptes de médias sociaux.
help_text_header: Conseils sur la sécurité du mot de passe
info:
lead: Il doit avoir une longueur minimale de %{min_length} caractères et ne pas
être un mot de passe couramment utilisé. C’est tout!
lead_html: Votre mot de passe doit comporter <strong>%{min_length}
caractères</strong> ou plus. N’utilisez pas de phrases courantes ou de
caractères répétés, comme « abc » ou « 111 ».
password_key: Vous avez besoin de votre clé personnelle de 16 caractères pour
réinitialiser votre mot de passe si vous avez vérifié votre identité
avec ce compte. Si vous ne l’avez pas, vous pouvez toujours
réinitialiser votre mot de passe et ensuite revérifier votre identité.
strength:
i: Très faible
ii: Faible
iii: Correct
iii: Moyen
intro: 'Force du mot de passe : '
iv: Bonne
v: Excellente!
v: Excellente
sp_handoff_bounced: Votre connexion a réussi, mais %{sp_name} vous a renvoyé à
%{app_name}. Veuillez contacter %{sp_link} pour obtenir de l’aide.
sp_handoff_bounced_with_no_sp: votre fournisseur de service
10 changes: 8 additions & 2 deletions spec/controllers/event_disavowal_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,13 @@
'Event disavowal password reset',
build_analytics_hash(
success: false,
errors: { password: ['This password is too short (minimum is 12 characters)'] },
errors: {
password: [
t(
'errors.attributes.password.too_short.other', count: Devise.password_length.first
),
],
},
),
)

Expand All @@ -107,7 +113,7 @@
'Event disavowal password reset',
build_analytics_hash(
success: false,
errors: { password: ['This password is too short (minimum is 12 characters)'] },
errors: { password: ['Password must be at least 12 characters long'] },
),
)

Expand Down
2 changes: 1 addition & 1 deletion spec/controllers/sign_up/passwords_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
success: false,
errors: {
password:
["This password is too short (minimum is #{Devise.password_length.first} characters)"],
["Password must be at least #{Devise.password_length.first} characters long"],
},
error_details: password_short_error,
user_id: user.uuid,
Expand Down
2 changes: 1 addition & 1 deletion spec/controllers/users/reset_passwords_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

describe Users::ResetPasswordsController, devise: true do
let(:password_error_message) do
"This password is too short (minimum is #{Devise.password_length.first} characters)"
t('errors.attributes.password.too_short.other', count: Devise.password_length.first)
end
let(:success_properties) { { success: true, failure_reason: nil } }
let(:token_expired_error) { 'token_expired' }
Expand Down
4 changes: 3 additions & 1 deletion spec/features/event_disavowal_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@
fill_in 'New password', with: 'invalid'
click_button t('forms.passwords.edit.buttons.submit')

expect(page).to have_content('is too short (minimum is 12 characters)')
expect(page).to have_content t(
'errors.attributes.password.too_short.other', count: Devise.password_length.first
)

fill_in 'New password', with: 'NewVal!dPassw0rd'
click_button t('forms.passwords.edit.buttons.submit')
Expand Down
6 changes: 3 additions & 3 deletions spec/features/users/user_profile_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@
click_link 'Edit', href: manage_password_path
end

expect(page).to_not have_css('#pw-strength-cntnr.display-none')
expect(page).to have_content '...'
expect(page).not_to have_content(t('instructions.password.strength.intro'))

fill_in t('forms.passwords.edit.labels.password'), with: 'this is a great sentence'
expect(page).to have_content 'Great'
expect(page).to have_content(t('instructions.password.strength.intro'))
expect(page).to have_content t('instructions.password.strength.v')

check t('components.password_toggle.toggle_label')

Expand Down
12 changes: 9 additions & 3 deletions spec/features/visitors/password_recovery_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,9 @@
click_button t('forms.passwords.edit.buttons.submit')

expect(page).
to have_content "is too short (minimum is #{Devise.password_length.first} characters)"
to have_content t(
'errors.attributes.password.too_short.other', count: Devise.password_length.first
)
end

it "does not update the user's password when password is invalid" do
Expand All @@ -226,12 +228,16 @@
fill_in 'New password', with: '1234'
click_button t('forms.passwords.edit.buttons.submit')

expect(page).to have_content 'is too short'
expect(page).to have_content t(
'errors.attributes.password.too_short.other', count: Devise.password_length.first
)

fill_in 'New password', with: '5678'
click_button t('forms.passwords.edit.buttons.submit')

expect(page).to have_content 'is too short'
expect(page).to have_content t(
'errors.attributes.password.too_short.other', count: Devise.password_length.first
)
end
end
end
Expand Down
10 changes: 7 additions & 3 deletions spec/features/visitors/set_password_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,20 @@
end

it 'updates strength feedback as password changes' do
expect(page).to have_content '...'
expect(page).not_to have_content(t('instructions.password.strength.intro'))

fill_in t('forms.password'), with: 'password'
expect(page).to have_content 'Very weak'
expect(page).to have_content(t('instructions.password.strength.intro'))
expect(page).to have_content t('instructions.password.strength.i')

fill_in t('forms.password'), with: '123456789'
expect(page).to have_content t('zxcvbn.feedback.this_is_a_top_10_common_password')

fill_in t('forms.password'), with: 'this is a great sentence'
expect(page).to have_content 'Great!'
expect(page).to have_content t('instructions.password.strength.v')

fill_in t('forms.password'), with: ':b/}6tT#,'
expect(page).to have_content t('errors.attributes.password.too_short.other', count: 12)
end
end

Expand Down
7 changes: 5 additions & 2 deletions spec/forms/password_form_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@
form = PasswordForm.new(user)
password = 'invalid'
errors = {
password:
["This password is too short (minimum is #{Devise.password_length.first} characters)"],
password: [
t(
'errors.attributes.password.too_short.other', count: Devise.password_length.first
),
],
}
extra = {
user_id: '123',
Expand Down
Loading