如何删除身份验证令牌“角色”以实施MFA系统?

时间:2019-09-18 21:28:36

标签: php symfony authentication symfony4

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

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对象。

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

1 个答案:

答案 0 :(得分:1)

您无法设置令牌的role属性。如您所见,TokenInterface不包含角色的设置器。

您不能setAuthenticated(false),因为那样您将清除第一个身份验证因素,并将bac设置为零。

但是我相信您正在以错误的方式解决问题。

保留令牌,并使用onAuthenticationSuccess的侦听器检查User::organization::getMfa()以确定是否需要:

  • 向用户发送辅助身份验证代码
  • 您要进行身份验证的位置的存储尚未完成(例如,在会话中,用户属性中的DB中,为此存储的单独存储等)。

(就我个人而言,我将MFA的要求设置为另一个角色,因此可以将其存储在令牌中,而您无需为此检查其他事项)。

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

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

  • 如果您没有令牌,则return,这是未经身份验证的视图,而不是侦听器问题。

  • 如果用户不需要MFA,则只需return;这里没事。

  • 如果用户需要MFA,但不在认证过程中,则您return。这意味着用户已经通过了完整的身份验证,可以继续进行操作。

  • 如果用户需要MFA并且处于身份验证过程的中间,则您将显示完成该身份的任何方式。通常是表格。

  • 如果用户需要MFA并且处于身份验证过程中,并且他们向您发送了检查此身份验证的方式(例如,带有相应字段的POST请求),则您验证身份验证是否正确。

  • 验证好吗?您清除“身份验证正在进行中”位,然后返回。进一步的请求将不会被拦截。

  • 验证不正确吗?您要么让他们再次检查,然后重新发送验证码,然后将用户完全注销,以便他们再次开始...究竟这取决于您的要求。

完整的代码实现将取决于您,因为它不可能涵盖所有可能的情况。

但是,您的订阅者可能会是这样的(请注意,还有两个未实现的方法)。

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',
        ];
    }
}