Closed patrickdamery closed 3 years ago
Since you obviously reached step "3) On login, do you reach the end (return statement) of method Scheb\TwoFactorBundle\Security\Authentication\Provider\AuthenticationProviderDecorator::authenticate()" from the troubleshooting guide: What is the class of $token
that you see here in line 94:
That class must be listed in security_tokens
in the configuration.
Hey Scheb, thanks for your reply! I've added my token to security_tokens
and now I'm getting as far as step 5. The method is returning an array containing the string 'google'. The user does have a googleAuthenticatorSecret defined and the method isGoogleAuthenticatorSecretEnabled() in the User model does return true.
However I'm still not being prompted for the Google Authenticator code. Do you have any idea as to what could be the problem now?
What's the security token that you see in your session after login? Is it a TwoFactorToken
or something else? You should see that in the debug toolbar.
And could you please post your security.yaml
, thanks.
It is a TwoFactorToken.
security:
encoders:
AppBundle\Entity\User: bcrypt
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
fos_userbundle:
id: fos_user.user_provider.username
api_key_user_provider:
id: AppBundle\Security\ApiKeyUserProvider
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^(?!/api|/login$|/2fa|/2fa_check|/resetting)\S+
logout_on_user_change: true
stateless: false
anonymous: ~
form_login:
default_target_path: dashboard
logout: ~
remember_me:
secret: '%secret%'
guard:
authenticators:
- app.security.login_form_authenticator
two_factor:
csrf_token_generator: security.csrf.token_manager
auth_form_path: 2fa_login # The route name you have used in the routes.yaml
check_path: 2fa_login_check # The route name you have used in the routes.yaml
access_control:
- { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
That firewall pattern looks interesting. Could it be that the path you're ending up after login doesn't match the firewall pattern?
The path I end up in after login in is the dashboard route, which does match the route specified in the firewall for form_login.
I updated the LoginFormAuthenticator to explicitly redirect the user to 2fa_login_check route (even though this should depend on if user has 2fa enabled) and that seems to have helped but now I'm getting an error saying that Symfony can't find the controller for the path /2fa_check
Am I missing some config options for this route? I copied this from the installation instructions:
2fa_login:
path: /2fa
defaults:
_controller: "scheb_two_factor.form_controller:form"
2fa_login_check:
path: /2fa_check
The check path is only accessiable via POST, it is there to validate the 2fa code. You want to force the redirect to the 2fa_login
route to display the 2fa form.
The bundle automatically redirects to the 2fa form whenever you try to access a path that is somehow "secured". Paths that are not within the firewall (don't match the pattern
) and paths that are explicitly configured for anonymous access (IS_AUTHENTICATED_ANONYMOUSLY
) don't do that redirect.
Alright, so I'm now displaying the 2fa_login form, but when I try to submit the code it's calling my default login authenticator and failing because it's setup to use username and password and not the auth_code for authentication. Is this normal? I would've expected the form to submit to a different authenticator to verify the validity of the auth_code.
No that's not normal. I have no idea why your normal authenticator triggers. The requirements for that authenticator shouldn't be fulfilled.
Are you really posting the 2fa code against /2fa_check
?
I am, this is my request header:
POST /2fa_check HTTP/1.1
Host: localhost:8000
Connection: keep-alive
Content-Length: 73
Pragma: no-cache
Cache-Control: no-cache
sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8000
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8000/2fa
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: PHPSESSID=1rsbeqc2t7a9q8vtion1r5cmf0
POST data:
_auth_code: 191684
_csrf_token: pekDTEJjnHvtCocMtAOFNbBVeJxB9669aBIRGBq9Gdg
Response Header:
HTTP/1.1 302 Found
Cache-Control: max-age=0, must-revalidate, private
Content-Type: text/html; charset=UTF-8
Date: Fri, 16 Apr 2021 11:09:33 GMT
Date: Fri, 16 Apr 2021 11:09:33 GMT
Expires: Fri, 16 Apr 2021 11:09:33 GMT
Host: localhost:8000
Location: /login
Set-Cookie: sf_redirect=%7B%22token%22%3A%224f84b2%22%2C%22route%22%3A%222fa_login_check%22%2C%22method%22%3A%22POST%22%2C%22controller%22%3A%22n%5C%2Fa%22%2C%22status_code%22%3A302%2C%22status_text%22%3A%22Found%22%7D; path=/; httponly
X-Debug-Token: 4f84b2
X-Debug-Token-Link: http://localhost:8000/_profiler/4f84b2
X-Powered-By: PHP/7.2.24-0ubuntu0.18.04.7
Content-Length: 268
The redirect of course happens due to my default login authenticator getting called. Do I maybe need to define explicitly what controller the route 2fa_check should use?
No, the /2fa_check
path doesn't need a controller. It's handled completely by the firewall.
You should check where that redirect is coming from. What is causing the application to redirect back to the login form.
I suspect this is caused by your weird firewall pattern
. I don't really understand what you're trying to achieve with that pattern. According to my understanding of the regex, this pattern would exclude the /2fa_check
path from the firewall. If that's the case, that's definitly wrong. Both the /2fa
and the /2fa_check
path must be included in the firewall's pattern, otherwise the authentication process doesn't work.
Yeah that was a mistake, I've removed them from the firewall but the issue still persists.
The redirect is coming from LoginFormAuthenticator because of course it tries to authenticate the user with the username and password but it's not available because it's getting the post data from the 2fa form, so it only gets auth_code. So it then redirects the user to the login page and displays an error message.
The question is, why is LoginFormAuthenticator being called in the first place? Do I have to specify another authenticator on the firewall?
The question is, why is LoginFormAuthenticator being called in the first place? Do I have to specify another authenticator on the firewall?
I can't tell you that. To my understanding, an authenticator is only called when its supports()
method returns true
.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Bundle version: 4.18.4 Symfony version: 3.4.35 PHP version: 7.2.24
Description I'm trying to setup 2 factor authentication with Google Authenticator on a web app I'm working on.
I've setup the User entity with the TwoFactorInterface and I have successfully generated the QR code and saved the secret to the database. However when I try logging in, I'm never prompted for the Google Authenticator Code. I've gone through the troubleshooting steps and I'm having trouble in step 4: getActiveTwoFactorProviders() is never called.
I've checked my bundle's configuration and I don't have anything whitelisted and have disabled trusted devices:
Additional Context I've also had a look at my Login authenticator and can't see anything that would suggest that the roles are loaded by replacing the security token after login, although I haven't actually found where the Role is loaded. As far as I can tell it's a pretty standard implementation of AbstractFormLoginAuthenticator (I didn't write this project just adding 2fa to it).