knpuniversity / oauth2-client-bundle

Easily talk to an OAuth2 server for social functionality in Symfony
https://symfonycasts.com
MIT License
787 stars 145 forks source link

Error fetching OAuth credentials: "Missing required parameter [code_verifier]. #428

Closed kasali closed 10 months ago

kasali commented 11 months ago

I have this error after clicking the authorize button. My config : knpu_oauth2_client.yaml :

knpu_oauth2_client:
    clients: 
        twitter:
            type: generic
            provider_class: '\Smolblog\OAuth2\Client\Provider\Twitter'
            client_id: '%env(resolve:TWITTER_CLIENT_ID)%'
            client_secret: '%env(resolve:TWITTER_CLIENT_SECRET)%'
            redirect_route: connect_twitter_check
            redirect_params: {} 

My routes :

    #[Route(path: "/Connexion/twitter", name: "app_twitter_start")]
    public function redirectToTwitter(ClientRegistry $clientRegistry)
    {
        $session = $request->getSession();
        $codeVerifier = bin2hex(random_bytes(64));

        // Generate PKCE code challenge
        $codeChallenge = rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');

        // Store the code verifier in the session
        $session->set('oauth2verifier', $codeVerifier);

        $authUrl = $clientRegistry
            ->getClient('twitter')
            ->redirect(['users.read'], []);
        // dd($authUrl);
      return $authUrl;
    }

    #[Route(path: "/Connexion/twitter/check", name: "connect_twitter_check")]
    public function connectTwitter(Request $request, ClientRegistry $clientRegistry)
    {
        // Will fetch the access token and user profile
        // $client = $clientRegistry->getClient('twitter');
        //  dd($client);
        // $token = $client->getAccessToken();
        // $userProfile = $client->fetchUserFromToken($token);

        // Now you can work with the $userProfile object
        // For example, you might store user information in the database
        // and log the user in.

        // Redirect or do whatever you need here
    }

Authenticate method :

    public function authenticate(Request $request): Passport
    {
        $client = $this->clientRegistry->getClient('twitter');
       $session = $request->getSession();
         // Get the code verifier from the session
         $codeVerifier = $session->get('oauth2verifier');
         $authorizationCode = $request->query->get('code');
         rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');
       $clientId = $_ENV['TWITTER_CLIENT_ID'];
        $clientSecret = $_ENV['TWITTER_CLIENT_SECRET'];
        $base64Credentials = base64_encode(sprintf('%s:%s', $clientId, $clientSecret));

        // Set the Authorization header
        $authorizationHeader = sprintf('Basic %s', $base64Credentials);
        // dd($authorizationHeader);
        try {
            $accessToken = $this->fetchAccessToken($client, [
                'code' => $authorizationCode,
                'code_verifier' => $codeVerifier,
                'headers' => [
                    'Authorization' => $authorizationHeader,
                ],  
            ]);
        } catch (IdentityProviderException $e) {
            // Log or handle the error
            throw $e;

            echo $e->getMessage();
        }

         dd($accessToken);
        return new SelfValidatingPassport(
            new UserBadge($accessToken->getToken(), function () use ($accessToken, $client) {
                /** @var TwitterUser $twitterUser */
                $twitterUser = $client->fetchUserFromToken($accessToken);
                dd($twitterUser);
                $email = $twitterUser->getEmail();
                // dd($email);
                // dd($accessToken);
                // 1) have they logged in with Facebook before? Easy!
                $existingUser = $this->entityManager->getRepository(User::class)->findOneBy(['twitterId' => $twitterUser->getId()]);
                // dd($existingUser);
                if ($existingUser) {
                    return $existingUser;
                } else {
                    // 2) do we have a matching user by email?
                    $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $email]);
                    // dd($user);
                    if (!$user) {
                        /** @var Particulier $user */
                        $user = new Particulier();
                        $user->setEmail($email);

                    }
                }
                // 3) Maybe you just want to "register" them by creating
                // a User object
                $user->setTwitterId($twitterUser->getId());
                $user->setTwitterAccessToken($accessToken);
                $this->entityManager->persist($user);
                $this->entityManager->flush();

                return $user;
            })
        );
    }
kasali commented 10 months ago

Now i updated my code by generating the code_verifier but I get this error : Error fetching OAuth credentials: "Missing valid authorization header". From this post I turn on Public client and I have this : Error fetching OAuth credentials: "Value passed for the authorization code was invalid.".

sadikoff commented 10 months ago

hey

rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');

what is the purpose of this row?

kasali commented 10 months ago

Hello sadikoff,

It's for encoding in base64 and haching the code_verifier.