Oauth 2.0 授予授权后不一致的oAuth2登录

Oauth 2.0 授予授权后不一致的oAuth2登录,oauth-2.0,discord,symfony4,Oauth 2.0,Discord,Symfony4,我有一个symfony4应用程序,我正在使用knpuniversity/oauth2客户机包根据discords oAuth端点对用户进行身份验证 用户单击“通过Discord登录”按钮,他从Discord看到授权页面,接受该页面,现在登录到我的页面。 到目前为止还不错 以下是不起作用的内容: 用户在另一台计算机上,因此我的页面上没有会话。他登录到discord web客户端。之后,他访问了我的页面,但他没有登录。 当他再次单击“通过Discord登录”时,他会再次看到授权页面,而不是直接登录到

我有一个symfony4应用程序,我正在使用knpuniversity/oauth2客户机包根据discords oAuth端点对用户进行身份验证

用户单击“通过Discord登录”按钮,他从Discord看到授权页面,接受该页面,现在登录到我的页面。
到目前为止还不错

以下是不起作用的内容:
用户在另一台计算机上,因此我的页面上没有会话。他登录到discord web客户端。之后,他访问了我的页面,但他没有登录。 当他再次单击“通过Discord登录”时,他会再次看到授权页面,而不是直接登录到我的页面

也许我在这里出错了,但通常当我使用oAuth登录google、facebook或其他类似于stackoverflow的东西时,我再也看不到授权页面。我单击“使用XY登录”,只要我登录到相应的帐户,我也会立即登录到另一个页面

<?php

namespace App\Security;

use App\Entity\User;
use App\Repository\UserRepository;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Client\OAuth2Client;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use League\OAuth2\Client\Token\AccessToken;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class DiscordAuthenticator extends SocialAuthenticator
{
    /**
     * @var \KnpU\OAuth2ClientBundle\Client\ClientRegistry
     */
    private $clientRegistry;

    /**
     * @var \App\Repository\UserRepository
     */
    private $repository;

    /**
     * @var \Symfony\Component\Routing\RouterInterface
     */
    private $router;

    /**
     * DiscordAuthenticator constructor.
     *
     * @param \KnpU\OAuth2ClientBundle\Client\ClientRegistry $clientRegistry
     * @param \App\Repository\UserRepository                 $repository
     * @param \Symfony\Component\Routing\RouterInterface     $router
     */
    public function __construct(
        ClientRegistry $clientRegistry,
        UserRepository $repository,
        RouterInterface $router
    ) {
        $this->clientRegistry = $clientRegistry;
        $this->repository = $repository;
        $this->router = $router;
    }

    /**
     * @inheritDoc
     */
    public function start(Request $request, AuthenticationException $authException = null)
    {
        return new RedirectResponse('/login/', Response::HTTP_TEMPORARY_REDIRECT);
    }

    /**
     * @inheritDoc
     */
    public function supports(Request $request): bool
    {
        return $request->attributes->get('_route') === 'discord_set_token';
    }

    /**
     * @inheritDoc
     */
    public function getCredentials(Request $request)
    {
        return $this->fetchAccessToken($this->getDiscordClient());
    }

    /**
     * @inheritDoc
     */
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        try {
            /** @var \Wohali\OAuth2\Client\Provider\DiscordResourceOwner $discordUser */
            $discordUser = $this->getDiscordClient()
                                ->fetchUserFromToken($credentials);

            $email = $discordUser->getEmail();

            $existingUser = $this->repository->findOneBy(
                [
                    'externalId'       => $discordUser->getId(),
                    'externalIdSource' => 'discord',
                ]
            );

            if ($existingUser) {
                return $existingUser;
            }

            $user = $this->repository->findOneBy(['email' => $email]);

            if (!$user) {
                $user = new User(
                    $discordUser->getId(),
                    'discord',
                    $discordUser->getUsername(),
                    $discordUser->getEmail()
                );
                $user->setExternalId($discordUser->getId());
                $user->setExternalIdSource('discord');
                $user->setToken($credentials->getToken());
                $user->setRefreshToken($credentials->getRefreshToken());
                $user->setTokenExpiresFromTimestamp($credentials->getExpires());
            }

            $this->repository->persist($user);

            return $user;
        } catch (\Throwable $e) {
            throw new AuthenticationException($e->getMessage(), $e->getCode(), $e);
        }
    }

    /**
     * @inheritDoc
     */
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
    {
        $message = strtr($exception->getMessageKey(), $exception->getMessageData());

        return new Response($message, Response::HTTP_FORBIDDEN);
    }

    /**
     * @inheritDoc
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): Response
    {
        $targetUrl = $this->router->generate('home');

        return new RedirectResponse($targetUrl);
    }

    /**
     * @return \KnpU\OAuth2ClientBundle\Client\OAuth2Client
     */
    private function getDiscordClient(): OAuth2Client
    {
        return $this->clientRegistry
            ->getClient('discord');
    }
}

在我写这篇文章的时候,我没有太多时间,只是简单地查看了一下文档

现在我有了更多的时间,花了一些时间阅读了整个Discords oAuth文档


然后,我找到了查询参数
提示符
,当设置为
时,该提示符只会请求授权一次,否则将直接重定向到
重定向uri

您是否使用了保护身份验证器?如果没有看到一些代码,就很难知道发生了什么。请将相关代码添加到您的问题中,谢谢。我已经添加了有关安全性的必要代码您的用户存储库是否确实有持久化方法
$this->repository->persist($user)
?你能发布吗?我已经添加了用户存储库。我还检查了我的用户被持久化的数据库。为什么
merge()
而不是
persist()
<?php

namespace App\Security;

use App\Entity\User;
use App\Repository\UserRepository;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;

class UserProvider implements UserProviderInterface
{
    /**
     * @var \App\Repository\UserRepository
     */
    private $repository;

    /**
     * UserProvider constructor.
     *
     * @param \App\Repository\UserRepository $repository
     */
    public function __construct(UserRepository $repository)
    {
        $this->repository = $repository;
    }

    /**
     * @inheritDoc
     */
    public function loadUserByUsername($username): UserInterface
    {
        $user = $this->repository->findOneBy(['email' => $username]);

        if (!$user) {
            throw new UsernameNotFoundException(sprintf('No User with username %s found', $username));
        }

        return $user;
    }

    /**
     * @inheritDoc
     */
    public function refreshUser(UserInterface $user): UserInterface
    {
        if (!$user instanceof User) {
            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
        }

        // TODO find out how to use the refresh token to get a new one

        return $user;
    }

    /**
     * @inheritDoc
     */
    public function supportsClass($class): bool
    {
        return $class === User::class;
    }
}
security:
    providers:
        user_provider:
            entity:
                class: App:User
                property: username
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true

            logout:
                path: /logout
                target: /login

            guard:
                authenticators:
                    - App\Security\DiscordAuthenticator

    access_control:
        # - { path: ^/admin, roles: ROLE_ADMIN }
<?php

namespace App\Repository;

use App\Entity\User;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\EntityManager;

/**
 * Class UserRepository
 */
class UserRepository
{
    /**
     * @var \Doctrine\ORM\EntityManager
     */
    private $entityManager;

    /**
     * @var \Doctrine\ORM\EntityRepository
     */
    private $repository;

    public function __construct(EntityManager $entityManager)
    {
        $this->entityManager = $entityManager;
        $this->repository = $entityManager->getRepository(User::class);
    }

    /**
     * Finds an entity by its primary key / identifier.
     *
     * @param int $id
     *
     * @return \App\Entity\User|null
     */
    public function find(int $id): ?User
    {
        return $this->repository->find($id);
    }

    /**
     * Finds all entities in the repository.
     *
     * @return \App\Entity\User[]
     */
    public function findAll(): iterable
    {
        return $this->repository->findAll();
    }

    /**
     * Finds entities by a set of criteria.
     *
     * @param array      $criteria
     * @param array|null $orderBy
     * @param int|null   $limit
     * @param int|null   $offset
     *
     * @return \App\Entity\User[]
     */
    public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null): iterable
    {
        return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
    }

    /**
     * Finds a single entity by a set of criteria.
     *
     * @param array      $criteria
     * @param array|null $orderBy
     *
     * @return \App\Entity\User|null
     */
    public function findOneBy(array $criteria, array $orderBy = null): ?User
    {
        return $this->repository->findOneBy($criteria, $orderBy);
    }

    /**
     * Counts entities by a set of criteria.
     *
     * @param array $criteria
     *
     * @return int
     */
    public function count(array $criteria): int
    {
        return $this->repository->count($criteria);
    }

    /**
     * Select all elements from a selectable that match the expression and
     * return a new collection containing these elements.
     *
     * @param \Doctrine\Common\Collections\Criteria $criteria
     *
     * @return \App\Entity\User[]
     */
    public function matching(Criteria $criteria): iterable
    {
        return $this->repository->matching($criteria);
    }

    /**
     * @param \App\Entity\User $user
     *
     * @throws \Doctrine\ORM\ORMException
     * @throws \Doctrine\ORM\OptimisticLockException
     */
    public function persist(User $user): void
    {
        $this->entityManager->merge($user);
        $this->entityManager->flush();
    }
}
<?php

namespace App\Controller;

use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * @Route(path="discord/")
 */
class DiscordOAuthController extends AbstractController
{
    /**
     * @var \KnpU\OAuth2ClientBundle\Client\ClientRegistry
     */
    private $clientRegistry;

    /**
     * DiscordOAuthController constructor.
     *
     * @param \KnpU\OAuth2ClientBundle\Client\ClientRegistry $clientRegistry
     */
    public function __construct(ClientRegistry $clientRegistry)
    {
        $this->clientRegistry = $clientRegistry;
    }

    /**
     * @Route(name="discord_redirect_authorization", path="redirect_authorization")
     *
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function redirectAuthorizationAction(): Response
    {
        return $this->clientRegistry->getClient('discord')
                                    ->redirect(['identify', 'email', 'guilds']);
    }

    /**
     * @Route(name="discord_set_token", path="set_token")
     *
     * @return void
     */
    public function setTokenAction(): void
    {
        // empty as authenticator will handle the request
    }
}