Symfony 4:如何通过GuardAuthenticator实现OpenId Connect?

时间:2019-07-18 17:10:17

标签: openid symfony4 openid-connect

我正在尝试仅在产品环境中通过Symfony 4项目上的OpenId Connect实现身份验证。 我创建了一个扩展GuardAuthenticator的类,它必须实现几种方法:supports,getCredentials,getUser,checkCredentials,onAuthenticationSuccess等...

我正在使用Jumbojett \ OpenIDConnectClient。

我找不到任何可以帮助我构造该GuardAuthenticator类的教程或文档。

(对我而言)OpenId Connect拥有沉重的协议,但是Jumbojett \ OpenIDConnectClient可以很好地对其进行排序。但是,我想在GuardAuthenticator的正确方法中正确使用它。

目前,我的问题是正确构造GuardAuthenticator的代码。

该项目在Linux Debian,php 7.1和Symfony 4上运行。 我的OpenID提供程序是本地计算机上的Keycloak容器。但是,该项目必须使用与Keycloak稍有不同的公司OpenId Provider。而且我很少能在上面测试我的代码。

我扩展GuardAuthenticator的类CustomAuthenticator正在使用OpenIDConnectClient在方法“ getCredentials”中完成整个过程。

当用户通过OpenId Provider进行身份验证时,我的类进入方法“ onAuthenticationSuccess”,并将ID令牌存储在Session(和数据库用户对象)中。 在“ onAuthenticationSuccess”之后,脚本进入重定向的Controller,然后以编程方式登录用户。

在那之后,我使用(GuardAuthenticator的)“ supports”方法来检查会话中的ID令牌,然后检查了一些作为JWT签名的东西(我不确定在那儿检查什么。我要求从我的OpenID提供程序验证会话ID令牌?)。

我真的不知道是否可以。它似乎在某个时候起作用。 将ID令牌存储在会话中以便检查用户身份验证是否可以?

任何帮助表示赞赏。

<?php

//...
use Doctrine\ORM\EntityManagerInterface;
//..
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;

use Jumbojett\OpenIDConnectClient;

class MyGuardAuthenticator extends AbstractGuardAuthenticator
{
    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
        $this->openIdConfig = $this->getOpenIdConfig();
        $this->openIdConnector = $this->initOpenIdConnect();
    }
   //...

    public function checkCredentials($credentials, UserInterface $user)
    {
        // What should i do here?
        return true;
    }

   //...

    // I check if user has to authenticate against OpenID Provider
    public function supports(Request $request)
    {

        if (isset($_SESSION['id_token'])) {
            $idToken = $_SESSION['id_token'];

            $existingUser = $this->em->getRepository(User::class)
                ->findOneBy(['idToken' => $idToken]);
            if (null === $existingUser) {
                return true;
            }

            // Signature false?
            $isOk = $this->openIdConnector->verifyJWTsignature($idToken);
            if (!$isOk) {
                return true;
            }

            $idTokenPayload = $this->decodeJWT($idToken, 1);
            // expired?
            $now = new \DateTime();
            if ($now->getTimeStamp() > (int) $idTokenPayload->exp) {
                return true;
            }

            //Accept authenticated user
            return false;
        }

        return true;
    }

    // I start the protocol here
    public function getCredentials(Request $request)
    {
        $data = $this->callProvider();

        return $data->preferred_username;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {

        $existingUser = $this->em->getRepository(User::class)
            ->findOneBy(['username' => strtoupper($credentials)]);
        if ($existingUser) {
            return $existingUser;
        }
    }

//...

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {

        $existingUser = $this->em->getRepository(User::class)
            ->findOneBy(['username' => $token->getUsername()]);

        if ($existingUser) {
            $existingUser->setIdToken($_SESSION['id_token']);
            $this->em->flush();
        }

        return null;
    }

    public function start(Request $request, AuthenticationException $authException = null)
    {
        return new RedirectResponse(
            '/login/', // might be the site, where users choose their oauth provider
            Response::HTTP_TEMPORARY_REDIRECT
        );
    }

//...

    private function callProvider()
    {
        $this->openIdConnector->setRedirectURL($this->openIdConfig['redirect_uri']);

        $this->openIdConnector->providerConfigParam(
            array(
                'issuer'                                => self::BASE_URL.'/auth/realms/myrealm',
                'authorization_endpoint'                =>
                    self::BASE_URL."/auth/realms/myrealm/protocol/openid-connect/auth?scope=openid",
                'token_endpoint'                        =>
                    self::BASE_URL."/auth/realms/myrealm/protocol/openid-connect/token",
                'userinfo_endpoint'                     =>
                    self::BASE_URL."/auth/realms/myrealm/protocol/openid-connect/userinfo",
                'end_session_endpoint'                  =>
                    self::BASE_URL."/auth/realms/myrealm/protocol/openid-connect/logout",
                'registration_endpoint'                 =>
                    self::BASE_URL."/auth/realms/myrealm/clients-registrations/openid-connect",
                'jwks_uri'                              =>
                    self::BASE_URL."/auth/realms/myrealm/protocol/openid-connect/certs",
                'token_endpoint_auth_methods_supported' => array('authorization_code'),
            )
        );

        $this->openIdConnector->addScope($this->openIdConfig['scope']);
        $this->openIdConnector->setVerifyPeer(false);
        $this->openIdConnector->authenticate();
        $token = $this->openIdConnector->getIdToken();

        if (null !== $token) {
            $_SESSION['id_token'] = $token;
        }
        $data = $this->openIdConnector->requestUserInfo();

        return $data;
    }


0 个答案:

没有答案