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
TOTP implementation for 2FA #1031
Conversation
…nts in the future
enabled: true | ||
server_name: '%packagist_host%' | ||
issuer: Packagist | ||
window: 1 |
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.
A new code is generated every 30 seconds. A window
of 1
will allow three codes to be accepted:
- The current code
- The code generated 30 seconds before it
- The next code that will be generated
This allows some flexibility in case the server clock and mobile clock are slightly out-of-sync, or the user needs a few extra seconds to enter in their code.
throw new AccessDeniedException('You cannot change this user\'s two-factor authentication settings'); | ||
} | ||
|
||
if ($backupCode = $this->get('session')->get('backup_code')) { |
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.
I briefly considered using a flash message for this, but I wanted special formatting and therefore opted to just stash the code in normal session storage and manually remove it once we read it.
return $this->backupCode !== null && hash_equals($this->backupCode, $code); | ||
} | ||
|
||
public function invalidateBackupCode(string $code): void |
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.
The interface assumes the user may have multiple codes, but we're only using a single code here.
@@ -0,0 +1,178 @@ | |||
{ |
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.
I had to manually look up each icon that was used to create the custom font, which took a while, so I figured why not commit this so it's easier to make future changes?
src/Packagist/WebBundle/Resources/views/user/enable_two_factor_auth.html.twig
Outdated
Show resolved
Hide resolved
src/Packagist/WebBundle/Resources/views/user/enable_two_factor_auth.html.twig
Outdated
Show resolved
Hide resolved
src/Packagist/WebBundle/Resources/views/user/enable_two_factor_auth.html.twig
Outdated
Show resolved
Hide resolved
Thanks for the review, @stof! I have made the suggested edits. For the sake of tracking them, I have not squashed these into my earlier commits yet but can certainly do so to clean up the history before merging. |
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.
concept & code review ACK - PR looks great, and the two-factor-bundle & otphp deps are tested and actively maintained
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.
Looks good to me
I have added commit 00ca834 which exposes the secret key to the user during setup. This will allow people without QR code scanners to set up 2FA: |
Just a quick flow review based on the screenshots (thanks for that, really helpful), it looks great overall, I'd just think having a proper intermediate page to show the confirmation that it's been enabled and show the backup code there prominently would be good. Having it only in the flash message confirmation can be very easily glanced over IMO. |
Great idea! I'll work on implementing that over the next few days. |
Implements #1031 (comment) Also includes a new checkbox icon used for the confirmation button
Additional testing revealed an issue with Github-based accounts. When such an account was logged in for a while, they eventually lost their 'fully-authenticated' status and would be forced to re-connect with Github, which was an unexpected behavior. Furthermore, even if you went through that process, you wouldn't become fully-authenticated afterwards. Instead of spending time to investigate this behavior, I've decided to revert to the convention used by the 'change password' functionality, which doesn't require this additional permission.
I found time this evening to implement it 😃 When the user is setting up 2FA and they submit the confirmation code from their authenticator, they will be taken to this new intermediate confirmation screen: 2FA is fully enabled at this point (as per the green success message). The green button is simply a link to the main page showing 2FA status, which again confirms that 2FA is enabled: (You don't actually have to click the big green button for 2FA to become enabled) Hopefully this makes it more obvious that the user should save their backup code in a safe place. |
Thanks, looking all good! It's live. |
Implements #1020 using TOTP codes
Overview
A new section has been added to the user area:
Clicking the button link takes you to this new page:
Using a compatible app, the user must scan the QR code and enter in a valid TOTP code to prove they've got it working. Once they submit a valid code 2FA will be active on their account:
The user is also given a single-use backup code - more on that later.
When a user with a 2FA-enabled account logs in, we redirect them to this new page:
They must enter a valid TOTP code from their authenticator in order to continue and be fully logged-in. They can also request that the current browser be remembered for 30 days so they aren't prompted to constantly re-enter TOTP codes from the current computer.
If the user loses access to their authenticator, they can instead use that backup code to gain access. Using this code will cause 2FA to become disabled until they set it up again:
Users can also disable 2FA manually if they wish. From the main 2FA status in their account area, they simply click that giant red button which takes them to a confirmation page:
Clicking the red button will disable 2FA:
Notes
A few notes about this implementation:
mobile
icon to represent 2FA in the menuI'll add some additional notes inline with the code
Happy #Hacktoberfest!