Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Telegram login not working with custom provider #412

Open
vitoff34 opened this issue Aug 12, 2023 · 1 comment
Open

Telegram login not working with custom provider #412

vitoff34 opened this issue Aug 12, 2023 · 1 comment

Comments

@vitoff34
Copy link

vitoff34 commented Aug 12, 2023

Hello,
I want to implement custom provider so users can log in / sign up with telegram in my Symfony 5.4 app. I do not want to use their widget.
The problem is that when a user accepts the login on the telegram auth url the window closes and I am not redirected back to my app for authentication.

Here is my code:

My TelegramProvider:


namespace App\Client;

use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Token\AccessToken;
use Psr\Http\Message\ResponseInterface;

class TelegramAuthProvider extends AbstractProvider
{

    public function getBaseAuthorizationUrl()
    {
        return 'https://oauth.telegram.org/auth';
    }

    public function getBaseAccessTokenUrl(array $params)
    {
        return 'https://oauth.telegram.org/auth';
    }

    public function getResourceOwnerDetailsUrl(AccessToken $token)
    {
        return 'https://oauth.telegram.org/auth'. $token;
    }

    protected function getDefaultScopes()
    {
        return ['user_info'];
    }

    protected function checkResponse(ResponseInterface $response, $data)
    {
        if (empty($data['error'])) {
            return;
        }
    }

    protected function createResourceOwner(array $response, AccessToken $token)
    {
        return $response;
    }
}

This is my telegram authenticator:

<?php

namespace App\Security;

use App\Entity\User;

// your user entity
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Security\Authenticator\OAuth2Authenticator;
use League\OAuth2\Client\Provider\GoogleUser;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\String\ByteString;

class TelegramAuthenticator extends OAuth2Authenticator implements AuthenticationEntrypointInterface
{
    private ClientRegistry $clientRegistry;
    private EntityManagerInterface $entityManager;
    private RouterInterface $router;
    private UserPasswordHasherInterface $hasher;

    public function __construct(ClientRegistry $clientRegistry, EntityManagerInterface $entityManager, RouterInterface $router, UserPasswordHasherInterface $hasher)
    {
        $this->clientRegistry = $clientRegistry;
        $this->entityManager = $entityManager;
        $this->router = $router;
        $this->hasher = $hasher;
    }

    public function supports(Request $request): ?bool
    {
        // continue ONLY if the current ROUTE matches the check ROUTE
        return $request->attributes->get('_route') === 'connect_telegram_check';
    }


    public function authenticate(Request $request): Passport
    {
        $query = $request->query;

        $telegramId = $request->query->get('id');

        return new SelfValidatingPassport(
            new UserBadge($telegramId, function () use ($telegramId, $query)
            {

                $userByTelegramId = $this->entityManager->getRepository(User::class)->findOneBy(['telegramId' => $telegramId]);
                if ($userByTelegramId) return $userByTelegramId;


                $user = new User();
                $user->setTelegramId($telegramId);
                $user->setFirstName($query->get('first_name'));
                $user->setLastName($query->get('last_name'));
                $user->setTelegramUsername($query->get('username'));

                $randomString = ByteString::fromRandom(32)->toString();
                $password = $this->hasher->hashPassword($user, $randomString);

                $user->setPassword($password);
                $this->entityManager->persist($user);
                $this->entityManager->flush();

                return $user;

            })
        );
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {

        $targetUrl = $this->router->generate('homepage');

        return new RedirectResponse($targetUrl);

    }

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

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

    /**
     * Called when authentication is needed, but it's not sent.
     * This redirects to the 'login'.
     */
    public function start(Request $request, AuthenticationException $authException = null): Response
    {
        return new RedirectResponse(
            '/login', // might be the site, where users choose their oauth provider
            Response::HTTP_TEMPORARY_REDIRECT
        );
    }


}

TelegramController:

<?php

namespace App\Controller;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class TelegramController extends AbstractController
{
    /**
     * Link to this controller to start the "connect" process
     *
     * @Route("/connect/telegram", name="connect_telegram_start", options={"expose" : true})
     */
    public function connectAction(ClientRegistry $clientRegistry, HttpClientInterface $client)
    {

        return $clientRegistry
            ->getClient('telegram')
            ->redirect([],[
                'bot_id' => $this->getParameter('telegram_bot_id'),
                'origin' => 'https://myapp.io',
                'return_to' => 'https://myapp.io/register',
            ]);

//        return new JsonResponse($url->getTargetUrl());
    }

    /**
     * @Route("/connect/telegram/check", name="connect_telegram_check")
     */
    public function connectCheckAction(Request $request, ClientRegistry $clientRegistry, EntityManagerInterface $entityManager)
    {
    }
}

knp config file:

knpu_oauth2_client:
    clients:
        # configure your clients as described here: https://github.com/knpuniversity/oauth2-client-bundle#configuration
        facebook_main:
            # this will be one of the supported types
            type: facebook
            client_id: '%env(OAUTH_FACEBOOK_ID)%'
            client_secret: '%env(FACEBOOK_SECRET)%'
            # the route that you're redirected to after
            # see the controller example below
            redirect_route: connect_facebook_check
            redirect_params: {}
            graph_api_version: v2.12
        google:
            # this will be one of the supported types
            type: google
            client_id: '%env(GOOGLE_CLIENT_ID)%'
            client_secret: '%env(GOOGLE_SECRET)%'
            # the route that you're redirected to after
            # see the controller example below
            redirect_route: connect_google_check
            access_type: 'offline'
            redirect_params: {}
        telegram:
            type: generic
            provider_class: App\Client\TelegramAuthProvider
            client_id: null
            client_secret: '%env(TELEGRAM_TOKEN)%'
            redirect_route: null
            redirect_params: {}

Why am I not redirected back to my web app for authentication?

@bocharsky-bw
Copy link
Member

The problem is that when a user accepts the login on the telegram auth url the window closes and I am not redirected back to my app for authentication.

Did you create any app / bot in the telegram for this? I suppose you need to check some configs there, probably you missed setting a redirect URL there? It's usually should be set up there.

Otherwise, please, check Telegram docs about how to redirect user back to your website.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants