Guard: Creating a Simple Authentication System for Symfony

Building a custom authentication system for Symfony can get atrocious.
You can get a glimpse of that here.
You have to deal with multiple classes, connect them to each other, and hope for the best.
It is hard to customize and never fun to work with.

Since Symfony 2.8, to simplify the customization of the authentication process, Guard has been introduced.
With Guard, you will not have any struggle building your own authentication system.
It does not redesign the existing authentication system included in Symfony, it plugs itself onto it, making your life easier.
Let’s explain how it works, and how you can use it!

Creating an Authenticator

With Guard, every step of the authentication process is handled by only one class: an Authenticator.
This class will have to implement the provided GuardAuthenticatorInterface.
This interface comes with seven simple methods:

start(Request $request, AuthenticationException $authException = null)

This gets called when the user tries to access a resource that requires authentication, but no authentication information was found in the request.
Its job is to inform the client that he has to send those authentication details.
This method is a bit different from the others, since it comes from AuthenticationEntryPointInterface, which is extended by GuardAuthenticatorInterface.
For example, you could redirect him to the login page:

/**
 * @var \Symfony\Component\Routing\RouterInterface
 */
private $router;

public function start(Request $request, AuthenticationException $authException = null)
{
  $url = $this->router->generate('login');
  return new RedirectResponse($url);
}

getCredentials(Request $request)

This method will get called on every request that requires an authentication.
Its job is to read the authentication information contained in the request, and return it.
You can return what you want! The only purpose what you return is to get used in the getUser() and checkCredentials() methods.
If this method returns null, authentication will fail.
So if the endpoint requires an authentication, the method start() will get called.
If not, the authentication gets skipped, the user is the famous "anon".
If you return a non null value, the method getUser() will get called.
Two examples:

// for an API
public function getCredentials(Request $request)
{
  return $request->headers->get('X-API-TOKEN');
}

// for a form login
public function getCredentials(Request $request)
{
  return array(
    'username' => $request->request->get('_username'),
    'password' => $request->request->get('_password'),
  );
}

getUser($credentials, UserProviderInterface $userProvider)

After you’ve gotten the credentials, you will try to get the User associated with those credentials.
The value of the credentials is passed to getUser() as the $credentials argument.
The job of this method is to return an object implementing UserInterface.
If it does, the next step of the authentication will be called: checkCredentials().
Else, the authentication will fail and the method onAuthenticationFailure() will get called.
An example:

# for an API
public function getUser($credentials, UserProviderInterface $userProvider)
{
  $user = $this->em->getRepository('AppBundle:User')
      ->findOneBy(array('apiToken' => $credentials));

  return $user;
}

checkCredentials($credentials, UserInterface $user)

The job of this method is to check if the credentials of the previously returned User are correct.
This method can do two things.
If it returns true, the user will be authenticated, and the method onAuthenticationSuccess() will be called.
If does not, the authentication fails and the method onAuthenticationFailure() is called.
Even if it works without, throwing any kind of AuthenticationException lets you explicit what went wrong.
An example with a password:

public function checkCredentials($credentials, UserInterface $user)
{
  if ($user->getPassword() === $credentials['password']) {
    return true;
  }

  throw new MyCustomAuthenticationException('The credentials are wrong!');
}

onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)

This method is called when the user is successfully authenticated.
It can return null, in which case the request continues to process as expected, or return a Reponse object, in which case this Response will be transfered to the user.
For example, you can redirect your users to the homepage:

/**
 * @var \Symfony\Component\Routing\RouterInterface
 */
private $router;

public function __construct(RouterInterface $router)
{
  $this->router = $router;
}

# ...

public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
  $url = $this->router->generate('homepage');

  return new RedirectResponse($url);
}

onAuthenticationFailure(Request $request, AuthenticationException $exception)

This method is called when the authentication fails.
Its job is to return a Reponse object that will be sent to the client.
You will know what went wrong in the process with the $exception parameter.
For example, you can return a custom JSON response:

public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
  return new JsonResponse(array('message' => $exception->getMessageKey()), Response::HTTP_FORBIDDEN);
}

supportsRememberMe()

Return true with this method if you want the remember me functionality to be active, false otherwise.
It will still requires the activation of the remember_me under your firewall to work.
A very simple example:

public function supportsRememberMe()
{
  return false;
}

registering your Authenticator

You have built a very nice Authenticator, but how can you use it in your application?
First, register in your security.yml file, under the firewall sections, that you will be using Guard.
For example:

firewalls:
    secured_area:
        anonymous: ~
        logout:
            path:   /logout
              target: /
        guard:
            authenticators:
                - my_custom_authenticator

Then, register your Authenticator as a service, for example in your service.yml:

services:
    my_custom_authenticator:
        class: AppBundle\Security\Authenticator
        arguments: ["@router"]

You can even specify multiple authenticators like so:

guard:
    authenticators:
        - my_custom_authenticator
        - my_facebook_authenticator
    entry_point: my_custom_authenticator

It this case, you will have to tell your application which authenticator will be your entry point, that is which start() method will be called when an anonymous user tries to access a resource requiring authentication.

And that’s it! You can now build your own custom authentication process, and only by implementing very simple methods.
You can easily use Guard to allow an authentication via Facebook, Google+, Github or whatever application you want.
Have a nice time building your Symfony authentication systems!

 


You liked this article? You'd probably be a good match for our ever-growing tech team at Theodo.

Join Us

  • Florent Morselli

    How to authenticate a client in an API context as the client is not a user?

  • cj5

    Why do you only supply partial code examples? It’s really difficult to understand your tutorial

  • not up to mark brief..
    really hard to understand.

    you should write step by step process with file name and location..

  • Tiaan Willemse

    Looks like this post has been abandoned, I used sitepoint’s “Easier Authentication with Guard in Symfony 3” and it worked perfectly. It even has git repo for you to clone if you want to inspect the code.

  • Antoine Kahn

    Hi there,

    My goal here was not to provide a complete step by step tutorial, but rather to explain how Guard works, and how easy it is to make a good use of it.

    If you want such a tutorial, there are excellent ones out there, for example this one : https://knpuniversity.com/screencast/guard

    Cheers !

  • Antoine Kahn

    Hi !

    Sitepoint’s tutorial is indeed very complete. My goal in this article was not to provide a full working example, but to explain how Guard works, and what you can do using it. The code examples I included were there not to work out of the box, but rather to explain how the methods work and how you can use them in your authentication, whatever it is. The logic of your authentication is yours and yours only 😀

    Cheers !

  • Antoine Kahn

    HI there,

    In an API context, you still want to have a User class containing the token of your clients, so they can authenticate on your application.
    The rest is pretty straightforward, and you should be able to write your authentication system with the information contained by this article.

    However, if what you need is a step by step tutorial, you can go there: https://knpuniversity.com/screencast/guard/api-token
    The tutorial is very complete and should fulfill your needs.

    Cheers !

  • VolCh

    Thanks for explain.