«invalid_grant» для Google SSO с Symfony4 и knpuuniversity OauthBundle

Когда я выбираю свою учетную запись электронной почты для входа в Google API, у меня систематически возникает ошибка «invalid_grant». После отладки GuardAuthenticator все в порядке до получения пользователя (он извлекает!), но когда я возвращаю пользователя, я попадаю в onAuthenticationFailure с ошибкой «invalid _grant».

Я работаю с Symfony 4.1 и Doctrine ODM:

Вот мой код:

knpu_oauth2_client.yaml:

knpu_oauth2_client:
  clients:
    google:
      # must be "google" - it activates that type!
      type: google
      # add and configure client_id and client_secret in parameters.yml
      client_id: "%env(GOOGLE_CLIENT_ID)%"
      client_secret: "%env(GOOGLE_CLIENT_SECRET)%"
      # a route name you'll create
      redirect_route: connect_google_check
      redirect_params: {}
      # Optional value for sending access_type parameter. More detail: https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters
      # access_type: ''
      # Optional value for sending hd parameter. More detail: https://developers.google.com/identity/protocols/OpenIDConnect#hd-param
      # hosted_domain: ''
      # Optional value for additional fields to be requested from the user profile. If set, these values will be included with the defaults. More details: https://developers.google.com/+/web/api/rest/latest/people
      # user_fields: {}
      # Optional value if you don't want or need to enable Google+ API access.
      # use_oidc_mode: false
      # whether to check OAuth2 "state": defaults to true
      # use_state: true

безопасность.yaml:

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt
    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern: ^/(?!(api|connect))
            form_login:
                provider: fos_userbundle
                csrf_token_generator: security.csrf.token_manager
            anonymous: ~
            logout:
                path: /logout
                target: /login
            remember_me:
                secret: '%env(APP_SECRET)%'
            guard:
                authenticators:
                    - App\Security\GuardAuthenticator\GoogleAuthenticator
                    - App\Security\GuardAuthenticator\LoginFormAuthenticator
                    - lexik_jwt_authentication.jwt_token_authenticator
                entry_point: App\Security\GuardAuthenticator\GoogleAuthenticator
        social:
            pattern: ^/connect/
            stateless: true
            anonymous: ~
            guard:
                authenticators:
                    - App\Security\GuardAuthenticator\GoogleAuthenticator

    access_control:
         - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/connect/google, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/, roles: ROLE_USER }
         - { path: ^/api, roles: ROLE_USER }

GoogleAuthenticator:

<?php
declare(strict_types=1);

namespace App\Security\GuardAuthenticator;

use App\Document\User;
use Doctrine\ODM\MongoDB\DocumentManager;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use League\OAuth2\Client\Provider\GoogleUser;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Router;
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\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use KnpU\OAuth2ClientBundle\Client\Provider\GoogleClient;

class GoogleAuthenticator extends SocialAuthenticator
{
    /**
     * @var ClientRegistry
     */
    protected $client;
    /**
     * @var DocumentManager
     */
    protected $documentManager;
    /**
     * @var Router
     */
    protected $router;

    public function __construct(
        ClientRegistry $client,
        DocumentManager $documentManager,
        RouterInterface $router
    ) {
        $this->client = $client;
        $this->documentManager = $documentManager;
        $this->router = $router;
    }

    /**
     * @param Request $request The request that resulted in an AuthenticationException
     * @param AuthenticationException $authException The exception that started the authentication process
     *
     * @return Response
     */
    public function start(
        Request $request,
        AuthenticationException $authException = null
    ) {
        return new JsonResponse(['message' => $authException->getMessage()], JsonResponse::HTTP_UNAUTHORIZED);
    }

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

    /**
     * @param Request $request
     *
     * @return mixed Any non-null value
     *
     * @throws \UnexpectedValueException If null is returned
     */
    public function getCredentials(Request $request)
    {
        return $this->fetchAccessToken($this->getGoogleClient());
    }

    /**
     * @param mixed $credentials
     * @param UserProviderInterface $userProvider
     *
     * @throws AuthenticationException
     *
     * @return UserInterface|null
     */
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        /** @var GoogleUser $googleUser */
        $googleUser = $this->getGoogleClient()->fetchUserFromToken($credentials);
        $userEmail = $googleUser->getEmail();

        $user = $this->documentManager
            ->getRepository(User::class)
            ->findOneBy(['email' => $userEmail]);

        if ($user) {
            return $user;
        }

        return null;
    }

    /**
     * @param Request $request
     * @param AuthenticationException $exception
     *
     * @return JsonResponse
     */
    public function onAuthenticationFailure(
        Request $request,
        AuthenticationException $exception
    ): JsonResponse {
        return new JsonResponse(
            [
                'message' => strtr(
                    $exception->getMessageKey(),
                    $exception->getMessageData())
            ],
            JsonResponse::HTTP_FORBIDDEN
        );
    }

    /**
     * @param Request $request
     * @param TokenInterface $token
     * @param string $providerKey The provider (i.e. firewall) key
     *
     * @return Response|null
     */
    public function onAuthenticationSuccess(
        Request $request,
        TokenInterface $token,
        $providerKey
    ) {
        $user = $token->getUser();
        $googleApiToken = $this->fetchAccessToken($this->getGoogleClient());

        $user->setApiToken($googleApiToken);
        $this->documentManager->persist($user);
        $this->documentManager->flush();

        return null;
    }

    /**
     * @return GoogleClient
     */
    private function getGoogleClient(): GoogleClient
    {
        return $this->client->getClient('google');
    }
}

GoogleController.php:

<?php
declare(strict_types=1);

namespace App\Controller;

use App\Document\User;
use App\Logger\PlanningLogger;
use Doctrine\ODM\MongoDB\DocumentManager;
use FOS\RestBundle\Controller\Annotations as Rest;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

class GoogleController extends AbstractController
{
    /**
     * @var ClientRegistry
     */
    protected $clientRegistry;

    public function __construct(
        DocumentManager $documentManager,
        LoggerInterface $logger,
        ClientRegistry $clientRegistry
    ) {
        parent::__construct($documentManager, $logger);
        $this->clientRegistry = $clientRegistry;
    }

    /**
     * @Rest\Get("/connect/google", name = "connect_google")
     *
     * @return RedirectResponse
     */
    public function connect()
    {
        return $this->clientRegistry->getClient('google')->redirect();
    }

    /**
     * @Rest\Get("/connect/google_check", name = "connect_google_check")
     *
     * @param Request $request
     * @throws \League\OAuth2\Client\Provider\Exception\IdentityProviderException
     */
    public function connectCheckAction(Request $request) {}
}

Я следил за документацией oauth2-клиент-пакет, но не смог ее решить.

Кто-нибудь может помочь мне решить эту проблему, пожалуйста?

Спасибо !

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
0
533
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам нужно установить поле access_type в вашем knpu_oauth2_client.yaml

knpu_oauth2_client:
  clients:
    google:
      .....

      access_type: 'offline'
      ...

Извини, думал, что ответил ... Ты сделал мой день, чувак, еще раз спасибо :)

Cyril Z. 12.03.2019 22:17

Другие вопросы по теме