Tuesday, 16 January 2018

Implementing authentication security token in symfony

Symfony provide a guard authentication bundle in which with API keys we handle the authentication responses and user credentials.you want to apply token based authentication system in Symfony and authenticate users through an API key. In this article i will show you how can you work with Guard and authenticate users via API tokens.

symfony authentication security token



1. Create User class & Provider class.

Create a user entity class which implements UserInterface and user provider. For authentication it depends on UserProvider when user submit the information the provider values are checked and further verification of password takes place.

User Entity : 

<?php

namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;

/**

* @ORM\Entity

* @ORM\Table(name="user")

*/

class User implements UserInterface

{

   /**

    * @ORM\Id

    * @ORM\GeneratedValue

    * @ORM\Column(type="integer")

    */

   private $id;

   /**

    * @ORM\Column(type="string", unique=true)

    */

   private $username;

   /**

    * @ORM\Column(type="string", unique=true)

    */

   private $email;

   /**

    * @ORM\Column(type="string")

    */

   private $password;

   /**

    * @ORM\Column(type="json_array")

    */

   private $roles = array();

   /**

    * @ORM\Column(type="string", unique=true)

    */

   private $apiToken;

   public function getId()

   {

       return $this->id;

   }

   public function getUsername()

   {

       return $this->username;

   }

   public function setUsername($username)

   {

       $this->username = $username;

   }

   public function getEmail()

   {

       return $this->email;

   }

   public function setEmail($email)

   {

       $this->email = $email;

   }

   public function getPassword()

   {

       return $this->password;

   }

   public function setPassword($password)

   {

       $this->password = $password;

   }

   /**

    * Returns the roles or permissions granted to the user for security.

    */

   public function getRoles()

   {
       $roles = $this->roles;

       // guarantees that a user always has at least one role for security

       if (empty($roles)) {

           $roles[] = 'ROLE_USER';

       }

       return array_unique($roles);

   }

   public function setRoles($roles)

   {
       $this->roles = $roles;

   }

   /**

    * Returns the salt that was originally used to encode the password.

    */

   public function getSalt()

   {
       return;

   }

   /**

    * Removes sensitive data from the user.

    */

   public function eraseCredentials()

   {

       // if you had a plainPassword property, you'd nullify it here

       // $this->plainPassword = null;

   }

   /**

    * @param string $apiToken

    */

   public function setApiToken($apiToken)

   {

       $this->apiToken = $apiToken;

   }

}


2. You need to register the provider in security.yml

providers:

       api_key_user_provider:

           entity:

               class: AppBundle:User

               property: apikey


3. Now you need to install Guard Bundle.

Guard provides different layers of authentication.It first introduced in symfony 2.8 and after that it’s now become a part of symfony core. With Guard authentication process is handled by only one class: an Authenticator.

You can also install the Guard with composer :

composer require symfony/security-guard

4. Create an Authenticator Class.

Create a new authenticator class which implements GuardAuthenticatorInterface extends the AbstractGuardAuthenticator.This class will read the api token in header request and find the respective user.

This class will read the api token in header request and find the respective user.

Create a new file: (src/AppBundle/Security/TokenAuthenticator.php)

<?php
namespace AppBundle\Security;



use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;


class TokenAuthenticator extends AbstractGuardAuthenticator

{

   /**
    * Called on every request. Return whatever credentials you want to
    * be passed to getUser(). Returning null will cause this authenticator
    * to be skipped.
    */

   public function getCredentials(Request $request)

   {
       if (!$token = $request->headers->get('X-AUTH-TOKEN')) {

           // No token?

           $token = null;

       }

       // What you return here will be passed to getUser() as $credentials

       return array(

           'token' => $token,

       );

   }

   public function getUser($credentials, UserProviderInterface $userProvider)

   {
       $apikey = $credentials['token'];

       if (null === $apikey) {

           return;

       }
       // if null, authentication will fail

       // if a User object, checkCredentials() is called

       return $userProvider->loadUserByUsername($apikey);

   }

   public function checkCredentials($credentials, UserInterface $user)

   {
       // check credentials - e.g. make sure the password is valid

       // no credential check is needed in this case



       // return true to cause authentication success

       return true;

   }

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

   {

       // on success, let the request continue

       return null;

   }

   public function onAuthenticationFailure(Request $request, AuthenticationException $exception)

   {

       $data = array(

           'message' => strtr($exception->getMessageKey(), $exception->getMessageData())

           // or to translate this message

           // $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())

       );

       return new JsonResponse($data, Response::HTTP_FORBIDDEN);

   }

   /**

    * Called when authentication is needed, but it's not sent

    */

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

   {
       $data = array(

           // you might translate this message

           'message' => 'Authentication Required'

       );

       return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);

   }

   public function supportsRememberMe()

   {
       return false;

   }

}

You can read the whole method of guard here

5. Configuring The Authenticator.

To configure the authenticator I need to update the firewall:

security:
   # ...
   firewalls:
       # ...
       main:
           anonymous: ~
           logout: ~
           guard:
               authenticators:
                   - AppBundle\Security\TokenAuthenticator



After that register the authenticator as a service in service.yml:

services:
   api_key_authenticator:
       class: AppBundle\Security\TokenAuthenticator
       arguments: ["@router"]


Everthing is done now check the response in url.

curl -H "X-AUTH-TOKEN: username" http://yourappurl.com

Featured post

How to create dynamic schema in mongodb

Sometime what we need in our application is to create mongodb schema for collection and some of our collection schema are same but names are...

Popular Posts