Forked from rwitchell/FacebookConnectController.php
Last active
October 19, 2017 15:05
-
-
Save alister/2f01d181924c612d5f31cfae8c450d4c to your computer and use it in GitHub Desktop.
oauth2-client-bundle install example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Learn more about services, parameters and containers at | |
# http://symfony.com/doc/current/book/service_container.html | |
parameters: | |
services: | |
comp_app.security.form_login_authenticator: | |
class: Comp\AppBundle\Security\FormLoginAuthenticator | |
arguments: ["@service_container"] | |
comp_app.security.my_facebook_authenticator: | |
class: Comp\AppBundle\Security\MyFacebookAuthenticator | |
#arguments: [ @service_container ] | |
facebook.handler: | |
class: Comp\AppBundle\Handler\FacebookHandler | |
arguments: ["%facebook_app_id%", "%facebook_app_secret%"] | |
autowire: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Twig Configuration | |
twig: | |
debug: "%kernel.debug%" | |
strict_variables: "%kernel.debug%" | |
form_themes: ['bootstrap_3_horizontal_layout.html.twig'] | |
globals: | |
facebook_app_id: "%facebook_app_id%" | |
facebook: "@facebook.handler" | |
# Doctrine Extension Configuration | |
stof_doctrine_extensions: | |
orm: | |
default: | |
timestampable: true | |
sluggable: true | |
# FOS User Provider Bundle | |
fos_user: | |
db_driver: orm | |
firewall_name: main | |
user_class: Comp\AppBundle\Entity\User | |
service: | |
mailer: fos_user.mailer.twig_swift | |
registration: | |
confirmation: | |
enabled: true | |
registration: | |
confirmation: | |
template: email/registration_confirmation.email.twig | |
from_email: | |
address: | |
sender_name: Project X | |
resetting: | |
email: | |
template: email/password_resetting.email.twig | |
from_email: | |
address: | |
sender_name: Project X | |
knpu_oauth2_client: | |
clients: | |
# will create service: "knpu.oauth2.client.facebook" | |
# an instance of: KnpU\OAuth2ClientBundle\Client\Provider\FacebookClient | |
facebook: | |
type: facebook | |
client_id: "%facebook_app_id%" | |
client_secret: "%facebook_app_secret%" | |
redirect_route: facebook_token_response | |
redirect_params: {} | |
graph_api_version: v2.5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Comp\AppBundle\Controller; | |
use Comp\AppBundle\Entity\User; | |
use Comp\AppBundle\Form\FacebookRegistrationType; | |
use League\OAuth2\Client\Provider\FacebookUser; | |
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | |
use Symfony\Bundle\FrameworkBundle\Controller\Controller; | |
use Symfony\Component\HttpFoundation\Request; | |
/** | |
* Class FacebookConnectController | |
*/ | |
class FacebookConnectController extends Controller | |
{ | |
/** | |
* @Route("/connect/facebook", name="connect_facebook") | |
* @param Request $request | |
* | |
* @return \Symfony\Component\HttpFoundation\RedirectResponse | |
*/ | |
public function connectFacebookAction(Request $request) | |
{ | |
// redirect to Facebook | |
$facebookClient = $this->get('knpu.oauth2.registry') | |
// key used in config.yml | |
->getClient('facebook'); | |
return $facebookClient->redirect( | |
[ | |
'public_profile', | |
'email', | |
//'user_friends', | |
] | |
); | |
} | |
/** | |
* @Route("/connect/facebook-check", name="connect_facebook_check") | |
*/ | |
public function connectFacebookActionCheck() | |
{ | |
// this function won't be reached. | |
$handler = $this->get('facebook.handler'); | |
$handler->getLongLivedAccessToken($handler->getAccessToken()); | |
} | |
/** | |
* @Route("/connect/facebook/registration", name="connect_facebook_registration") | |
* @param Request $request | |
* | |
* @return null|\Symfony\Component\HttpFoundation\Response | |
*/ | |
public function finishRegistration(Request $request) | |
{ | |
/** @var FacebookUser $facebookUser */ | |
$facebookUser = $this->get('comp_app.security.my_facebook_authenticator') | |
->getUserInfoFromSession($request); | |
if (!$facebookUser) { | |
throw $this->createNotFoundException('How did you get here without user information!?'); | |
} | |
$user = new User(); | |
$user->setFacebookId($facebookUser->getId()); | |
$user->setEmail($facebookUser->getEmail()); | |
$form = $this->createForm(new FacebookRegistrationType(), $user); | |
$form->handleRequest($request); | |
if ($form->isValid()) { | |
// encode the password manually | |
$plainPassword = $form['plainPassword']->getData(); | |
$encodedPassword = $this->get('security.password_encoder') | |
->encodePassword($user, $plainPassword); | |
$user->setPassword($encodedPassword); | |
$em = $this->getDoctrine()->getManager(); | |
$em->persist($user); | |
$em->flush(); | |
// remove the session information | |
$request->getSession()->remove('facebook_user'); | |
// log the user in manually | |
$guardHandler = $this->container->get('security.authentication.guard_handler'); | |
return $guardHandler->authenticateUserAndHandleSuccess( | |
$user, | |
$request, | |
$this->container->get('comp_app.security.my_facebook_authenticator'), | |
'main' // the firewall key | |
); | |
} | |
return $this->render( | |
'AppBundle:Security:registration_facebook.html.twig', | |
[ | |
'form' => $form->createView(), | |
] | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Comp\AppBundle\Controller; | |
use Exception; | |
use Facebook\Exceptions\FacebookSDKException; | |
use Comp\AppBundle\Entity\User; | |
use Comp\AppBundle\Handler\FacebookHandler; | |
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; | |
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | |
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; | |
use Symfony\Bundle\FrameworkBundle\Controller\Controller; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | |
/** | |
* Class FacebookController | |
*/ | |
class FacebookController extends Controller | |
{ | |
/** | |
* @param Request $request | |
* | |
* @return \Symfony\Component\HttpFoundation\RedirectResponse | |
* | |
* @Route("/facebook-get-permission", name="facebook_get_permission" ) | |
*/ | |
public function getPermissionAction(Request $request) | |
{ | |
/** @var FacebookHandler $handler */ | |
$handler = $this->get('facebook.handler'); | |
$redirectUrl = $this->generateUrl('facebook_token_response', [], UrlGeneratorInterface::ABSOLUTE_URL); | |
$handler->setPermission('email') | |
->setPermission('user_likes') | |
->setPermission('user_friends') | |
->setPermission('public_profile'); | |
$loginUrl = $handler->getLoginUrl($redirectUrl, $handler->getPermissions()); | |
return $this->redirect($loginUrl); | |
} | |
/** | |
* @return array | |
* @throws FacebookSDKException | |
* | |
* @Route("/facebook-find-friends", name="facebook_show_friends") | |
* @Method("GET") | |
* @Template("AppBundle:Facebook:show_friends.html.twig") | |
*/ | |
public function showFriendsAction() | |
{ | |
return $this->facebookRequest('/me/friends'); | |
} | |
/** | |
* @param string $endpoint | |
* | |
* @return \Facebook\FacebookResponse|null|\Symfony\Component\HttpFoundation\RedirectResponse | |
* @throws FacebookSDKException | |
*/ | |
public function facebookRequest($endpoint) | |
{ | |
$response = null; | |
/** @var FacebookHandler $handler */ | |
$handler = $this->get('facebook.handler'); | |
/** @var User $user */ | |
$user = $this->getUser(); | |
if (!$handler->hasToken($user)) { | |
return $this->redirectToRoute("facebook_get_permission"); | |
} | |
try { | |
$response = $handler->SocialNetworkRequest($endpoint); | |
} catch (FacebookSDKException $e) { | |
if ($e->getMessage() === "You must provide an access token.") { | |
return $this->redirectToRoute('facebook_get_permission'); | |
} | |
if ($e->getMessage() === "Access token not in user.") { | |
return $this->redirectToRoute('facebook_get_permission'); | |
} | |
return $this->redirectToRoute('facebook_get_permission'); | |
//throw new FacebookSDKException($e->getMessage()); | |
} catch (Exception $e) { | |
echo $e; | |
} | |
return [ | |
'facebook' => $response, | |
]; | |
} | |
/** | |
* @param Request $request | |
* | |
* @Route("/facebook-token-response", name="facebook_token_response") | |
* @Method("GET") | |
* @return \Symfony\Component\HttpFoundation\RedirectResponse | |
*/ | |
public function tokenResponseAction(Request $request) | |
{ | |
/** @var FacebookHandler $handler */ | |
$handler = $this->get('facebook.handler'); | |
$accessToken = $handler->getAccessToken(); | |
if (isset($accessToken)) { | |
$accessToken = $handler->getLongLivedAccessToken($accessToken); | |
$user = $this->getUser(); | |
$user->setFacebookAccessToken($accessToken); | |
$userRepo = $this->getDoctrine()->getRepository("AppBundle:User"); | |
$userRepo->save($user); | |
} | |
//return $this->redirectToRoute($request->get('redirectTo')) | |
return $this->redirectToRoute('facebook_show_friends'); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Comp\AppBundle\Handler; | |
use Facebook\Exceptions\FacebookSDKException; | |
use Facebook\Facebook; | |
use Comp\AppBundle\Entity\User; | |
/** | |
* Class FacebookHandler | |
*/ | |
class FacebookHandler extends SocialHandler | |
{ | |
/** | |
* used by friend controller to take URL parameter and | |
* assign correct column in User table. | |
*/ | |
const ENTITY_PROPERTY = 'facebookId'; | |
/** | |
* @var Facebook | |
*/ | |
protected $socialNetwork; | |
/** | |
* FacebookHandler constructor. | |
* | |
* @param string $facebookAppId | |
* @param string $facebookAppSecret | |
*/ | |
public function __construct($facebookAppId, $facebookAppSecret) | |
{ | |
parent::__construct(); | |
$this->socialNetwork = new Facebook( | |
[ | |
'app_id' => $facebookAppId, | |
'app_secret' => $facebookAppSecret, | |
'default_graph_version' => 'v2.5', | |
] | |
); | |
return $this; | |
} | |
/** | |
* @return Facebook | |
*/ | |
public function getSocialNetwork() | |
{ | |
return $this->socialNetwork; | |
} | |
/** | |
* @param string|null $redirectUrl | |
* | |
* @return \Facebook\Authentication\AccessToken|null | |
*/ | |
public function getAccessToken($redirectUrl = null) | |
{ | |
return $this->socialNetwork->getRedirectLoginHelper()->getAccessToken($redirectUrl); | |
} | |
/** | |
* @param string $accessToken | |
* | |
* @return \Facebook\Authentication\AccessToken | |
*/ | |
public function getLongLivedAccessToken($accessToken) | |
{ | |
return $this->socialNetwork->getOAuth2Client()->getLongLivedAccessToken($accessToken); | |
} | |
/** | |
* @param string $redirectUrl | |
* @param array $scope | |
* @param string $separator | |
* | |
* @return string | |
*/ | |
public function getLoginUrl($redirectUrl, array $scope = [], $separator = '&') | |
{ | |
return $this->socialNetwork->getRedirectLoginHelper()->getLoginUrl($redirectUrl, $scope, $separator); | |
} | |
/** | |
* @param User $user | |
* | |
* @return \Facebook\GraphNodes\GraphNode|void | |
*/ | |
public function getProfilePhoto($user) | |
{ | |
return $this->SocialNetworkRequest('/'.$user->getFacebookId().'/picture', $user); | |
return $request->getGraphNode(); | |
} | |
/** | |
* @param string $endpoint | |
* @param User|null $user | |
* | |
* @return \Facebook\FacebookResponse | |
* | |
*/ | |
public function SocialNetworkRequest($endpoint, $user = null) | |
{ | |
if (!$this->hasToken($user)) { | |
return false; | |
} | |
$response = $this->socialNetwork->get($endpoint); | |
return $response; | |
} | |
/** | |
* @param User|null $user | |
* | |
* @return bool | |
*/ | |
public function hasToken(User $user = null) | |
{ | |
// check if default is set | |
if (!empty($this->socialNetwork->getDefaultAccessToken())) { | |
return true; | |
} | |
if (empty($user)) { | |
return false; | |
} | |
// check if we have token on user entity | |
$userToken = $user->getFacebookAccessToken() ?: null; | |
if (empty($userToken)) { | |
return false; | |
} | |
$this->socialNetwork->setDefaultAccessToken($userToken); | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Comp\AppBundle\Security; | |
use Doctrine\ORM\EntityManager; | |
use Comp\AppBundle\Entity\User; | |
use Comp\AppBundle\Handler\FacebookHandler; | |
use KnpU\OAuth2ClientBundle\Client\ClientRegistry; | |
use KnpU\OAuth2ClientBundle\Client\Provider\FacebookClient; | |
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator; | |
use KnpU\OAuth2ClientBundle\Security\Exception\FinishRegistrationException; | |
use League\OAuth2\Client\Provider\Exception\IdentityProviderException; | |
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\UserInterface; | |
use Symfony\Component\Security\Core\User\UserProviderInterface; | |
/** | |
* Class MyFacebookAuthenticator | |
*/ | |
class MyFacebookAuthenticator extends SocialAuthenticator | |
{ | |
/** | |
* @var FacebookClient | |
*/ | |
private $clientRegistry; | |
/** | |
* @var EntityManager | |
*/ | |
private $em; | |
/** | |
* @var RouterInterface | |
*/ | |
private $router; | |
/** | |
* MyFacebookAuthenticator constructor. | |
* | |
* @param ClientRegistry $clientRegistry | |
* @param EntityManager $em | |
* @param RouterInterface $router | |
* | |
*/ | |
public function __construct(ClientRegistry $clientRegistry, EntityManager $em, RouterInterface $router) | |
{ | |
$this->clientRegistry = $clientRegistry; | |
$this->em = $em; | |
$this->router = $router; | |
} | |
/** | |
* Returns a response that directs the user to authenticate. | |
* | |
* This is called when an anonymous request accesses a resource that | |
* requires authentication. The job of this method is to return some | |
* response that "helps" the user start into the authentication process. | |
* | |
* Examples: | |
* A) For a form login, you might redirect to the login page | |
* return new RedirectResponse('/login'); | |
* B) For an API token authentication system, you return a 401 response | |
* return new Response('Auth header required', 401); | |
* | |
* @param Request $request The request that resulted in an AuthenticationException | |
* @param AuthenticationException $authException The exception that started the authentication process | |
* | |
* @return Response | |
*/ | |
public function start(Request $request, AuthenticationException $authException = null) | |
{ | |
// this is never called, as all entries are redirected to form_login | |
} | |
/** | |
* Get the authentication credentials from the request and return them | |
* as any type (e.g. an associate array). If you return null, authentication | |
* will be skipped. | |
* | |
* Whatever value you return here will be passed to getUser() and checkCredentials() | |
* | |
* For example, for a form login, you might: | |
* | |
* return array( | |
* 'username' => $request->request->get('_username'), | |
* 'password' => $request->request->get('_password'), | |
* ); | |
* | |
* Or for an API token that's on a header, you might use: | |
* | |
* return array('api_key' => $request->headers->get('X-API-TOKEN')); | |
* | |
* @param Request $request | |
* | |
* @return mixed|null | |
* @throws IdentityProviderException | |
*/ | |
public function getCredentials(Request $request) | |
{ | |
// Pre 3.4, getCredentials() returns null to skip the authenticator | |
// In 3.4 this will have already been called, and so will succeed here | |
if (!$this->supports($request)) { | |
return; | |
} | |
try { | |
return $this->fetchAccessToken($this->getFacebookClient()); | |
} catch (IdentityProviderException $e) { | |
throw $e; | |
} | |
} | |
/** | |
* Symfony 3.4+ splits getCredentials() and calls supports() first. | |
* | |
* Create (and use in getCredentials) to be ready and 3.x and 3.4+ | |
* compatible. | |
* | |
* @param Request $request | |
* | |
* @return bool | |
*/ | |
public function supports(Request $request) | |
{ | |
return ($request->getPathInfo() === '/connect/facebook-check'); | |
} | |
/** | |
* @return \KnpU\OAuth2ClientBundle\Client\OAuth2Client | |
*/ | |
private function getFacebookClient() | |
{ | |
return $this->clientRegistry->getClient('facebook'); | |
} | |
/** | |
* Return a UserInterface object based on the credentials. | |
* | |
* The *credentials* are the return value from getCredentials() | |
* | |
* You may throw an AuthenticationException if you wish. If you return | |
* null, then a UsernameNotFoundException is thrown for you. | |
* | |
* @param mixed $credentials | |
* @param UserProviderInterface $userProvider | |
* | |
* @throws AuthenticationException | |
* | |
* @return UserInterface|null | |
*/ | |
public function getUser($credentials, UserProviderInterface $userProvider) | |
{ | |
/** @var FacebookUser $facebookUser */ | |
$facebookUser = $this->getFacebookClient()->fetchUserFromToken($credentials); | |
$userRepo = $this->em->getRepository('AppBundle:User'); | |
// 1) Have they logged in with Facebook before? | |
/** @var User $existingUser */ | |
$existingUser = $userRepo->findOneBy([FacebookHandler::ENTITY_PROPERTY => $facebookUser->getId()]); | |
if ($existingUser) { | |
return $existingUser; | |
} | |
// 2) Do we have a matching user by email? | |
$email = $facebookUser->getEmail(); | |
$user = $userRepo->findOneBy(['email' => $email]); | |
if (!$user) { | |
// throw a special exception we created - see onAuthenticaitonFailure | |
throw new FinishRegistrationException($facebookUser); | |
} | |
// Take $user (which can be null if none found above, or have the user without facebook ID) | |
// and attach the facebook ID to the account. | |
$user->setFacebookId($facebookUser->getId()); | |
//TODO: $user->setFacebookAccessToken($this->facebookClient->getAccessToken()); | |
$userRepo->save($user); | |
return $user; | |
} | |
/** | |
* Called when authentication executed, but failed (e.g. wrong username password). | |
* | |
* This should return the Response sent back to the user, like a | |
* RedirectResponse to the login page or a 403 response. | |
* | |
* If you return null, the request will continue, but the user will | |
* not be authenticated. This is probably not what you want to do. | |
* | |
* @param Request $request | |
* @param AuthenticationException $exception | |
* | |
* @return Response|null | |
*/ | |
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) | |
{ | |
if ($exception instanceof FinishRegistrationException) { | |
$this->saveUserInfoToSession($request, $exception); | |
$registrationUrl = $this->router->generate('connect_facebook_registration'); | |
return new RedirectResponse($registrationUrl); | |
} | |
$this->saveAuthenticationErrorToSession($request, $exception); | |
$loginUrl = $this->router->generate('fos_user_security_login'); | |
return new RedirectResponse($loginUrl); | |
} | |
/** | |
* Called when authentication executed and was successful! | |
* | |
* This should return the Response sent back to the user, like a | |
* RedirectResponse to the last page they visited. | |
* | |
* If you return null, the current request will continue, and the user | |
* will be authenticated. This makes sense, for example, with an API. | |
* | |
* @param Request $request | |
* @param TokenInterface $token | |
* @param string $providerKey The provider (i.e. firewall) key | |
* | |
* @return Response|null | |
*/ | |
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) | |
{ | |
if (!$url = $this->getPreviousUrl($request, $providerKey)) { | |
$url = $this->router->generate('comp_app_welcome_myhome'); | |
} | |
// /** @var User $user */ | |
// $user = $request->getUser(); | |
// $user->setFacebookAccessToken($this->clientRegistry->getAccessToken()); | |
return new RedirectResponse($url); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# To get started with security, check out the documentation: | |
# http://symfony.com/doc/current/book/security.html | |
security: | |
encoders: | |
comp\AppBundle\Entity\User: | |
algorithm: bcrypt | |
cost: 4 | |
role_hierarchy: | |
ROLE_ADMIN: ROSE_USER | |
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] | |
# http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers | |
providers: | |
fos_userbundle: | |
id: fos_user.user_provider.username_email | |
firewalls: | |
# disables authentication for assets and the profiler, adapt it according to your needs | |
dev: | |
pattern: ^/(_(profiler|wdt|error)|css|images|js)/ | |
security: false | |
main: | |
pattern: ^/ | |
form_login: | |
provider: fos_userbundle | |
csrf_token_generator: security.csrf.token_manager | |
login_path: /login | |
logout: true | |
anonymous: true | |
guard: | |
authenticators: | |
- comp_app.security.form_login_authenticator | |
- comp_app.security.my_facebook_authenticator | |
# by default, use the start() function from FormLoginAuthenticator | |
entry_point: comp_app.security.form_login_authenticator | |
access_control: | |
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY } | |
- { path: ^/connect/facebook, role: IS_AUTHENTICATED_ANONYMOUSLY } | |
- { path: ^/connect/facebook-check, role: IS_AUTHENTICATED_ANONYMOUSLY } | |
- { path: ^/connect/facebook/registration, role: IS_AUTHENTICATED_ANONYMOUSLY } | |
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY } | |
- { path: ^/resetpw, role: IS_AUTHENTICATED_ANONYMOUSLY } | |
- { path: ^/logout, role: IS_AUTHENTICATED_ANONYMOUSLY } | |
- { path: ^/welcome, role: IS_AUTHENTICATED_ANONYMOUSLY } | |
- { path: ^/, role: [ROLE_USER, ROLE_ADMIN] } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment