Social Login with Facebook

Everybody wants their site to have a "Login with Facebook", "Login with GitHub" or "Login with InstaFaceTweet". Let's give the people what they want!

Setting this up will take some coding, but the result will be easy to understand and simple to extend. Let's do it!

Tip

Watch our OAuth2 in 8 Steps tutorial first to get handle on how OAuth works.

The Flow

Social authentication uses OAuth - usually the authorization code grant type. That just means that we have a flow that looks like this:

TODO - IMAGE HERE

  1. Your user clicks on a link to "Login with Facebook". This takes them to a Symfony controller on your site (e.g. /connect/facebook)

  2. That controller redirects to Facebook, where they grant your application access

  3. Facebook redirects back to your site (e.g. /connect/facebook-check) with a ?code= query parameter

  4. We make an API request back to Facebook to exchange this code for an access token. Then, immediately, we use this access token to make another API request to Facebook to fetch the user's information - like their email address. If we find an existing user, we can log the user in. If not, we might choose to create a User in the database, or have the user complete a "registration" form.

Installing Guard

Read the short Installation chapter to make sure you've got the bundle installed and enabled.

Creating your Facebook Application

To follow along, you'll need to create a Facebook Application at https://developers.facebook.com/. You can name it anything, but for the "Site URL", make sure it uses whatever domain you're using. In my case, I'm using http://localhost:8000/. I'll probably create a different application for my production site.

When you're done, this will give you "App ID" and "App Secret". Keep those handy!

Installing the OAuth Client Libraries

To help with the OAuth heavy-lifting, we'll use a nice oauth2-client library, and its oauth2-facebook helper library. Get these installed:

composer require league/oauth2-client:~1.0@dev league/oauth2-facebook

Setting up the Facebook Provider Service

The oauth2-facebook library lets us create a nice Facebook object that makes doing OAuth with Facebook a breeze. We'll need this object in several places, so let's register it as a service:

30 lines app/config/services.yml
... lines 1 - 5
services:
... lines 7 - 14
app.facebook_provider:
class: League\OAuth2\Client\Provider\Facebook
arguments:
-
clientId: %facebook_app_id%
clientSecret: %facebook_app_secret%
graphApiVersion: v2.3
redirectUri: "@=service('router').generate('connect_facebook_check', {}, true)"
... lines 23 - 30

This references two new parameters - facebook_app_id and facebook_app_secret. Add these to your parameters.yml (and parameters.yml.dist) file with your Facebook application's "App ID" and "App Secret" values:

16 lines app/config/parameters.yml
... line 1
parameters:
... lines 3 - 12
facebook_app_id: XXXX
facebook_app_secret: XXXX
... lines 15 - 16

Tip

If you haven't seen the odd "@=service('router')..." expression syntax before, we have a blog post on it: Symfony Service Expressions: Do things you thought Impossible.

Creating the FacebookConnectController

Don't think about security yet! Instead, look at the flow above. We'll need a controller with two actions: one that simply redirects to Facebook for authorization (/connect/facebook) and another that handles what happens when Facebook redirects back to us (/connect/facebook-check):

37 lines src/AppBundle/Controller/FacebookConnectController.php
... lines 1 - 2
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class FacebookConnectController extends Controller
{
/**
* @Route("/connect/facebook", name="connect_facebook")
*/
public function connectFacebookAction(Request $request)
{
// redirect to Facebook
$facebookOAuthProvider = $this->get('app.facebook_provider');
$url = $facebookOAuthProvider->getAuthorizationUrl([
// these are actually the default scopes
'scopes' => ['public_profile', 'email'],
]);
return $this->redirect($url);
}
/**
* @Route("/connect/facebook-check", name="connect_facebook_check")
*/
public function connectFacebookActionCheck()
{
// will not be reached!
}
}

The first URL - /connect/facebook - uses the Facebook provider service from the oauth2-client library that we just setup. Its job is simple: redirect to Facebook to start the authorization process. In a second, we'll add a "Login with Facebook" link that will point here.

The second URL - /connect/facebook-check - will be the URL that Facebook will redirect back to after. But notice it doesn't do anything - and it never will. Another layer (the authenticator) will intercept things and handle all the logic.

For good measure, let's create a "Login with Facebook" on our normal login page:

27 lines app/Resources/views/security/login.html.twig
... lines 1 - 23
<a href="{{ path('connect_facebook') }}">Login with Facebook</a>
... lines 25 - 27

Creating an Authenticator

With Guard, the whole authentication process - fetching the access token, getting the user information, redirecting after success, etc - is handled in a single class called an "Authenticator". Your authenticator can be as crazy as you want, as long as it implements KnpU\Guard\GuardAuthenticatorInterface.

Most of the time, you can extend a convenience class called AbstractGuardAuthenticator. Create a new FacebookAuthenticator class, make it extend this class, and add all the necessary methods:

49 lines src/AppBundle/Security/FacebookAuthenticator.php
... lines 1 - 2
namespace AppBundle\Security;
use KnpU\Guard\AbstractGuardAuthenticator;
use Symfony\Component\HttpFoundation\Request;
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 FacebookAuthenticator extends AbstractGuardAuthenticator
{
public function getCredentials(Request $request)
{
// todo
}
public function getUser($authorizationCode, UserProviderInterface $userProvider)
{
// todo
}
public function checkCredentials($credentials, UserInterface $user)
{
// todo
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
// todo
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// todo
}
public function supportsRememberMe()
{
// todo
}
public function start(Request $request, AuthenticationException $authException = null)
{
// todo
}
}

Your mission: fill in each method. We'll get to that in a second.

To fill in those methods, we're going to need some services. To keep this tutorial simple, let's pass the entire container into our authenticator:

162 lines src/AppBundle/Security/FacebookAuthenticator.php
... lines 1 - 10
use Symfony\Component\DependencyInjection\ContainerInterface;
... lines 12 - 22
class FacebookAuthenticator extends AbstractGuardAuthenticator
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
... lines 31 - 160
}

Tip

For seasoned-Symfony devs, you can of course inject only the services you need.

Registering your Authenticator

Before filling in the methods, let's tell Symfony about our fancy new authenticator. First, register it as a service:

30 lines app/config/services.yml
... lines 1 - 5
services:
... lines 7 - 23
app.facebook_authenticator:
class: AppBundle\Security\FacebookAuthenticator
arguments:
# you can also inject the services you need individually
# except for the router, as it causes a circular reference problem
- @service_container

Next, update your security.yml file to use the new service:

28 lines app/config/security.yml
security:
... lines 2 - 13
firewalls:
... lines 15 - 18
main:
... line 20
knpu_guard:
authenticators:
... lines 23 - 24
- app.facebook_authenticator
... lines 26 - 28

Your firewall (called main here) can look however you want, as long as it has a knpu_guard section under it with an authenticators key that includes the service name that you setup a second ago (app.facebook_authenticator in my example).

Filling in the Authenticator Methods

Congratulations! Your authenticator is now being used by Symfony. Here's the flow so far:

1) The user clicks "Login with Facebook"; 2) Our connectFacebookAction redirects the user to Facebook; 3) After authorizing our app, Facebook redirects back to /connect/facebook-connect; 4) The getCredentials() method on FacebookAuthenticator is called and we start working our magic!

getCredentials()

162 lines src/AppBundle/Security/FacebookAuthenticator.php
... lines 1 - 22
class FacebookAuthenticator extends AbstractGuardAuthenticator
{
... lines 25 - 31
public function getCredentials(Request $request)
{
if ($request->getPathInfo() != '/connect/facebook-check') {
// skip authentication unless we're on this URL!
return null;
}
if ($code = $request->query->get('code')) {
return $code;
}
// no code! Something went wrong. Quite probably the user denied our app access
// you could read the error, error_code, error_description, error_reason query params
// http://localhost:8000/connect/facebook-check?error=access_denied&error_code=200&error_description=Permissions+error&error_reason=user_denied&state=S2fKgHJSZSJM0Qs2fhKL6USZP50KSBHc#_=_
throw CustomAuthenticationException::createWithSafeMessage(
'There was an error getting access from Facebook. Please try again.'
);
}
... lines 50 - 160
}

If the user approves our application, Facebook will redirect back to /connect/facebook-connect with a ?code=ABC123 query parameter. That's called the "authorization code".

The getCredentials() method is called on every single request and its job is simple: grab this "authorization code" and return it.

Inside getCredentials(), here are 2 possible paths:

# Conditions Result Next Step
A) Return non-null value Authentication continues getUser()
B) Throw an exception Authentication fails onAuthenticationFailure()
C) Return null Authentication is skipped Nothing!

A) If the URL is /connect/facebook-connect, then we fetch the code query parameter that Facebook is sending us and return it. This will be passed to a few other methods later. In this case - since we returned a non-null value from getCredentials() - the getUser() method is called next.

B) If the URL is /connect/facebook-connect but there is no code query parameter, something went wrong! This probably means the user didn't authorize our app. To fail authentication, you can throw any AuthenticationException. The CustomAuthenticationException is just a cool way to control the message the user sees.

C) If the URL is not /connect/facebook-connect, we return null. In this case, the request continues anonymously - no other methods are called on the authenticator.

getUser()

If getCredentials() returns a non-null value, then getUser() is called next. Its job is simple: return a user (an object implementing UserInterface).

But to do that, there are several steps. Ultimately, there are two possible results:

# Conditions Result Next Step
A) Return a User object Authentication continues checkCredentials()
B) Return null or throw an AuthenticationException Authentication fails Redirect to getLoginUrl()

getUser() Part 1: Get the access token

The $authorizationCode argument is whatever you returned from getCredentials(). Our first job is to talk to the Facebook API and exchange this for an "access token". Fortunately, with the oauth2-client library, this is easy:

162 lines src/AppBundle/Security/FacebookAuthenticator.php
... lines 1 - 50
public function getUser($authorizationCode, UserProviderInterface $userProvider)
{
$facebookProvider = $this->container->get('app.facebook_provider');
try {
// the credentials are really the access token
$accessToken = $facebookProvider->getAccessToken(
'authorization_code',
['code' => $authorizationCode]
);
} catch (IdentityProviderException $e) {
// probably the authorization code has been used already
$response = $e->getResponseBody();
$errorCode = $response['error']['code'];
$message = $response['error']['message'];
throw CustomAuthenticationException::createWithSafeMessage(
'There was an error logging you into Facebook - code '.$errorCode
);
}
... lines 71 - 105
}
... lines 107 - 162

If this fails for some reason, we throw an AuthenticationException (specifically a CustomAuthenticationException to control the message).

getUser() Part 2: Get Facebook User Information

Now that we have a valid access token, we can make reqeusts to the Facebook API on behalf of the user. The most important thing we need is information about the user - like what is their email address?

To get that, use the getResourceOwner() method:

162 lines src/AppBundle/Security/FacebookAuthenticator.php
... lines 1 - 50
public function getUser($authorizationCode, UserProviderInterface $userProvider)
{
... lines 53 - 71
/** @var FacebookUser $facebookUser */
$facebookUser = $facebookProvider->getResourceOwner($accessToken);
$email = $facebookUser->getEmail();
... lines 75 - 105
}
... lines 107 - 162

This returns a FacebookUser from the oauth2-facebook library (if you connect to something else like GitHub, it will have a different object). We can use that to get the user's email address.

getUser() Part 3: Fetching/Creating the User

Great! We now know some information about the user, including their email address.

A) If you return some User object (using whatever method you want) - then you'll continue on to checkCredentials().

B If you return null or throw any Symfony\Component\Security\Core\Exception\AuthenticationException, authentication will fail and the user will be redirected back to the login page: see getLoginUrl().

checkCredentials()

If you return a user from getUser(), then checkCredentials() is called next. Your job is simple: check if the username/password combination is valid. If it isn't, throw a BadCredentialsException (or any AuthenticationException):

68 lines src/AppBundle/Security/FormLoginAuthenticator.php
... lines 1 - 8
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
... line 10
use Symfony\Component\Security\Core\User\UserInterface;
... lines 12 - 13
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 16 - 45
public function checkCredentials($credentials, UserInterface $user)
{
$plainPassword = $credentials['password'];
$encoder = $this->container->get('security.password_encoder');
if (!$encoder->isPasswordValid($user, $plainPassword)) {
// throw any AuthenticationException
throw new BadCredentialsException();
}
}
... lines 55 - 66
}

Like before, $credentials is whatever you returned from getCredentials(). And now, the $user argument is what you just returned from getUser(). To check the user, you can use the security.password_encoder, which automatically hashes the plain password based on your security.yml configuration.

Want to do some other custom checks beyond the password? Go crazy! Based on what you do, there are 2 paths:

# Conditions Result Next Step
A) do anything except throwing an AuthenticationException Authentication successful Redirect the user (may involve getDefaultSuccessRedirectUrl())
B) Throw any type of AuthenticationException Authentication fails Redirect to getLoginUrl()

If you don't throw an exception, congratulations! You're user is now authenticated, and will be redirected somewhere...

getDefaultSuccessRedirectUrl()

Your user is now authenticated. Woot! But, where should we redirect them? The AbstractFormLoginAuthenticator class takes care of most of this automatically. If the user originally tried to access a protected page (e.g. /admin) but was redirected to the login page, then they'll now be redirected back to that URL (so, /admin).

But what if the user went to /login directly? In that case, you'll need to decide where they should go. How about the homepage?

68 lines src/AppBundle/Security/FormLoginAuthenticator.php
... lines 1 - 13
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 16 - 61
protected function getDefaultSuccessRedirectUrl()
{
return $this->container->get('router')
->generate('homepage');
}
}

This fetches the router service and redirects to a homepage route (change this to a real route in your application). But note: this method is only called if there isn't some previous page that user should be redirected to.

getLoginUrl()

If authentication fails in getUser() or checkCredentials(), the user will be redirected back to the login page. In this method, you just need to tell Symfony where your login page lives:

68 lines src/AppBundle/Security/FormLoginAuthenticator.php
... lines 1 - 13
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 16 - 55
protected function getLoginUrl()
{
return $this->container->get('router')
->generate('security_login');
}
... lines 61 - 66
}

In our case, the login page route name is security_login.

Installing Guard

Read the short Installation chapter to make sure you've got the bundle installed and enabled.

Creating an Authenticator

With Guard, the whole authentication process - fetching the username/password POST values, validating the password, redirecting after success, etc - is handled in a single class called an "Authenticator". Your authenticator can be as crazy as you want, as long as it implements KnpU\Guard\GuardAuthenticatorInterface.

For login forms, life is easier, thanks to a convenience class called AbstractFormLoginAuthenticator. Create a new FormLoginAuthenticator class, make it extend this class, and add all the missing methods (from the interface and abstract class):

37 lines src/AppBundle/Security/FormLoginAuthenticator.php
... lines 1 - 2
namespace AppBundle\Security;
use KnpU\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
public function getCredentials(Request $request)
{
// TODO: Implement getCredentials() method.
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
// TODO: Implement getUser() method.
}
public function checkCredentials($credentials, UserInterface $user)
{
// TODO: Implement checkCredentials() method.
}
protected function getLoginUrl()
{
// TODO: Implement getLoginUrl() method.
}
protected function getDefaultSuccessRedirectUrl()
{
// TODO: Implement getDefaultSuccessRedirectUrl() method.
}
}

Your mission: fill in each method. We'll get to that in a second.

To fill in those methods, we're going to need some services. To keep this tutorial simple, let's pass the entire container into our authenticator:

45 lines src/AppBundle/Security/FormLoginAuthenticator.php
... lines 1 - 5
use Symfony\Component\DependencyInjection\ContainerInterface;
... lines 7 - 10
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
... lines 19 - 43
}

Tip

For seasoned-Symfony devs, you can of course inject only the services you need.

Registering your Authenticator

Before filling in the methods, let's tell Symfony about our fancy new authenticator. First, register it as a service:

10 lines app/config/services.yml
... lines 1 - 5
services:
app.form_login_authenticator:
class: AppBundle\Security\FormLoginAuthenticator
arguments: ["@service_container"]

Next, update your security.yml file to use the new service:

24 lines app/config/security.yml
security:
... lines 2 - 13
firewalls:
... lines 15 - 18
main:
anonymous: ~
knpu_guard:
authenticators:
- app.form_login_authenticator

Your firewall (called main here) can look however you want, as long as it has a knpu_guard section under it with an authenticators key that includes the service name that you setup a second ago (app.form_login_authenticator in my example).

I've also setup my "user provider" to load my users from the database:

24 lines app/config/security.yml
security:
encoders:
# Our user class and the algorithm we'll use to encode passwords
# http://symfony.com/doc/current/book/security.html#encoding-the-user-s-password
AppBundle\Entity\User: bcrypt
providers:
# Simple example of loading users via Doctrine
# To load users from somewhere else: http://symfony.com/doc/current/cookbook/security/custom_provider.html
database_users:
entity: { class: AppBundle:User, property: username }
... lines 13 - 24

In a minute, you'll see where that's used.

Filling in the Authenticator Methods

Your authenticator is now being used by Symfony. So let's fill in each method:

getCredentials()

68 lines src/AppBundle/Security/FormLoginAuthenticator.php
... lines 1 - 6
use Symfony\Component\HttpFoundation\Request;
... lines 8 - 9
use Symfony\Component\Security\Core\Security;
... lines 11 - 13
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 16 - 22
public function getCredentials(Request $request)
{
if ($request->getPathInfo() != '/login_check') {
return;
}
$username = $request->request->get('_username');
$request->getSession()->set(Security::LAST_USERNAME, $username);
$password = $request->request->get('_password');
return array(
'username' => $username,
'password' => $password
);
}
... lines 38 - 66
}

The getCredentials() method is called on every single request and its job is either to fetch the username/password from the request and return them.

So, from here, there are 2 possibilities:

# Conditions Result Next Step
A) Return non-null value Authentication continues getUser()
B) Return null Authentication is skipped Nothing! But if the user is anonymous and tries to access a secure page, getLoginUrl() will be called

A) If the URL is /login_check (that's the URL that our login form submits to), then we fetch the _username and _password post parameters (these were our form field names) and return them. Whatever you return here will be passed to a few other methods later. In this case - since we returned a non-null value from getCredentials() - the getUser() method is called next.

B) If the URL is not /login_check, we return null. In this case, the request continues anonymously - no other methods are called on the authenticator. If the page the user is accessing requires login, they'll be redirected to the login form: see getLoginUrl().

Tip

We also set a Security::LAST_USERNAME key into the session. This is optional, but it lets you pre-fill the login form with this value (see the SecurityController::loginAction from earlier).

getUser()

If getCredentials() returns a non-null value, then getUser() is called next. Its job is simple: return a user (an object implementing UserInterface):

68 lines src/AppBundle/Security/FormLoginAuthenticator.php
... lines 1 - 11
use Symfony\Component\Security\Core\User\UserProviderInterface;
... line 13
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 16 - 38
public function getUser($credentials, UserProviderInterface $userProvider)
{
$username = $credentials['username'];
return $userProvider->loadUserByUsername($username);
}
... lines 45 - 66
}

The $credentials argument is whatever you returned from getCredentials() and the $userProvider is whatever you've configured in security.yml under the providers key. My provider queries the database and returns the User entity.

There are 2 paths from there:

# Conditions Result Next Step
A) Return a User object Authentication continues checkCredentials()
B) Return null or throw an AuthenticationException Authentication fails Redirect to getLoginUrl()

A) If you return some User object (using whatever method you want) - then you'll continue on to checkCredentials().

B If you return null or throw any Symfony\Component\Security\Core\Exception\AuthenticationException, authentication will fail and the user will be redirected back to the login page: see getLoginUrl().

checkCredentials()

If you return a user from getUser(), then checkCredentials() is called next. Your job is simple: check if the username/password combination is valid. If it isn't, throw a BadCredentialsException (or any AuthenticationException):

68 lines src/AppBundle/Security/FormLoginAuthenticator.php
... lines 1 - 8
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
... line 10
use Symfony\Component\Security\Core\User\UserInterface;
... lines 12 - 13
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 16 - 45
public function checkCredentials($credentials, UserInterface $user)
{
$plainPassword = $credentials['password'];
$encoder = $this->container->get('security.password_encoder');
if (!$encoder->isPasswordValid($user, $plainPassword)) {
// throw any AuthenticationException
throw new BadCredentialsException();
}
}
... lines 55 - 66
}

Like before, $credentials is whatever you returned from getCredentials(). And now, the $user argument is what you just returned from getUser(). To check the user, you can use the security.password_encoder, which automatically hashes the plain password based on your security.yml configuration.

Want to do some other custom checks beyond the password? Go crazy! Based on what you do, there are 2 paths:

# Conditions Result Next Step
A) do anything except throwing an AuthenticationException Authentication successful Redirect the user (may involve getDefaultSuccessRedirectUrl())
B) Throw any type of AuthenticationException Authentication fails Redirect to getLoginUrl()

If you don't throw an exception, congratulations! You're user is now authenticated, and will be redirected somewhere...

getDefaultSuccessRedirectUrl()

Your user is now authenticated. Woot! But, where should we redirect them? The AbstractFormLoginAuthenticator class takes care of most of this automatically. If the user originally tried to access a protected page (e.g. /admin) but was redirected to the login page, then they'll now be redirected back to that URL (so, /admin).

But what if the user went to /login directly? In that case, you'll need to decide where they should go. How about the homepage?

68 lines src/AppBundle/Security/FormLoginAuthenticator.php
... lines 1 - 13
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 16 - 61
protected function getDefaultSuccessRedirectUrl()
{
return $this->container->get('router')
->generate('homepage');
}
}

This fetches the router service and redirects to a homepage route (change this to a real route in your application). But note: this method is only called if there isn't some previous page that user should be redirected to.

getLoginUrl()

If authentication fails in getUser() or checkCredentials(), the user will be redirected back to the login page. In this method, you just need to tell Symfony where your login page lives:

68 lines src/AppBundle/Security/FormLoginAuthenticator.php
... lines 1 - 13
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
... lines 16 - 55
protected function getLoginUrl()
{
return $this->container->get('router')
->generate('security_login');
}
... lines 61 - 66
}

In our case, the login page route name is security_login.

Customize!

Try it out! You should be able to login, see login errors, and control most of the process. So what else can we customize?

Leave a comment!

  • 2016-09-15 Victor Bocharsky

    haha, agreed! :)

  • 2016-09-15 Robert

    Easiest thing in the world - when I know how should I approach this problem. Great :) If I ever meet you, drinks on me! :)

  • 2016-09-14 Victor Bocharsky

    Yep, it should fly as well! Actually, if you use this authenticator only for Facebook - it even easier, just always return true in checkCredentials(). This method will be called only if getUser() returns a User object. So, your challenge here: return user only if user successfully authenticated via Facebook and you found its corresponding User object in the DB. That's it.

  • 2016-09-14 Robert

    Just as I thought. Is this secure? To know how user is logging in - I simply created separate Autheticator files for facebook and for standard form login and in getCredentials methods I just check for URL. According to your post I assume that I could have all-in-one feature in one file. I guess my solution would also fly?

    Thank you so much, you are the best!

    Best Regards,
    Robert

  • 2016-09-14 Victor Bocharsky

    Hey Robert,

    Yes, you're definitely right here! You don't need to check password if user logging in with Facebook. So, you should simply return true in the `checkCredentials()` method IF user logging in with Facebook. How to determine if user is logging in using social network - that's a really good question :). Actually, the first argument of checkCredentials() method is `$credentials`, so I think you could add a boolean field on $credentials array that you return in getCredentials(), i.e. return ['social_network' => true]; And then, just use this in checkCredentials().

    Cheers!

  • 2016-09-14 Robert

    Victor Bocharsky

    Hey Victor,

    Since you've been so kind and helped me every time I have one more question about this part:

    getUser() Part 3: Fetching/Creating the User

    After I get user email from facebook I'm at the point where I can log user into my application. Now there's something like this:

    $plainPassword = $credentials['password']; in checkCredentials - I would like to ask from where exactly should I get a password to check against database. I mean the whole point of logging in via FB is to ommit password if user is already logged into facebook.

    Best Regards,
    Robert

  • 2016-09-08 Robert

    Thanks ;)

    I might dig into that :) As soon as I will finish this part of the project I will have some extra free time ;)

  • 2016-09-08 Victor Bocharsky

    Thanks!

    Haha, well, you even could add a new OAuth2 provider to this bundle which does not exist yet. Probably it should help you to understand full process better. There're a lot of providers actually, which have not added yet. BTW, you can discuss it here first: https://github.com/knpuniversi... . Of course, if you love open source and have a free time :)

    Good luck to you with your project! ;)

  • 2016-09-08 Robert

    Thanks a lot for information! That's awesome!

    For sure I will take a closer look at this bundle. Also I will try to understand how it works so I can try to write my own bundle and.... become Symfony master someday :P Just kidding but it would be nice to understand full process.

    Thanks one more time.
    I hope you will have a nice day.

    Cheers!

  • 2016-09-08 Victor Bocharsky

    Ah, great idea! And yes, I could share some information with you ;)

    First of all, yes, you could be able to write your own Social Networking Login with Guard which implemented directly into Symfony. Actually, you even could do it without Guard Component and without any third party libs (I know it, I did). But with Guard it'll be more obvious.

    I also suggest you to look closer to our oauth bundle: https://github.com/knpuniversi... . It already has Facebook and Google+ providers, and some others you want to include later and also has some example of integration with Guard, check docs here: https://github.com/knpuniversi... . This bundle helps you to do Social Networking Login quicker and in more right way.

    Cheers!

  • 2016-09-08 Robert

    Hey Victor,

    Thanks for quick answer :) Of course I know about The Guard from KNP tutorials and I'm in the middle of writing my own auth system - only website for this week. However I'm new to social networking login and I would like to start implementing this next week. Could you be so kind and correct me if I'm wrong?:

    If I use plain Guard implemented directly into Symfony I will be able to write my own Social Networking Login(I'm aiming for Facebook and Google+ for now) without being forced to use some shady bundles and solutions?

    If you also have any advice that you would like to share I'm more than happy to hear it.

    Cheers!

  • 2016-09-08 Victor Bocharsky

    Hey Robert,

    The Guard now is a part of Symfony since Symfony 2.8. The https://github.com/knpuniversi... was deprecated in favour of https://github.com/symfony/sec... . So the Symfony Guard Component is still good! Even more, you can use it out of the box in the Symfony Framework Standard Edition starting with 2.8. There's a post in Symfony Blog about it: http://symfony.com/blog/new-in... . That's why we are still plan to maintained this course about Guard, but probably we should rewrite it a bit. I can't say when it will be released, we have no any estimates, but it's in our TODO list!

    Cheers!

  • 2016-09-07 Robert

    Hello,

    Is this package still good and will it be maintained?

    Best Regards

  • 2016-05-31 weaverryan

    Hi Ivan!

    I can't quite remember, but I remember thinking it was likely a "bug" in Symfony, possibly caused by Assetic. The problem may have been:

    - router is called, cache is not warmed up yet -> cache warming is started -> Assetic cache warmer is called -> Assetic warms up Twig cache -> Twig cache needs security component -> security component needs your authenticator -> your authenticator needs the router -> circular reference!

    I believe it was a little tricky because it didn't happen all the time (depends on if your cache needs warming). And as I mentioned, if you don't use Assetic, it might not happen.

    Cheers!

  • 2016-05-28 Ivan

    Hello!
    When you registered facebook guard in services.yml, you wrote the next comment:
    "# except for the router, as it causes a circular reference problem".
    Can you, please, describe why circular reference problem takes place?