Closed xoniq closed 4 years ago
Depending on what authentication provider you're using (not sure if it's GoogleAuthenticator or TOTP), use either the scheb_two_factor.security.google_authenticator
or scheb_two_factor.security.totp_authenticator
service and call the checkCode()
method to validate a 2fa code. The both have an identical method:
checkCode(TwoFactorInterface $user, string $code): bool
The $user
should must have the requirements for 2fa set-up, but it doesn't need to be persisted yet.
Thank you sir! I managed to make it work using your example. The back-end part is done.
I now struggle with the front-end. I prepared everything, it does hook into /2fa after logging in, I created a custom twig template, but kept form and fields intact. But when I submit, I get this error:
Unable to find the controller for path "/2fa_check". The route is wrongly configured.
Now, I can add /2fa_check as a custom route, but then I have no idea how to handle it (I could do the same as my initial question, but then I need to somehow tell the system its authorized), but I assume it should handle it by your bundle by default, but it doesn't seem to do it.
This is my routing:
2fa_login:
path: /2fa
defaults:
_controller: "scheb_two_factor.form_controller:form"
2fa_login_check:
path: /2fa_check
That's exactly the same route configuration that I'm using in my test application, so nothing wrong with that.
https://github.com/scheb/two-factor-app/blob/master/config/routes/scheb_two_factor.yaml
Are your 2fa routes within the firewall's pattern
?
This is in my firewall.yml for the admin area which allows 2FA:
admin_secured_area:
pattern: ^/(admin/|admin$)
anonymous: ~
provider: fos_userbundle
form_login:
provider: fos_userbundle
use_referer: true
login_path: /admin/login
check_path: /admin/login_check
default_target_path: /admin
logout:
path: /admin/logout
target: /admin
switch_user: true
context: primary_auth
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 week in seconds
path: /
domain: ~
two_factor:
csrf_token_generator: security.csrf.token_manager
auth_form_path: 2fa_login
check_path: 2fa_login_check
And for the access_control
section:
access_control:
- { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
I just checked, and it's the same as your security.yml (those parts)
There is no /2fa_check
part in access_control
(it isn't in your example).
Additional info; this is Symfony 3.4
Your 2fa paths /2fa_check
and /2fa
do not match the firewall's pattern: pattern: ^/(admin/|admin$)
. Changing their path to /admin/2fa_check
and /admin/2fa
should fix it.
Thanks! That was the problem. I kept it at /2fa at first to have a route for both backend and front-end, but then I have to define both i guess.
Now only I have to find out why the error message is shown in french (it uses the french translation file instead of dutch).
Thanks! That was the problem. I kept it at /2fa at first to have a route for both backend and front-end, but then I have to define both i guess.
Yes, you have to. These routes work similar to the login/logout routes, which also need to be defined per firewall.
Can't help you on the translation issue, doesn't seem to be a bundle-related issue to me.
No, I will check it out by myself. Thanks for your help, and this great bundle!
Hi xoniq, I just came across this question as I am trying to do a similar thing by verifying the OTP before persisting the secret to the database. I am however having some difficulties with this as when I do $user->getGoogleAuthenticatorSecret() it comes up with nothing since I haven't yet persisted it to the database. Is it even possible to access this property before persisting it?
Also, thanks scheb for this bundle!
Set the secret value on the user entity, but don't persist the user entity until you got the code confirmed ;)
Hi scheb,
Thanks for the reply. I did that initially ($user->setGoogleAuthenticatorSecret) but the confirmation was being handled by another request and the result of getGoogleAuthenticationSecret was always blank, unless I persisted the value first. I solved it for now by creating a second field in the user entity and marking it as enabled/disabled and updating the isGoogleAuthenticatorEnabled method to take this into account. It all works fine but I can't help feeling I'm missing something obvious.
I should maybe mention this is all being handled by AJAX requests to my controller, one to get the QRCode and set the secret to the user, and the second to verify the OTP.
Ah, I see what the issue is. Your approach with isGoogleAuthenticatorEnabled
seems good to me.
Thank you for this wonderful bundle. I have figured it out mostly. But now I have my custom controller where the user can tell the controller he wants to use 2FA. If true, it generates a secret and a set of backup codes, and then redirect to a custom action where it shows a QR code and input field, to verify a successful setup.
The question it, it all works so far, but in my custom controller, upon submitting the field where they have to fill in the generated code to verify, how do I handle this?
In fact I have a POST statement, here I need to check if the OTP is valid, if so, it's marked as 'enabled' in the database. (next to the secret and backup codes array)