-
Notifications
You must be signed in to change notification settings - Fork 166
Properly countdown time to account unlocking #772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3ebab02
5267d09
1ae4ece
ff76b20
2c43600
619237f
a83a870
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| export default () => { | ||
| window.onbeforeunload = null; | ||
| window.onunload = null; | ||
| window.location.href = '/timeout'; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import msFormatter from './ms-formatter'; | ||
| import autoLogout from './auto-logout'; | ||
|
|
||
| export default (targetSelector, timeLeft = 0, interval = 1000) => { | ||
| const countdownTarget = document.querySelector(targetSelector); | ||
| let remaining = timeLeft; | ||
|
|
||
| if (!countdownTarget) return; | ||
|
|
||
| (function tick() { | ||
| countdownTarget.innerHTML = msFormatter(remaining); | ||
|
|
||
| if (remaining <= 0) { | ||
| autoLogout(); | ||
| return; | ||
| } | ||
|
|
||
| remaining -= interval; | ||
| setTimeout(tick, interval); | ||
| }()); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import autoLogout from './auto-logout'; | ||
| import countdownTimer from './countdown-timer'; | ||
| import msFormatter from './ms-formatter'; | ||
|
|
||
| const LoginGov = window.LoginGov = (window.LoginGov || {}); | ||
|
|
||
| LoginGov.autoLogout = autoLogout; | ||
| LoginGov.countdownTimer = countdownTimer; | ||
| LoginGov.msFormatter = msFormatter; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| function pluralize(word, count) { | ||
| return `${word}${count !== 1 ? 's' : ''}`; | ||
| } | ||
|
|
||
| function formatMinutes(minutes) { | ||
| if (!minutes) return 0; | ||
|
|
||
| return `${minutes} ${pluralize('minute', minutes)}`; | ||
| } | ||
|
|
||
| function formatSeconds(seconds) { | ||
| return `${seconds} ${pluralize('second', seconds)}`; | ||
| } | ||
|
|
||
| export default (milliseconds) => { | ||
| const seconds = milliseconds / 1000; | ||
| const minutes = parseInt(seconds / 60, 10); | ||
| const remainingSeconds = parseInt(seconds % 60, 10); | ||
|
|
||
| const displayMinutes = formatMinutes(minutes); | ||
| const displaySeconds = formatSeconds(remainingSeconds); | ||
|
|
||
| return `${displayMinutes} and ${displaySeconds}`; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import 'app/utils/index'; | ||
| import 'app/pw-toggle'; | ||
| import 'app/form-validation'; | ||
| import 'app/form-field-format'; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <% lockout_time_in_words = decorated_user.lockout_time_remaining_in_words %> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not a huge fan of declaring vars in views like this (because I would expect this to be a helper method and woud look for it by grepping for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used a variable here because the view originally had the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think a helper method is needed, but I do think that using that veryyyy long |
||
| <% title t('titles.account_locked') %> | ||
|
|
||
| <h1 class="h3 my0"> | ||
| <%= t('titles.account_locked') %> | ||
| </h1> | ||
| <p> | ||
| <%= t('devise.two_factor_authentication.max_login_attempts_reached') %> | ||
| </p> | ||
| <p> | ||
| <%= t('devise.two_factor_authentication.please_try_again_html', | ||
| time_remaining: content_tag(:span, lockout_time_in_words, id: 'countdown')) %> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The upside of this (vs having the HTML in the i18n string itself):
Downside:
Because of this downside, I think we've mostly been including the HTML directly in I18n translation. At least that was the case the last time I checked. What do you think?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do agree with the downside, I originally had the html directly in the locale file. However, per @zachmargolis comment above, I do think that it is a little cleaner to have the html live in the view.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we, strictly speaking, need to know that the translation includes HTML?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do not, but if I were buzzing around in the locale files and saw an I am fine with keeping as-is and will try to move in this direction when I am using html-ified translations in the future in this proj.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that part does require one to know that the _html automatically calls
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that is the magic ✨ -- what I meant to convey is that, as someone who does know this magic, if I saw a translation without HTML but with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ahhh I see! The ✨ was not known to me. 😢 |
||
| </p> | ||
|
|
||
| <%= nonced_javascript_tag do %> | ||
| var test = <%= decorated_user.lockout_time_remaining %> * 1000; | ||
| window.LoginGov.countdownTimer('#countdown', test); | ||
| <% end %> | ||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change means that a user who signs in with email and password and sits on the 2FA delivery page will never auto time out. The consequence is that if I leave my computer and phone unattended, someone can sign in to my account without needing to know my email and password. That's probably an edge case, but still something to think about and decide whether or not this is what we want.
The solution to keeping the user on the account locked screen is to reverse the order of these 2 lines: https://github.com/18F/identity-idp/blob/master/app/controllers/concerns/two_factor_authenticatable.rb#L16-L18
The reason is that application.html.slim wasn't called after signing the user out, and therefore the JS was still active. By reversing the order, we force a new call to the layout template, which then sees that a current user does not exist, and therefore does not trigger the timeout JS.
I will submit a PR shortly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disregard my first comment about not timing out. I forgot that Devise will time you out regardless of any JS we have. The JS is just for the modal to appear, but I think we do want it to appear during 2FA, right?