scheb / two-factor-bundle

[ABANDONED] Two-factor authentication for Symfony 2 & 3 applications 🔐. Please use the newer versions from https://github.com/scheb/2fa.
https://github.com/scheb/2fa
MIT License
385 stars 111 forks source link

Can't get 2FA to work #260

Closed RootProgger closed 4 years ago

RootProgger commented 4 years ago

Bundle version:4.11 Symfony version: 5.0.2

Description i have a fresh installed app wich has no anonymously authenticated sites. on app start the login page is showing. auth is generated with bin/console make:user & make:auth, with guard authentication. i tried to configure my firewall as in your tow-factor-app on the guard branch. but nothing happen. after login, i was redirected to my target-path, not to 2FA.

Additional Context
security.yml:

security:
    role_hierarchy:
        ROLE_MERCHANT: ROLE_USER
        ROLE_ADMIN: ROLE_MERCHANT
        ROLE_SUPERADMIN: ROLE_ADMIN

    encoders:
        App\Entity\User:
            algorithm: auto

    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern: ^/
            anonymous: ~
            provider: app_user_provider
            guard:
                authenticators:
                    - App\Security\SecurityControllerAuthenticator
            logout:
                path: app_logout
                target: dashboard

            two_factor:
                auth_form_path: 2fa_login
                check_path: 2fa_login_check
                provider: app_user_provider
                auth_code_parameter_name: _auth_code
                trusted_parameter_name: _trusted
                default_target_path: /
                always_use_default_target_path: false
                csrf_token_generator: security.csrf.token_manager

    access_control:
        - { path: ^/logout, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
        - { path: ^/, role: ROLE_USER }

packages/scheb_two_factor.yaml

scheb_two_factor:
    security_tokens:
        - Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken
    google:
        enabled: true
        server_name: '%env(TWOFACTOR_SERVER_NAME)%'

    ip_whitelist: ~

config/routes/sheb_two_factor.yaml:

2fa_login:
    path: /2fa
    defaults:
        _controller: "scheb_two_factor.form_controller:form"

2fa_login_check:
    path: /2fa_check

User-Entity:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 * @UniqueEntity(fields={"email"}, message="There is already an account with this email")
 */
class User implements UserInterface, TwoFactorInterface
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=180, unique=true)
     */
    private $email;

    /**
     * @ORM\Column(type="json")
     */
    private $roles = [];

    /**
     * @var string The hashed password
     * @ORM\Column(type="string")
     */
    private $password;

    /**
     * @var string
     * @ORM\Column(type="string", nullable=true)
     */
    private $googleAuthenticatorSecret;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    /**
     * A visual identifier that represents this user.
     *
     * @see UserInterface
     */
    public function getUsername(): string
    {
        return (string) $this->email;
    }

    /**
     * @see UserInterface
     */
    public function getRoles(): array
    {
        $roles = $this->roles;
        // guarantee every user at least has ROLE_USER
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

    public function setRoles(array $roles): self
    {
        $this->roles = $roles;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function getPassword(): string
    {
        return (string) $this->password;
    }

    public function setPassword(string $password): self
    {
        $this->password = $password;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function getSalt()
    {
        // not needed when using the "bcrypt" algorithm in security.yaml
    }

    /**
     * @see UserInterface
     */
    public function eraseCredentials()
    {
        // If you store any temporary, sensitive data on the user, clear it here
        // $this->plainPassword = null;
    }

    /**
     * @inheritDoc
     */
    public function isGoogleAuthenticatorEnabled(): bool
    {
        return true;
    }

    /**
     * @inheritDoc
     */
    public function getGoogleAuthenticatorUsername(): string
    {
        return $this->email;
    }

    /**
     * @inheritDoc
     */
    public function getGoogleAuthenticatorSecret(): ?string
    {
        return $this->googleAuthenticatorSecret;
    }

    /**
     * @param string $googleAuthenticatorSecret
     *
     * @return User
     */
    public function setGoogleAuthenticatorSecret(string $googleAuthenticatorSecret): User
    {
        $this->googleAuthenticatorSecret = $googleAuthenticatorSecret;
        return $this;
    }
}
scheb commented 4 years ago

I'd suspect there's no googleAuthenticatorSecret set, it returns null for that user. This is a requirement for GoogleAuthenticator to work.

If you're absolutely sure there's a secret set and returned, please follow the troubleshooting guide for "Two-factor authentication form is not shown after login":

https://github.com/scheb/two-factor-bundle/blob/master/Resources/doc/troubleshooting.md#two-factor-authentication-form-is-not-shown-after-login

RootProgger commented 4 years ago

Hi @scheb and thanks for formatting my previous post.

you are right, no google-auth-secret is set in my code. i don't know where i should place this code. in your two-factor-app (guard) you have the secret in your SecurityController accessible over URL:

/**

  • @Route("/googleSecret", name="generate_google_secret") */ public function googleSecret(GoogleAuthenticatorInterface $googleAuthenticator) { return new Response($googleAuthenticator->generateSecret()); }

i don't find any other implementations on that app.

or have I thought incorrectly about that? do i have to call up authentication separately? after the first login or after registering, set up the 2FA and set the secret there?

scheb commented 4 years ago

You have to set a secret code on the user entity, on the googleAuthenticatorSecret attribute. Typically, this is done during account creation or later on a the account settings. This is something you have to implement.

A valid secret can be generated via scheb_two_factor.security.google_authenticator service, see: https://github.com/scheb/two-factor-bundle/blob/master/Resources/doc/providers/google.md#generating-a-secret-code