Aprende a configurar el bundle HWIOAuthBundle correctamente con FOSUserBundle en tu proyecto Symfony 3 para permitir el inicio de sesión social.

Cómo configurar y usar HWIOAuthBundle con FOSUserBundle (Social Login) en Symfony 3

Mucha gente sufre hoy en día la famosa "Fatiga de la contraseña", incluso probablemente usted. Los visitantes de un sitio web siempre buscan una forma sencilla de hacer las cosas, y cuando hablamos de iniciar sesión en sitios, el inicio de sesión social es la mejor opción porque los usuarios pueden usar sus cuentas sociales existentes como Facebook, Google+, Twitter, etc. Esta ventaja puede impulsar las conversiones en su sitio web mejorando su experiencia de usuario. Muchos sitios web informan que sus consumidores objetivo prefieren usar Social Login en lugar de crear una nueva cuenta en su sitio y eso por una buena razón. Para un proyecto Symfony, HWIOAuthBundle es la mejor y más famosa solución para lograr esta tarea.

En este artículo, aprenderá cómo permitir que su usuario inicie sesión en su aplicación utilizando una red social. Aunque puedes configurar de la misma forma cualquier red social, te explicaremos en este caso con Github, Facebook, Google Plus y Stack Exchange.

Nota

Puede seguir el mismo proceso si desea agregar otras redes sociales como Twitter, etc. O si no necesita una red social del ejemplo, simplemente omítala.

Requisitos

Antes de continuar, deberá crear en la fos_usertabla de su base de datos 2 columnas de tipo cadena (Varchar 255) para cada red social (propietario de recursos) que desee agregar. Estableceremos este paso como un requisito, porque usted es quien decide cómo agregar los campos a la base de datos. Algunos desarrolladores manejan la base de datos con algún administrador como PHPMyAdmin u otro sigue el método de Symfony modificando el user.orm.ymlarchivo y luego construyendo la base de datos usando php bin/console doctrine:schema:update --force.

Los 2 campos para cada propietario de recursos siguen la siguiente nomenclatura: <social-network-name>_id<social-network-name>_access_token. Por ejemplo, con nuestras 4 redes sociales mencionadas tendríamos 8 nuevas columnas en la tabla fos_user especificamente:

github_id
github_access_token

facebook
facebook_access_token

googleplus_id
googleplus_access_token

stackexchange_id
stackexchange_access_token

Una vez que existan las columnas, obviamente necesitará agregar los captadores y definidores del campo en la clase Usuario de su aplicación :

/** @ORM\Column(name="github_id", type="string", length=255, nullable=true) */
protected $github_id;

/** @ORM\Column(name="github_access_token", type="string", length=255, nullable=true) */
protected $github_access_token;

/** @ORM\Column(name="facebook_id", type="string", length=255, nullable=true) */
protected $facebook_id;

/** @ORM\Column(name="facebook_access_token", type="string", length=255, nullable=true) */
protected $facebook_access_token;

/** @ORM\Column(name="googleplus_id", type="string", length=255, nullable=true) */
protected $googleplus_id;

/** @ORM\Column(name="googleplus_access_token", type="string", length=255, nullable=true) */
protected $googleplus_access_token;

/** @ORM\Column(name="stackexchange_id", type="string", length=255, nullable=true) */
protected $stackexchange_id;

/** @ORM\Column(name="stackexchange_access_token", type="string", length=255, nullable=true) */
protected $stackexchange_access_token;

public function setGithubId($githubId) {
    $this->github_id = $githubId;

    return $this;
}

public function getGithubId() {
    return $this->github_id;
}

public function setGithubAccessToken($githubAccessToken) {
    $this->github_access_token = $githubAccessToken;

    return $this;
}

public function getGithubAccessToken() {
    return $this->github_access_token;
}

public function setFacebookId($facebookID) {
    $this->facebook_id = $facebookID;

    return $this;
}

public function getFacebookId() {
    return $this->facebook_id;
}

public function setFacebookAccessToken($facebookAccessToken) {
    $this->facebook_access_token = $facebookAccessToken;

    return $this;
}

public function getFacebookAccessToken() {
    return $this->facebook_access_token;
}

public function setGoogleplusId($googlePlusId) {
    $this->googleplus_id = $googlePlusId;

    return $this;
}

public function getGoogleplusId() {
    return $this->googleplus_id;
}

public function setGoogleplusAccessToken($googleplusAccessToken) {
    $this->googleplus_access_token = $googleplusAccessToken;

    return $this;
}

public function getGoogleplusAccessToken() {
    return $this->googleplus_access_token;
}


public function setStackexchangeId($stackExchangeId) {
    $this->stackexchange_id = $stackExchangeId;

    return $this;
}

public function getStackexchangeId() {
    return $this->stackexchange_id;
}

public function setStackexchangeAccessToken($stackExchangeAccessToken) {
    $this->stackexchange_access_token = $stackExchangeAccessToken;

    return $this;
}

public function getStackexchangeAccessToken() {
    return $this->stackexchange_access_token;
}

Si configuras todo en orden, podrás recuperar esas propiedades del objeto Usuario y podrá proceder a configurar el HWIOAuthBundle.

Nota

Recuerde que debe borrar el caché y cerrar la sesión del usuario actual cuando modifica la clase de usuario; de lo contrario, cuando agregue nuevos campos, no se actualizarán hasta que el usuario vuelva a iniciar sesión.

1. Instale y habilite HWIOAuthBundle

Lo primero que debe hacer es instalar HWIOAuthBundle con composer usando el siguiente comando:

composer require hwi/oauth-bundle

Alternativamente, puede modificar manualmente su composer.json y configurar el paquete como una dependencia:

{
    "require": {
        "hwi/oauth-bundle": "^0.5.3",
    }
}

Y finalmente instálelo usando composer install. Una vez que finalice la instalación del paquete, no olvides habilitarlo en el AppKernel.phparchivo de tu aplicación Symfony:

<?php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = [
            // .. 
            new HWI\Bundle\OAuthBundle\HWIOAuthBundle(),
            // .. 
        ];

        // .. 
    }
}

2. Cree cuentas de desarrollador en las redes sociales

El punto más importante para permitir que tu usuario pueda iniciar sesión en tu aplicación con una Red Social, es que el servicio de terceros (Red Social) te permita hacerlo también. Para ello, la mayoría de los servicios requieren que su aplicación esté registrada para poder gestionar solicitudes de permisos, etc.

Por ejemplo, para Github puede registrar su aplicación aquí , para Facebook aquí , para Stack Exchange aquí y para Google Plus aquí . Una vez que su aplicación esté registrada, le proporcionarán los tokens OAuth necesarios que le permitirán crear una solicitud a sus servidores. El administrador de Github se ve así:

OAuth Manager Github

Nota

Además de la configuración de su aplicación personalizada, deberá proporcionar la URL de devolución de llamada de autorización que definiremos en el paso 4. Así que cree su aplicación con esta opción vacía y no olvide actualizarla una vez que haya terminado con este tutorial.

En este caso, la URL seguirá un patrón como  https://yourwebsite/connect/check-<resource-owner-name>., Sin embargo, puede cambiar la URL de devolución de llamada mientras sigue este tutorial.

Casi todos los propietarios de recursos tienen al menos 2 parámetros, a saber, client_idsecret. En este ejemplo, vamos a utilizar las 4 redes sociales mencionadas anteriormente, por lo que necesitaremos registrar los siguientes parámetros en su app/config/config.ymlarchivo. También se recomienda almacenar sus tokens entre comillas dobles, por ejemplo "your-token-here":

# app/config/config.yml
parameters:
    # Para Github necesitarás el client_id y el secreto
    github_client_id: <replace-with-your-github-client-id>
    github_secret: <replace-with-your-github-secret>
    
    # Para Facebook necesitarás el client_id y el secreto
    facebook_client_id: <replace-with-your-facebook-client-id>
    facebook_secret: <replace-with-your-facebook-secret>
    
    # Para Google+, necesitará el client_id y el secreto
    googleplus_client_id: <replace-with-your-googleplus-client-id>
    googleplus_secret: <replace-with-your-googleplus-secret>
    
    # Para Stack Exchange, necesitará el client_id, el secreto y la clave
    stackexchange_client_id: <replace-with-your-stackexchange-client-id>
    stackexchange_secret: <replace-with-your-stackexchange-secret>
    stackexchange_key: <replace-with-your-stackexchange-key>

Los propietarios de los recursos utilizarán estos tokens en el siguiente paso.

3. Configurar HWIO y propietario de recursos

Ahora que tiene los derechos para crear solicitudes a los servidores de redes sociales deseados, debe crear los propietarios de recursos locales de cada red social. Vaya al config.ymlarchivo de su aplicación Symfony y establezca la configuración ( hwi_oauth) de HWIOAuthBundle. Aquí es donde registra nuevas redes sociales con sus respectivos tokens de acceso:

# app/config/config.yml
hwi_oauth:
    # Defina qué firewalls se usarán para oauth
    # Por lo general, es solo el principal, pero puede agregarlo si tiene uno personalizado
    firewall_names: ["main"]
    fosub:
        username_iterations: 30
        # Definir en qué columnas de la tabla fos_user se almacenarán
        # el token de acceso de cada resource_owner
        properties:
            github: github_id
            facebook: facebook_id
            googleplus: googleplus_id
            stackexchange: stackexchange_id
    # Defina los resource_owners que su usuario puede usar para iniciar sesión en su aplicación
    # Tenga en cuenta que client_id y client_secret y los valores clave son parámetros de Symfony
    # almacenado también en el config.yml del paso anterior!
    resource_owners:
        github:
            type:           github
            client_id:      "%github_client_id%"
            client_secret:  "%github_secret%"
            scope: 'user:email,public_repo'
        facebook:
            type:           facebook
            client_id:      "%facebook_client_id%"
            client_secret:  "%facebook_secret%"
            infos_url:     "https://graph.facebook.com/me?fields=id,name,email"
        googleplus:
            type:           google
            client_id:      "%googleplus_client_id%"
            client_secret:  "%googleplus_secret%"
            scope:  "email profile"
        stackexchange:
            type:           stack_exchange
            client_id:      "%stackexchange_client_id%"
            client_secret:  "%stackexchange_secret%"
            options:
                key: "%stackexchange_key%"

Para obtener más información sobre cómo funciona la configuración de cada propietario de recursos, consulte los documentos oficiales de HWIOAuthBundle aquí .

4. Configurar rutas de propietarios de recursos

Tu usuario deberá acceder a una ruta que identifique con qué red social quiere iniciar sesión. Vaya al app/config/security.ymlarchivo de su aplicación y agregue la oauthconfiguración para su firewall:

# app/config/security.yml
security:
    # Modificar firewalls
    firewalls:
        # Establezca la configuración en su firewall
        main:
            oauth:
                # Declare las URL de devolución de llamada de OAuth para cada propietario de recurso
                # Ellos se agregarán en el archivo routing.yml demasiado tarde.
                resource_owners:
                    github: "/connect/check-github"
                    facebook: "/connect/check-facebook"
                    googleplus: "/connect/check-googleplus"
                    stackexchange: "/connect/check-stackexchange"
                ## Proporcione la ruta de inicio de sesión original de su aplicación (fosuserroute)
                ## y la ruta de falla cuando falla la autenticación.
                login_path:     /user/login
                failure_path:   /user/login
                # Inyecte un servicio que se creará en el paso # 6
                oauth_user_provider:
                    service: app.fos_user.oauth_provider

A continuación, proceda a agregar las rutas de HWIOBundle y los propietarios de recursos a su app/config/routing.ymlarchivo:

Nota

Como se mencionó en el paso 2, en el administrador de cuentas de OAuth de aplicaciones como Facebook, Github, etc., deberá proporcionar la URL de devolución de llamada de OAuth . Puede utilizar las rutas de este paso para proporcionar una ruta a los servicios de terceros, por ejemplo http://yoursite.com/connect/check-facebook.

# app/config/routing.yml

hwi_oauth_redirect:
    resource: "@HWIOAuthBundle/Resources/config/routing/redirect.xml"
    prefix:   /connect
    
hwi_oauth_connect:
    resource: "@HWIOAuthBundle/Resources/config/routing/connect.xml"
    prefix:   /connect

hwi_oauth_login:
    resource: "@HWIOAuthBundle/Resources/config/routing/login.xml"
    prefix:   /login
    
github_login:
    path: /connect/check-github
    
facebook_login:
    path: /connect/check-facebook
    
googleplus_login:
    path: /connect/check-googleplus

stackexchange_login:
    path: /connect/check-stackexchange

De esta forma, si el usuario quiere iniciar sesión en su aplicación utilizando su cuenta de Facebook, solo necesitará redirigirlo a la ruta http://yoursite.com/connect/check-facebook

5. Crear administrador de inicio de sesión y registro

De alguna manera su aplicación necesita recibir información de la Red Social para registrarse o iniciar sesión. Esa es la función de la siguiente clase FOSUBUserProvider. Por defecto funciona sin necesidad de modificaciones. Una vez que el usuario accede a la ruta de verificación, la loadUserByOAuthUserResponsefunción entra en acción. Si el usuario no está registrado con la cuenta de la red social en su aplicación, creará una nueva fila en la tabla fos_user de forma predeterminada con un nombre de usuario aleatorio, por ejemplo, 12345_<name-of-social-network>y lo firmará automáticamente. Si el usuario ya existe, lo buscará por el campo <social-network>_idy enviará el token de acceso para recuperar la información.

Eres libre de modificar la clase para establecer los campos que necesitas, establecer el nombre de usuario que necesitas, etc. En este caso, almacenamos la clase en la carpeta Entity del AppBundle, pero puedes guardarla donde quieras:

<?php

// Cambie el espacio de nombres según su proyecto.
namespace AppBundle\Entity;

use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider as BaseClass;
use Symfony\Component\Security\Core\User\UserInterface;


// Source: https://gist.github.com/danvbe/4476697

class FOSUBUserProvider extends BaseClass {

    public function connect(UserInterface $user, UserResponseInterface $response) {
        $property = $this->getProperty($response);
        
        $username = $response->getUsername();
        
        // Al conectarse, recupere el token de acceso y la identificación de usuario
        $service = $response->getResourceOwner()->getName();
        
        $setter = 'set' . ucfirst($service);
        $setter_id = $setter . 'Id';
        $setter_token = $setter . 'AccessToken';
        
        // Desconectar usuarios previamente conectados
        if (null !== $previousUser = $this->userManager->findUserBy(array($property => $username))) {
            $previousUser->$setter_id(null);
            $previousUser->$setter_token(null);
            $this->userManager->updateUser($previousUser);
        }
        
        // Conectar usando el usuario actual
        $user->$setter_id($username);
        $user->$setter_token($response->getAccessToken());
        $this->userManager->updateUser($user);
    }

    public function loadUserByOAuthUserResponse(UserResponseInterface $response) {
        $data = $response->getResponse();
        $username = $response->getUsername();
        $email = $response->getEmail() ? $response->getEmail() : $username;
        $user = $this->userManager->findUserBy(array($this->getProperty($response) => $username));
        
        // Si el usuario es nuevo
        if (null === $user) {
            $service = $response->getResourceOwner()->getName();
            $setter = 'set' . ucfirst($service);
            $setter_id = $setter . 'Id';
            $setter_token = $setter . 'AccessToken';
            // Crear nuevo usuario aquí
            $user = $this->userManager->createUser();
            $user->$setter_id($username);
            $user->$setter_token($response->getAccessToken());
            
            // He configurado todos los datos solicitados con el nombre de usuario del usuario.
            // modificar aquí con datos relevantes
            $user->setUsername($this->generateRandomUsername($username, $response->getResourceOwner()->getName()));
            $user->setEmail($email);
            $user->setPassword($username);
            $user->setEnabled(true);
            $this->userManager->updateUser($user);
            return $user;
        }
        
        // Si el usuario existe, use HWIOAuth
        $user = parent::loadUserByOAuthUserResponse($response);
        
        $serviceName = $response->getResourceOwner()->getName();
        
        $setter = 'set' . ucfirst($serviceName) . 'AccessToken';
        
        // Actualizar el token de acceso
        $user->$setter($response->getAccessToken());
        
        return $user;
    }
    
    /**
     * Genera un nombre de usuario aleatorio con el dado
     * e.g 12345_github, 12345_facebook
     * 
     * @param string $username
     * @param type $serviceName
     * @return type
     */
    private function generateRandomUsername($username, $serviceName){
        if(!$username){
            $username = "user". uniqid((rand()), true) . $serviceName;
        }
        
        return $username. "_" . $serviceName;
    }
}

6. Cree el servicio fos_user.oauth_provider

En el security.ymlhemos definido la  oauth_user_provideropción con el  app.fos_user.oauth_providerservicio, que hasta ahora no existe, por lo que es necesario crearlo. El servicio devuelve la clase FOSUBUserProvider y como argumentos el administrador de usuarios de FOSUserBundle y los Propietarios de recursos creados en el paso 3:

# app/config/services.yml
services:
    app.fos_user.oauth_provider:
        # Cambie la clase según la ubicación de la clase FOSUBUserProvider
        class: AppBundle\Entity\FOSUBUserProvider
        arguments:
            # Inyectar como primer argumento el user_manager de FOSUserBundle
            user_manager: "@fos_user.user_manager"
            # Un objeto / matriz con las redes sociales registradas de config.yml
            user_response:
                github: github_id
                facebook: facebook_id
                googleplus: googleplus_id 
                stackexchange: stackexchange_id

7. ¡Pruébelo!

Si todo estaba configurado correctamente (y siguiendo la configuración por defecto) podrá acceder (iniciar sesión o registrarse) a través de una Red Social accediendo a las siguientes rutas:

<!-- Elimine app_dev.php de la URL si no está en modo DEV -->

<a href="/app_dev.php/connect/github">
    Login with Github 
</a>

<a href="/app_dev.php/connect/stackexchange">
    Login with Stack Exchange 
</a>

<a href="/app_dev.php/connect/facebook">
    Login with Facebook 
</a>

<a href="/app_dev.php/connect/googleplus">
    Login with Google+ 
</a>

Aquí, el usuario será redirigido a la página de concesión de la red social que le pregunta si realmente desea usar su cuenta para iniciar sesión en otra aplicación. Vale la pena decir que debe borrar el caché y cerrar sesión en la cuenta actual para evitar cualquier error.

Que te diviertas ❤️!


Ingeniero de Software Senior en EPAM Anywhere. Interesado en la programación desde los 14 años, Carlos es un programador autodidacta, fundador y autor de la mayoría de los artículos de Our Code World.

Conviertete en un programador más sociable

Patrocinadores