Cómo implementar el inicio de sesión con Github en tu aplicación Symfony 5

Cómo implementar el inicio de sesión con Github en tu aplicación Symfony 5

Hoy en día, la mayoría de las aplicaciones deberían permitir a sus usuarios iniciar sesión en su aplicación utilizando una red social. Básicamente, porque te permite leer datos de un usuario desde otra aplicación, sin obligarlo a completar todos los datos requeridos del formulario de registro en tu aplicación ya que simplemente puede iniciar sesión en la red social deseada y autorizar el acceso. Proporciona el flujo de trabajo de autorización para cualquier aplicación web, de escritorio y móvil, ya que en el lado del servidor, la aplicación web utiliza un código de autorización y no interactúa con las credenciales del usuario.

En este artículo, te explicaré cómo implementar fácilmente la función de "Iniciar sesión con Github" en tu aplicación Symfony 5.

Requisitos

Debe tener una entidad de usuario en su proyecto. Esta entidad debe tener las 2 siguientes propiedades adicionales con sus respectivos getters y setters:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
// DON'T forget the following use statement!!!
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity
 * @UniqueEntity("email")
 */
class User implements UserInterface
{
    /**
     * @var string|null
     *
     * @ORM\Column(name="github_id", type="string", length=255, nullable=true, options={"default"="NULL"})
     */
    private $githubId;

    /**
     * @var string|null
     *
     * @ORM\Column(name="github_access_token", type="string", length=255, nullable=true, options={"default"="NULL"})
     */
    private $githubAccessToken;
}

1. Registra tu aplicación OAuth en Github

Inicia sesión en Github y accede al área de Desarrolladores a través de este enlace. También puedes acceder a este menú a través de la configuración de tu perfil, luego busca la configuración del desarrollador y desplázate hasta Aplicaciones OAuth . Haz clic en crear nueva aplicación y completa el formulario:

Register OAuth App on Github

La información que debes proporcionar es simple y lo único que debes tener en cuenta es la URL de devolución de llamada de autorización, que será el punto final de la URL que crearemos más adelante en este tutorial. En este caso, la dirección será /connect/check/github, sin embargo podría ser diferente si decides cambiarla a medida que avanzamos en este tutorial.

Después de registrar la aplicación, obtendrás un ID de cliente de Github y podrás generar un nuevo secreto de cliente de Github. Esas credenciales serán necesarias en la configuración de esta implementación en pasos posteriores.

2. Instale KnpUOAuth2ClientBundle

Para facilitar la integración de cualquier servidor OAuth2, KnpUOAuth2ClientBundle es la desicion correcta para hacer el trabajo en Symfony 5. Este paquete te proporcionará:

  • Permitir autenticación / inicio de sesión "social"
  • Tipo de funcionalidad "Conectar con Facebook"
  • Obtener claves de acceso a través de OAuth2 para usarlas con una API
  • Realización de autenticación OAuth2 con Guard . 

Este paquete se integra con league / oauth2-client . Para instalar esta biblioteca en tu proyecto Symfony, ejecuta el siguiente comando: 

composer require knpuniversity/oauth2-client-bundle

Para obtener más información sobre este paquete, visita el repositorio oficial en Github aquí .

3. Instale la biblioteca cliente de Github

El cliente oauth2 maneja la autenticación OAuth2, sin embargo, necesita saber con qué cliente debería funcionar. En este caso vamos a trabajar con la librería Github Client que se puede instalar fácilmente con el siguiente comando:

composer require league/oauth2-github

Para obtener más información sobre este cliente, visite el repositorio oficial en Github aquí .

4. Configure los parámetros Github ID y Github Secret

Después de instalar ambas bibliotecas, finalmente podemos comenzar con la implementación de la nuestra. Lo primero que debes hacer es definir el ID de cliente de Github y el secreto de tu aplicación OAuth creada en el primer paso. Puede sdefinirlos en tu archivo .env de esta manera:

# project/.env
###> Social Authentication ###
OAUTH_GITHUB_CLIENT_ID="your-github-oauth-app-id"
OAUTH_GITHUB_CLIENT_SECRET="your-unique-secret-client-secret"
###< Social Authentication ###

Esto será necesario en el archivo de configuración knpu_oauth2_client.yaml. Luego, registra un nuevo cliente OAuth con una ID personalizada que usará la biblioteca del cliente Github de esta manera:

# app/config/packages/knpu_oauth2_client.yaml
knpu_oauth2_client:
    clients:
        # El ID de este cliente OAuth será "github_main", puedes usar un ID personalizado
        # como"my_github"
        github_main:
            # debe ser "github" - ¡activa ese tipo!
            type: github
            # agregue y establezca estas variables de entorno en sus archivos .env
            client_id: '%env(OAUTH_GITHUB_CLIENT_ID)%'
            client_secret: '%env(OAUTH_GITHUB_CLIENT_SECRET)%'
            # un nombre de ruta que creará, en este caso la ruta con id "connect_github_check"
            # que crearemos en el controlador de autenticación
            redirect_route: connect_github_check
            redirect_params: {}
            # si se debe verificar el "estado" de OAuth2: el valor predeterminado es verdadero
            # use_state: true

Las propiedades importantes del cliente en este caso son:

  • github_main: este es el nombre del cliente que quieres declarar, puede ser el que quieras para identificarlo. Esto será necesario más adelante en la clase Authenticator que crearemos en los siguientes pasos.
  • type: especifica el tipo de cliente OAuth que se usará, en este caso será GitHub como es el id del cliente que instalamos en el paso 3.
  • redirect_route: esta será la ID de ruta de Symfony a la que será redirigido después de ir a Github.

5. Creación de un autenticador de Github

Ahora necesita crear la clase Authenticator, puedes usar el siguiente código como base para el autenticador. Hay algunas cosas a tener en cuenta y que necesitas modificar en esta clase:

  1. En el método supports: este es el id de la ruta CHECK que crearemos en el siguiente paso. Si decides cambiarlo más tarde, asegúrate de actualizarlo aquí para que el autenticador reaccione cuando sea necesario.
  2. En el método getGithubClient: este es el id del cliente definido previamente en el archivo knpu_oauth2_client.yaml. Si decides cambiarlo, no olvides actualizarlo también en el autenticador.
  3. En el método onAuthenticationSuccess: Esto debe cambiarse, ya que será la ruta a la que se redirigirá al usuario después de que la autenticación se realice correctamente. Como las rutas en un proyecto son personales, necesitas definir el ID de la ruta donde el usuario debe ser redirigido de acuerdo con las rutas que existen en tu proyecto.
  4. En elmétodo  getUser: debes revisar este método, ya que manejará lo que sucede cuando el cliente OAuth tiene éxito y obtiene la información del usuario en Github. Puedes utilizar la información para encontrar un usuario ya existente o crear uno nuevo según los campos de tu entidad de usuario.

La clase se verá así:

<?php
// app/src/Security/GithubAuthenticator.php
namespace App\Security;

// Tu entidad de usuario
use App\Entity\User;

use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use KnpU\OAuth2ClientBundle\Client\Provider\FacebookClient;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
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;
use Symfony\Component\HttpFoundation\RedirectResponse;

class GithubAuthenticator extends SocialAuthenticator
{
    private $clientRegistry;
    private $em;
    private $router;

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

    public function supports(Request $request)
    {
        // continuar SÓLO si la RUTA actual coincide con la ruta de verificación
        return $request->attributes->get('_route') === 'connect_github_check';
    }

    public function getCredentials(Request $request)
    {
        // este método solo se llama si supports () devuelve verdadero

        // Para Symfony inferior a 3.4, el método de soporte debe llamarse manualmente aquí:
        // if (!$this->supports($request)) {
        //     return null;
        // }

        return $this->fetchAccessToken($this->getGithubClient());
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        /** @var League\OAuth2\Client\Provider\ResourceOwnerInterface $githubUser */
        $githubUser = $this->getGithubClient()->fetchUserFromToken($credentials);
        
        // Nota: normalmente, el correo electrónico siempre es nulo si el usuario no tiene una dirección de correo electrónico pública configurada en Github
        // https://stackoverflow.com/questions/35373995/github-user-email-is-null-despite-useremail-scope
        $email = $githubUser->getEmail();

        // 1. ¿Han iniciado sesión con Github antes? ¡Fácil!
        $existingUser = $this->em->getRepository(User::class)->findOneBy(['githubId' => $githubUser->getId()]);
        
        // Esta matriz contiene la información de la API del usuario de Github autenticado
        $githubData = $githubUser->toArray();
        
        if ($existingUser) {
            return $existingUser;
        }
        
        // Si tu aplicación requiere un correo electrónico para conservar una entidad de usuario, debe encontrar uno en caso de que el usuario de Github no proporcione uno.
        if(!$email){
            $email = "{$githubUser->getId()}@githuboauth.com";
        }

        // Si el usuario existe, úsalo.
        if ($existingUser) {
            $user = $existingUser;
        
        // De lo contrario, crea uno nuevo (?)
        } else {
            // 2) ¿Tenemos un usuario coincidente por correo electrónico? Si es así, no deberíamos crear un nuevo usuario, podemos usar la misma entidad y establecer el id de github
            $user = $this->em->getRepository(User::class)->findOneBy(['email' => $email]);

            // Si aún no existe, debe crear uno nuevo.
            // Aquí viene la lógica personalizada de la creación de su usuario.
            if (!$user) {

                // e.g. Este es solo un ejemplo, depende de su entidad de usuario, así que asegúrese de modificar esto
                /** @var User $user */
                $user = new User();
                $now = new \DateTime();
                $user->setLastLogin($now);
                $user->setRegisteredAt($now);
                $user->setName($githubData["name"]);
                $user->setPassword(null);
                $user->setEmail($email);
            }
        }

        // Finalmente, siempre debería existir un objeto $ user
        // Así que actualice el GithubId y persista la info si no existe
        $user->setGithubId($githubUser->getId());
        $this->em->persist($user);
        $this->em->flush();

        return $user;
    }

    /**
     * @return GithubClient
     */
    private function getGithubClient()
    {
        // "github_main" es la clave utilizada en config / packages / knpu_oauth2_client.yaml
        return $this->clientRegistry->getClient('github_main');
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        // cambia "app_homepage" a alguna ruta en su aplicación
        $targetUrl = $this->router->generate('pages_homepage');

        return new RedirectResponse($targetUrl);
    
        // o, en caso de éxito, deje que la solicitud continúe siendo manejada por el controlador
        //return null;
    }

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

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

    /**
     * Se llama cuando se necesita autenticación, pero no se envía.
     * Esto redirige al 'inicio de sesión'.
     */
    public function start(Request $request, AuthenticationException $authException = null)
    {
        return new RedirectResponse(
            '/connect/', // podría ser el sitio, donde los usuarios eligen su proveedor de oauth
            Response::HTTP_TEMPORARY_REDIRECT
        );
    }
}

Después de crear la clase de autenticador, debes registrarla. Esto se puede hacer en el archivo security.yaml asi:

Nota: como probablemente tengas el autenticador predeterminado de tu aplicación, asegúrese de especificarlo como punto_entrada de su aplicación para evitar que aparezca una excepción .

# app/config/packages/security.yaml
security:
    firewalls:
        main:
            guard:
                # Definir el punto de entrada predeterminado
                entry_point: App\Security\LoginFormAuthenticator
                authenticators:
                    - App\Security\LoginFormAuthenticator
                    # Agrega el nuevo Autenticador de Github
                    - App\Security\GithubAuthenticator

6. Crea un controlador de autenticación

¡Casi terminamos! Debes crear ahora las 2 rutas que debemos enviar al usuario para que se autentique con Github y a la que será redirigido cuando la autenticación sea exitosa. Este paso es personal y puedes manejarlo de la manera que quieras, en mi caso, como puede que necesite implementar otras redes sociales para iniciar sesión, manejaré esas rutas en un solo controlador. Si ese no es tu caso, puede crear un controlador como GithubController para manejar estas rutas. Cree el controlador con los 2 métodos mencionados asi:

<?php
// app/src/Controller/SocialAuthenticationController.php
namespace App\Controller;

use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class SocialAuthenticationController extends AbstractController
{
    /**
     * Enlace a este controlador para iniciar el proceso de "conexión"
     * @param ClientRegistry $clientRegistry
     *
     * @Route("/connect/github", name="connect_github_start")
     *
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function connectGithubAction(ClientRegistry $clientRegistry)
    {
        return $clientRegistry
            // ID used in config/packages/knpu_oauth2_client.yaml
            ->getClient('github_main')
            // Request access to scopes
            // https://github.com/thephpleague/oauth2-github
            ->redirect([
                'user:email'
            ])
        ;
    }

    /**
     * Después de ir a Github, se le redirige de nuevo aquí.
     * porque esta es la "redirect_route" que configuró
     * en config/packages/knpu_oauth2_client.yaml
     *
     * @param Request $request
     * @param ClientRegistry $clientRegistry
     *
     * @Route("/connect/github/check", name="connect_github_check")
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function connectGithubCheckAction(Request $request, ClientRegistry $clientRegistry)
    {
        return $this->redirectToRoute('pages_homepage');
    }
}

En este controlador, creamos las 2 rutas que se requieren en la clase del autenticador y el archivo de configuración del knpu_oauth2_client. En la ruta connect_github_start es donde comenzará todo, ya que el usuario será redirigido al sitio web de Github para iniciar sesión y autorizar los ámbitos que usaremos para nuestra aplicación, en este caso, la información personal del usuario y el correo electrónico. Cuando el usuario acepta, será redirigido a la ruta connect_github_check y el flujo de trabajo de tu aplicación continuará como de costumbre con un nuevo usuario autenticado desde Github.

7. Probando

Todo lo que queda es crear un botón o enlace que redirija al usuario a Github para autenticarse así en algunas de tus vistas:

<a href="{{ path('connect_github_start') }}">
    Iniciar sesion con Github
</a>

Que te diviertas ❤️!

Esto podria interesarte

Conviertete en un programador más sociable