Biblioteca cliente de Facebook (league/oauth2-facebook), necesitaremos configurar un cliente para cada servidor OAuth con el que queramos autenticarnos.
También debemos haber registrado nuestra aplicación en Facebook (https://developers.facebook.com) y obtener el APP_ID y la clave (SECRET).
Configuración
Como estamos utilizando las recetas de autoconfiguración de Symfony 4 y Flex no necesitaremos hacer prácticamente nada, salvo incorporar los datos particulares de nuestra aplicación:
Añade al fichero .env y .env.local los parámetros (en el segundo fichero con los datos reales obtenidos de Facebook):
Y dentro del fichero config/packages/knpu_oauth2_client.yaml que se habrá creado en la instalación del bundle incluye los datos del cliente:
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
knpu_oauth2_client:
clients:
# configure your clients as described here: https://github.com/knpuniversity/oauth2-client-bundle#configuration
# the key "facebook" can be anything, it
# will create a service: "knpu.oauth2.client.facebook"
facebook:
type: facebook
client_id: '%env(OAUTH_FACEBOOK_ID)%'
client_secret: '%env(OAUTH_FACEBOOK_SECRET)%'
# the route that you're redirected to after
redirect_route: oauth_facebook_check
redirect_params: {}
graph_api_version: v4.0
knpu_oauth2_client:
clients:
# configure your clients as described here: https://github.com/knpuniversity/oauth2-client-bundle#configuration
# the key "facebook" can be anything, it
# will create a service: "knpu.oauth2.client.facebook"
facebook:
type: facebook
client_id: '%env(OAUTH_FACEBOOK_ID)%'
client_secret: '%env(OAUTH_FACEBOOK_SECRET)%'
# the route that you're redirected to after
redirect_route: oauth_facebook_check
redirect_params: {}
graph_api_version: v4.0
knpu_oauth2_client:
clients:
# configure your clients as described here: https://github.com/knpuniversity/oauth2-client-bundle#configuration
# the key "facebook" can be anything, it
# will create a service: "knpu.oauth2.client.facebook"
facebook:
type: facebook
client_id: '%env(OAUTH_FACEBOOK_ID)%'
client_secret: '%env(OAUTH_FACEBOOK_SECRET)%'
# the route that you're redirected to after
redirect_route: oauth_facebook_check
redirect_params: {}
graph_api_version: v4.0
Creando las operaciones de autenticación
Debemos crear dos rutas y sus correspondientes métodos en el controlador de seguridad (o en otro específico para la autenticación en Facebook).
return$this->redirectToRoute('home'); // or any other route
}
// [...]
}
<?php // src/Controller/SecurityController.php
namespace App\Controller;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class SecurityController extends AbstractController
{
// [...]
public function facebookConnect(ClientRegistry $clientRegistry)
{
return $clientRegistry
->getClient('facebook')
->redirect([
'public_profile', 'email' // the scopes you want to access
])
;
}
public function facebookConnectionCheck(Request $request, ClientRegistry $clientRegistry)
{
return $this->redirectToRoute('home'); // or any other route
}
// [...]
}
<?php // src/Controller/SecurityController.php
namespace App\Controller;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class SecurityController extends AbstractController
{
// [...]
public function facebookConnect(ClientRegistry $clientRegistry)
{
return $clientRegistry
->getClient('facebook')
->redirect([
'public_profile', 'email' // the scopes you want to access
])
;
}
public function facebookConnectionCheck(Request $request, ClientRegistry $clientRegistry)
{
return $this->redirectToRoute('home'); // or any other route
}
// [...]
}
Autenticación en Symfony
Y ahora el último paso. Una vez realizada la autenticación en el servidor OAuth y recuperada la información del usuario y el token de acceso tenemos que efectivamente autenticar en nuestra aplicación.
$this->router->generate('app_login'), // might be the site, where users choose their oauth provider
Response::HTTP_TEMPORARY_REDIRECT
);
}
}
<?php // src/Security/FacebookAuthenticator.php
namespace App\Security;
use App\Entity\User; //User entity
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use KnpU\OAuth2ClientBundle\Client\Provider\FacebookClient;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use League\OAuth2\Client\Provider\FacebookUser;
use Symfony\Component\HttpFoundation\RedirectResponse;
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;
class FacebookAuthenticator 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)
{
// continue ONLY if the current ROUTE matches the check ROUTE
return $request->attributes->get('_route') === 'oauth_facebook_check';
}
public function getCredentials(Request $request)
{
// this method is only called if supports() returns true
return $this->fetchAccessToken($this->getFacebookClient());
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
/** @var FacebookUser $facebookUser */
$facebookUser = $this->getFacebookClient()->fetchUserFromToken($credentials);
$email = $facebookUser->getEmail();
// 1) have they logged in with Facebook before? Easy!
$existingUser = $this->em->getRepository(User::class)
->findOneBy([
'origin' => User::ORIGIN_FACEBOOK,
'externalId' => $facebookUser->getId()
]);
if ($existingUser) {
return $existingUser;
}
$user = new User();
$user->setEmail($facebookUser->getEmail());
$user->setPassword('NO_PASSWORD');
$user->setOrigin(User::ORIGIN_FACEBOOK);
$user->setExternalId($facebookUser->getId());
//TODO: Fill profile
$this->em->persist($user);
$this->em->flush();
return $user;
}
/**
* @return FacebookClient
*/
private function getFacebookClient()
{
// "facebook" is the key used in config/packages/knpu_oauth2_client.yaml
return $this->clientRegistry->getClient('facebook');
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return new RedirectResponse($this->router->generate('home'));
// or, on success, let the request continue to be handled by the controller
//return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$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)
{
return new RedirectResponse(
$this->router->generate('app_login'), // might be the site, where users choose their oauth provider
Response::HTTP_TEMPORARY_REDIRECT
);
}
}
<?php // src/Security/FacebookAuthenticator.php
namespace App\Security;
use App\Entity\User; //User entity
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use KnpU\OAuth2ClientBundle\Client\Provider\FacebookClient;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use League\OAuth2\Client\Provider\FacebookUser;
use Symfony\Component\HttpFoundation\RedirectResponse;
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;
class FacebookAuthenticator 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)
{
// continue ONLY if the current ROUTE matches the check ROUTE
return $request->attributes->get('_route') === 'oauth_facebook_check';
}
public function getCredentials(Request $request)
{
// this method is only called if supports() returns true
return $this->fetchAccessToken($this->getFacebookClient());
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
/** @var FacebookUser $facebookUser */
$facebookUser = $this->getFacebookClient()->fetchUserFromToken($credentials);
$email = $facebookUser->getEmail();
// 1) have they logged in with Facebook before? Easy!
$existingUser = $this->em->getRepository(User::class)
->findOneBy([
'origin' => User::ORIGIN_FACEBOOK,
'externalId' => $facebookUser->getId()
]);
if ($existingUser) {
return $existingUser;
}
$user = new User();
$user->setEmail($facebookUser->getEmail());
$user->setPassword('NO_PASSWORD');
$user->setOrigin(User::ORIGIN_FACEBOOK);
$user->setExternalId($facebookUser->getId());
//TODO: Fill profile
$this->em->persist($user);
$this->em->flush();
return $user;
}
/**
* @return FacebookClient
*/
private function getFacebookClient()
{
// "facebook" is the key used in config/packages/knpu_oauth2_client.yaml
return $this->clientRegistry->getClient('facebook');
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return new RedirectResponse($this->router->generate('home'));
// or, on success, let the request continue to be handled by the controller
//return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$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)
{
return new RedirectResponse(
$this->router->generate('app_login'), // might be the site, where users choose their oauth provider
Response::HTTP_TEMPORARY_REDIRECT
);
}
}
Y tenemos que decirle en la configuración que lo utilice. Ahora bien, si seguimos teniendo la autenticación por usuario/contraseña existente tendremos dos clases para la misma funcionalidad, múltiples autenticadores y tenemos obligatoriamente que especificar cuál se utilizará por defecto: el entry point.
Comentarios recientes