如何移除身份验证令牌"roles"以实现MFA系统?

How to remove the authentication token "roles" to implement a MFA system?

我想在登录后为一部分用户集成辅助身份验证措施:

public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
    $user = $token->getUser();

    if ($user->getOrganization()->getMfa() {

        //$token->setAuthenticated(false);
        $token->setRoles(null);

        return new RedirectResponse($this->router->generate('mfa'));
    }

    return new RedirectResponse($this->router->generate('home'));
}

然后我想在另一个控制器中验证安全码:

if((int)$session->get('authCode') === (int)$request->get('auth_code')) {

    //$token->setAuthenticated(true);
    $token->setRoles($user->getRoles());

    return new RedirectResponse($this->router->generate('home'));
}

setAutheticated(false) 的问题是我在方法完成后无法访问 $user 对象。

我正在考虑删除授予用户的角色;但我不知道如何从现有令牌中删除角色。

不能设置令牌的角色属性。如您所见,TokenInterface 不包含角色的 setter。

而且您不能 setAuthenticated(false),因为那样您将清除第一个身份验证 因素,然后返回到零。

但我相信您处理问题的方式是错误的。

保持令牌不变,使用 onAuthenticationSuccess 的侦听器检查 User::organization::getMfa() 以决定是否需要:

  • 向用户发送二次验证码,
  • 将身份验证未完成的地方存储在您所在的位置(例如,在会话中,在用户 属性 的数据库中,为此单独存储,等等)。

(就个人而言,我会将 MFA 的要求作为另一个角色,这样就可以将其存储在令牌上,您不需要为此检查其他内容)。

然后,为每个请求创建一个event subscriber

在该侦听器上,您将注入 use Symfony\Component\Security\Core\Security 以访问用户并查看是否 User::organization::getMfa() === true;以及检查身份验证是否未完成的任何方式(例如,如果您存储在会话中,或直接检查用户实体等)。

  • 如果你没有令牌,你return,这是一个未经验证的视图,这不是听众的问题。

  • 如果用户不需要MFA,你就return;此处无事可做。

  • 如果用户需要 MFA 但它不在身份验证过程中,您 return。这意味着用户已经完全验证,他们可以继续。

  • 如果用户需要 MFA 并且正处于身份验证过程中,您会显示任何方法来完成它。通常是表格。

  • 如果用户需要 MFA 并且正在进行身份验证过程并且他们会向您发送检查此身份验证的方法(例如 POST 请求适当的字段),您验证身份验证是否正确。

  • 验证好了吗?您清除 "authentication in process" 位和 return。不会拦截更多请求。

  • 验证有问题吗?您要么让他们再次检查,重新发送验证码,将用户完全注销以便他们重新开始...具体取决于您的要求。

完整的代码实现将由您决定,因为涵盖所有可能的场景是不可能的。

但是您的订阅者可能是这样的,首先(请注意还有一些未实现的方法)。

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Security;

class MfaSubscriber implements EventSubscriberInterface
{

    private $security;

    public function __construct(Security $security)
    {
        $this->security = $security;
    }

    public function onRequest(RequestEvent $event)
    {

        if ($event->getRequestType() === HttpKernelInterface::SUB_REQUEST) {
            return;
        }

        $request = $event->getRequest();

        if ( null == $token = $this->security->getToken()) {
            return;
        }

        if ( ! in_array('ROLE_NEEDS_MFA', $token->getRoleNames(), true)) {
            return;
        }

        if ( ! $request->hasPreviousSession() || ! $request->getSession()->get('AUTHENTICATION_IN_PROCESS')) {
            return;
        }

        if (
            $request->getMethod() === Request::METHOD_POST
            && $request->get('verification_code')
            && $this->verifyCode($request->get('verification_code')) // verify code needs to be implemented
        ) {
            $request->getSession()->set('AUTHENTICATION_IN_PROCESS', null);

            return;
        }

        $event->setResponse($this->displayMfaVerificationMethod($request, $token));

    }

    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => 'onRequest',
        ];
    }
}