vgrdominik/elasticsearchfosoauthserver-bundle

Elasticsearch oauth server management for Symfony2 apis.

v0.1.0 2014-07-13 15:42 UTC

README

About

Elasticsearch oauth server management for Symfony2 apis.

Installation

To install this bundle, you'll need PHP >= 5.3, Symfony >= 2.4, FOSOAuthServerBundle, VGRElasticsearchBundle and VGRElasticsearchHttpFoundationBundle.

Step 1: Download VGRElasticsearchFOSOAuthServerBundle using composer

Tell composer to require VGRElasticsearchFOSOAuthServerBundle by running the command:

$ php composer.phar require "vgrdominik/elasticsearchfosoauthserver-bundle:dev-master"

Step 2: Enable the bundles

<?php
// app/AppKernel.php

public function registerBundles()
{
    $bundles = array(
        // ...

        new VGR\ElasticsearchFOSOAuthServerBundle\VGRElasticsearchFOSOAuthServerBundle()
    );
}

Step 3: Create user environment

This bundle needs to user environment some classes:

  • Provider
  • User

You'll see examples of how your classes should look.

<?php
// src/Acme/ApiBundle/Security/Provider.php

namespace Acme\ApiBundle\Security;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;

use Acme\ApiBundle\Entity\User;
use VGR\ElasticsearchBundle\Model\ElasticsearchEntityRepository;

class Provider implements UserProviderInterface {

    protected $user;
    /**
     * @var ElasticsearchEntityRepository
     */
    protected $userRepository;
    public function __contsruct (UserInterface $user, ElasticsearchEntityRepository $userRepository)
    {
        $this->user = $user;
        $this->userRepository = $userRepository;
    }

    /**
    * Loads the user for the given username.
    *
    * This method must throw UsernameNotFoundException if the user is not
    * found.
    *
    * @throws UsernameNotFoundException if the user is not found
    * @param string $username The username
    *
    * @return UserInterface
    */
    function loadUserByUsername($mail)
    {
        $user = $this->userRepository->findOneBy(array('email'=>$mail));
        if(empty($user)){
            throw new UsernameNotFoundException('Could not find user. Sorry!');
        }
        $this->user = $user;
        return $user;
    }

    /**
    * Refreshes the user for the account interface.
    *
    * It is up to the implementation if it decides to reload the user data
    * from the database, or if it simply merges the passed User into the
    * identity map of an entity manager.
    *
    * @throws UnsupportedUserException if the account is not supported
    * @param UserInterface $user
    *
    * @return UserInterface
    */
    function refreshUser(UserInterface $user)
    {
        return $user;
    }

    /**
    * Whether this provider supports the given user class
    *
    * @param string $class
    *
    * @return Boolean
    */
    function supportsClass($class)
    {
        return $class === 'Acme\ApiBundle\Entity\User';
    }
}
// src/Acme/ApiBundle/Resources/config/services.yml
parameters:
    vgr.fos_oauth_server.entity.user.class: Acme\ApiBundle\Entity\User
    acme.apibundle.security.provider.class: Acme\ApiBundle\Security\Provider

services:
    acme.apibundle.entity.user:
        class:  %vgr.fos_oauth_server.entity.user.class%

    acme.apibundle.security.provider:
        class:  %acme.apibundle.security.provider.class%
        arguments:
            - @acme.apibundle.entity.user
            - @vgr.elasticsearch.manager.elasticsearch
<?php
// src/Acme/ApiBundle/Entity/User.php

namespace Acme\ApiBundle\Entity;

use VGR\ElasticsearchBundle\Model\ElasticsearchEntity;
use Symfony\Component\Security\Core\User\UserInterface;

class User extends ElasticsearchEntity implements UserInterface
{
    /**
     * @var string
     */
    private $email;

    /**
     * @param string $email
     */
    public function setEmail($email)
    {
        $this->email = $email;
    }

    /**
     * @return string
     */
    public function getEmail()
    {
        return $this->email;
    }

    public static function getStructure()
    {
        return '{
                  "'.self::getType().'": {
                    "properties": {
                      "email": {
                        "type": "string",
                        "index": "not_analyzed"
                      }
                    }
                  }
                }';
    }

    public function getId()
    {
        return base64_encode($this->getEmail());
    }

    public static function getType()
    {
        return 'user';
    }

    public static function getIndex()
    {
        return 'users';
    }

    public function hasSubobjects()
    {
        return false;
    }

    public function getSubobjects()
    {
        return array();
    }

    public function getSubobjectsRelatedFields()
    {
        return false;
    }

    public function getRoles()
    {
        return array('ROLE_USER');
    }

    public function getSalt()
    {
        return '1';
    }

    public function getUsername()
    {
        return $this->getEmail();
    }

    public function eraseCredentials()
    {
        return true;
    }

    public function getLists()
    {
        return array();
    }
}

Step 4: Create model classes

This bundle needs to persist some classes to a database:

  • Client (OAuth2 consumers)
  • AccessToken
  • RefreshToken
  • AuthCode

Your first job, then, is to create these classes for your application. These classes can look and act however you want: add any properties or methods you find useful.

These classes have just a few requirements:

  1. They must extend one of the base classes from the bundle
  2. They must have an id field
<?php
// src/Acme/ApiBundle/Entity/Client.php

namespace Acme\ApiBundle\Entity;

use FOS\OAuthServerBundle\Entity\Client as BaseClient;
use VGR\ElasticsearchBundle\Model\ElasticsearchEntityInterface;
use FOS\OAuthServerBundle\Util\Random;

class Client extends BaseClient implements ElasticsearchEntityInterface
{
    public static $_elasticsearchEntityService;

    public function __call($method, $args) {
        try{
            $reflectionMethod = static::getElasticsearchEntityService($this)->getMethod($method);
        }catch(\ReflectionException $e){
            new \Exception('Method not found');
            return false;
        }

        return call_user_func_array(array($reflectionMethod, 'invoke'), array_merge(array(static::getElasticsearchEntityService($this)), $args));
    }

    public static function __callStatic($method, $args) {
        try{
            $reflectionMethod = static::getElasticsearchEntityService(get_called_class())->getMethod($method);
        }catch(\ReflectionException $e){
            new \Exception('Method not found');
            return false;
        }

        return call_user_func_array(array($reflectionMethod, 'invoke'), array_merge(array(static::getElasticsearchEntityService(get_called_class())), $args));
    }

    /**
     * @return ElasticsearchEntityService
     */
    public static function getElasticsearchEntityService($mainObject)
    {
        static::$_elasticsearchEntityService = new \VGR\ElasticsearchBundle\Service\ElasticsearchEntityService($mainObject);

        return static::$_elasticsearchEntityService;
    }

    protected $id;

    public function __construct()
    {
        parent::__construct();
        $this->setId(Random::generateToken());
    }

    public function setId($id)
    {
        $this->id = $id;
    }

    public static function getStructure()
    {
        return '{
                  "'.self::getType().'": {
                    "properties": {
                      "id": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "randomId": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "secret": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "lists" : {
                        "properties" : {
                            "redirectUris" : {"type" : "string"}
                        }
                      }
                    }
                  }
                }';
    }

    public function getLists()
    {
        return array(
            "redirectUris" => $this->getRedirectUris()
        );
    }

    public static function getIndex()
    {
        return 'oauth';
    }

    public static function getType()
    {
        return 'client';
    }

    public function hasSubobjects()
    {
        return false;
    }

    public function getSubobjects()
    {
        return array();
    }

    public function getSubobjectsRelatedFields()
    {
        return false;
    }
}
<?php
// src/Acme/ApiBundle/Entity/AccessToken.php

namespace Acme\ApiBundle\Entity;

use FOS\OAuthServerBundle\Entity\AccessToken as BaseAccessToken;
use VGR\ElasticsearchBundle\Model\ElasticsearchEntityInterface;
use Acme\ApiBundle\Entity\User;
use Symfony\Component\Security\Core\User\UserInterface;
use FOS\OAuthServerBundle\Model\ClientInterface;
use FOS\OAuthServerBundle\Util\Random;

class AccessToken extends BaseAccessToken implements ElasticsearchEntityInterface
{
    public static $_elasticsearchEntityService;

    public function __call($method, $args) {
        try{
            $reflectionMethod = static::getElasticsearchEntityService($this)->getMethod($method);
        }catch(\ReflectionException $e){
            new \Exception('Method not found');
            return false;
        }

        return call_user_func_array(array($reflectionMethod, 'invoke'), array_merge(array(static::getElasticsearchEntityService($this)), $args));
    }

    public static function __callStatic($method, $args) {
        try{
            $reflectionMethod = static::getElasticsearchEntityService(get_called_class())->getMethod($method);
        }catch(\ReflectionException $e){
            new \Exception('Method not found');
            return false;
        }

        return call_user_func_array(array($reflectionMethod, 'invoke'), array_merge(array(static::getElasticsearchEntityService(get_called_class())), $args));
    }

    /**
     * @return ElasticsearchEntityService
     */
    public static function getElasticsearchEntityService($mainObject)
    {
        static::$_elasticsearchEntityService = new \VGR\ElasticsearchBundle\Service\ElasticsearchEntityService($mainObject);

        return static::$_elasticsearchEntityService;
    }

    protected $id;

    protected $idClient;

    protected $idUser;

    public function __construct()
    {
        $this->setId(Random::generateToken());
    }

    public function setId($id)
    {
        $this->id = $id;
    }

    public static function getStructure()
    {
        return '{
                  "'.self::getType().'": {
                    "properties": {
                      "id": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "idClient": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "idUser": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "token": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "expiresAt": {
                        "type": "date"
                      },
                      "scope": {
                        "type": "string",
                        "index": "not_analyzed"
                      }
                    }
                  }
                }';
    }

    public static function getIndex()
    {
        return 'oauth';
    }

    public static function getType()
    {
        return 'access_token';
    }

    public function hasSubobjects()
    {
        return true;
    }

    public function getSubobjects()
    {
        return array($this->getClient(), $this->getUser());
    }

    public function getSubobjectsRelatedFields()
    {
        $user = $this->getUser();
        $client = $this->getClient();

        $stdUser = new \stdClass();
        $stdUser->relatedClass = get_class($user);
        $stdUser->relatedField = 'id';
        $stdClient = new \stdClass();
        $stdClient->relatedClass = get_class($client);
        $stdClient->relatedField = 'id';

        return array($stdUser, $stdClient);
    }

    public function getIdClient()
    {
        return $this->idClient;
    }

    public function setIdClient($idClient)
    {
        $this->idClient = $idClient;
    }

    public function setClient(ClientInterface $client)
    {
        $this->client = $client;
        $this->setIdClient($client->getId());
    }

    public function getIdUser()
    {
        return $this->idUser;
    }

    public function setUser(UserInterface $user)
    {
        $this->user = $user;
        $this->setIdUser($user->getId());
    }

    public function setIdUser($idUser)
    {
        $this->idUser = $idUser;
    }

    public function getLists()
    {
        return array();
    }
}
<?php
// src/Acme/ApiBundle/Entity/RefreshToken.php

namespace Acme\ApiBundle\Entity;

use FOS\OAuthServerBundle\Entity\RefreshToken as BaseRefreshToken;
use VGR\ElasticsearchBundle\Model\ElasticsearchEntityInterface;
use Acme\ApiBundle\Entity\User;
use Symfony\Component\Security\Core\User\UserInterface;
use FOS\OAuthServerBundle\Model\ClientInterface;
use FOS\OAuthServerBundle\Util\Random;

class RefreshToken extends BaseRefreshToken implements ElasticsearchEntityInterface
{
    public static $_elasticsearchEntityService;

    public function __call($method, $args) {
        try{
            $reflectionMethod = static::getElasticsearchEntityService($this)->getMethod($method);
        }catch(\ReflectionException $e){
            new \Exception('Method not found');
            return false;
        }

        return call_user_func_array(array($reflectionMethod, 'invoke'), array_merge(array(static::getElasticsearchEntityService($this)), $args));
    }

    public static function __callStatic($method, $args) {
        try{
            $reflectionMethod = static::getElasticsearchEntityService(get_called_class())->getMethod($method);
        }catch(\ReflectionException $e){
            new \Exception('Method not found');
            return false;
        }

        return call_user_func_array(array($reflectionMethod, 'invoke'), array_merge(array(static::getElasticsearchEntityService(get_called_class())), $args));
    }

    /**
     * @return ElasticsearchEntityService
     */
    public static function getElasticsearchEntityService($mainObject)
    {
        static::$_elasticsearchEntityService = new \VGR\ElasticsearchBundle\Service\ElasticsearchEntityService($mainObject);

        return static::$_elasticsearchEntityService;
    }

    protected $id;

    protected $client;

    protected $user;

    protected $idClient;

    protected $idUser;

    public function __construct()
    {
        $this->setId(Random::generateToken());
    }

    public function setId($id)
    {
        $this->id = $id;
    }

    public static function getStructure()
    {
        return '{
                  "'.self::getType().'": {
                    "properties": {
                      "id": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "idClient": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "idUser": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "token": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "expiresAt": {
                        "type": "date"
                      },
                      "scope": {
                        "type": "string",
                        "index": "not_analyzed"
                      }
                    }
                  }
                }';
    }

    public static function getIndex()
    {
        return 'oauth';
    }

    public static function getType()
    {
        return 'refresh_token';
    }

    public function hasSubobjects()
    {
        return true;
    }

    public function getSubobjects()
    {
        return array($this->getClient(), $this->getUser());
    }

    public function getSubobjectsRelatedFields()
    {
        $user = $this->getUser();
        $client = $this->getClient();

        $stdUser = new \stdClass();
        $stdUser->relatedClass = get_class($user);
        $stdUser->relatedField = 'id';
        $stdClient = new \stdClass();
        $stdClient->relatedClass = get_class($client);
        $stdClient->relatedField = 'id';

        return array($stdUser, $stdClient);
    }

    public function getIdClient()
    {
        return $this->idClient;
    }

    public function setIdClient($idClient)
    {
        $this->idClient = $idClient;
    }

    public function setClient(ClientInterface $client)
    {
        $this->client = $client;
        $this->setIdClient($client->getId());
    }

    public function getIdUser()
    {
        return $this->idUser;
    }

    public function setIdUser($idUser)
    {
        $this->idUser = $idUser;
    }

    public function setUser(UserInterface $user)
    {
        $this->user = $user;
        $this->setIdUser($user->getId());
    }

    public function getLists()
    {
        return array();
    }
}
<?php
// src/Acme/ApiBundle/Entity/AuthCode.php

namespace Acme\ApiBundle\Entity;

use FOS\OAuthServerBundle\Entity\AuthCode as BaseAuthCode;
use VGR\ElasticsearchBundle\Model\ElasticsearchEntityInterface;
use Acme\ApiBundle\Entity\User;
use FOS\OAuthServerBundle\Util\Random;
use Symfony\Component\Security\Core\User\UserInterface;
use FOS\OAuthServerBundle\Model\ClientInterface;

class AuthCode extends BaseAuthCode implements ElasticsearchEntityInterface
{
    public static $_elasticsearchEntityService;

    public function __call($method, $args) {
        try{
            $reflectionMethod = static::getElasticsearchEntityService($this)->getMethod($method);
        }catch(\ReflectionException $e){
            new \Exception('Method not found');
            return false;
        }

        return call_user_func_array(array($reflectionMethod, 'invoke'), array_merge(array(static::getElasticsearchEntityService($this)), $args));
    }

    public static function __callStatic($method, $args) {
        try{
            $reflectionMethod = static::getElasticsearchEntityService(get_called_class())->getMethod($method);
        }catch(\ReflectionException $e){
            new \Exception('Method not found');
            return false;
        }

        return call_user_func_array(array($reflectionMethod, 'invoke'), array_merge(array(static::getElasticsearchEntityService(get_called_class())), $args));
    }

    /**
     * @return ElasticsearchEntityService
     */
    public static function getElasticsearchEntityService($mainObject)
    {
        static::$_elasticsearchEntityService = new \VGR\ElasticsearchBundle\Service\ElasticsearchEntityService($mainObject);

        return static::$_elasticsearchEntityService;
    }

    protected $id;

    protected $client;

    protected $user;

    protected $idClient;

    protected $idUser;

    public function __construct()
    {
        $this->setId(Random::generateToken());
    }

    public function setId($id)
    {
        $this->id = $id;
    }

    public static function getStructure()
    {
        return '{
                  "'.self::getType().'": {
                    "properties": {
                      "id": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "idClient": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "idUser": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "redirectUri": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "token": {
                        "type": "string",
                        "index": "not_analyzed"
                      },
                      "expiresAt": {
                        "type": "date"
                      },
                      "scope": {
                        "type": "string",
                        "index": "not_analyzed"
                      }
                    }
                  }
                }';
    }

    public static function getIndex()
    {
        return 'oauth';
    }

    public static function getType()
    {
        return 'auth_code';
    }

    public function hasSubobjects()
    {
        return true;
    }

    public function getSubobjects()
    {
        return array($this->getClient(), $this->getUser());
    }

    public function getSubobjectsRelatedFields()
    {
        $user = $this->getUser();
        $client = $this->getClient();

        $stdUser = new \stdClass();
        $stdUser->relatedClass = get_class($user);
        $stdUser->relatedField = 'id';
        $stdClient = new \stdClass();
        $stdClient->relatedClass = get_class($client);
        $stdClient->relatedField = 'id';

        return array($stdUser, $stdClient);
    }

    public function getIdClient()
    {
        return $this->idClient;
    }

    public function setIdClient($idClient)
    {
        $this->idClient = $idClient;
    }

    public function setClient(ClientInterface $client)
    {
        $this->client = $client;
        $this->setIdClient($client->getId());
    }

    public function getIdUser()
    {
        return $this->idUser;
    }

    public function setIdUser($idUser)
    {
        $this->idUser = $idUser;
    }

    public function setUser(UserInterface $user)
    {
        $this->user = $user;
        $this->setIdUser($user->getId());
    }

    public function getLists()
    {
        return array();
    }
}

Step 5: Create command to generate elasticsearch configuration

<?php
// src/Acme/ApiBundle/Command/CreateModelCommand.php

namespace Acme\ApiBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use Acme\ApiBundle\Entity\User;
use Acme\ApiBundle\Entity\Client;
use Acme\ApiBundle\Entity\AccessToken;
use Acme\ApiBundle\Entity\RefreshToken;
use Acme\ApiBundle\Entity\AuthCode;

class CreateModelCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this
            ->setName('my_app:model:create')
            ->setDescription('Create model')
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $esm = $this->getContainer()->get('vgr.elasticsearch.manager.elasticsearch');

        $output->writeln('Creating index "'.User::getIndex().'".');
        $esm->createIndex(User::getIndex());
        $output->writeln('Index "'.User::getIndex().'" created.');
        $output->writeln('Creating mapping "'.User::getPath().'".');
        $esm->createMapping(User::getIndex(), User::getType(),  User::getStructure());
        $output->writeln('Mapping "'.User::getPath().'" created.');

        $output->writeln('Creating index "'.Client::getIndex().'".');
        $esm->createIndex(Client::getIndex());
        $output->writeln('Index "'.Client::getIndex().'" created.');
        $output->writeln('Creating mapping "'.Client::getPath().'".');
        $esm->createMapping(Client::getIndex(), Client::getType(),  Client::getStructure());
        $output->writeln('Mapping "'.Client::getPath().'" created.');

        $output->writeln('Creating index "'.AccessToken::getIndex().'".');
        $esm->createIndex(AccessToken::getIndex());
        $output->writeln('Index "'.AccessToken::getIndex().'" created.');
        $output->writeln('Creating mapping "'.AccessToken::getPath().'".');
        $esm->createMapping(AccessToken::getIndex(), AccessToken::getType(),  AccessToken::getStructure());
        $output->writeln('Mapping "'.AccessToken::getPath().'" created.');

        $output->writeln('Creating index "'.RefreshToken::getIndex().'".');
        $esm->createIndex(RefreshToken::getIndex());
        $output->writeln('Index "'.RefreshToken::getIndex().'" created.');
        $output->writeln('Creating mapping "'.RefreshToken::getPath().'".');
        $esm->createMapping(RefreshToken::getIndex(), RefreshToken::getType(),  RefreshToken::getStructure());
        $output->writeln('Mapping "'.RefreshToken::getPath().'" created.');

        $output->writeln('Creating index "'.AuthCode::getIndex().'".');
        $esm->createIndex(AuthCode::getIndex());
        $output->writeln('Index "'.AuthCode::getIndex().'" created.');
        $output->writeln('Creating mapping "'.AuthCode::getPath().'".');
        $esm->createMapping(AuthCode::getIndex(), AuthCode::getType(),  AuthCode::getStructure());
        $output->writeln('Mapping "'.AuthCode::getPath().'" created.');
    }
}

Step 6: Configure your application's security.yml

In order for Symfony's security component to use the ElasticsearchFOSOAuthServerBundle, you must tell it to do so in the security.yml file. The security.yml file is where the basic configuration for the security for your application is contained.

Below is a minimal example of the configuration necessary to use the FOSOAuthServerBundle in your application:

# app/config/security.yml
security:
# You should choose your own encoder
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext

# You should choose your own roles
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
# A common configuration to view that it's compatible.
        in_memory:
            memory:
                users:
                    myownuser: { password: myPASSWORDisWONDERFUL, roles: [ 'ROLE_ADMIN' ] }
# Api provider
    	acme_apibundle_provider:
            id: acme.apibundle.security.provider

    firewalls:
# A service or group of services anonymous
        api_v1_my_anonymous_service_namespace:
            pattern: ^/api/v1/my_anonymous_service_namespace
            fos_oauth: true
            stateless: true
            anonymous: true
# A service or group of services protected by oauth
        api_v1_my_protected_service_namespace:
            pattern: ^/api/v1/my_protected_service_namespace
            fos_oauth: true
            stateless: true
# Pages protected by common configuration
        admin:
            pattern: ^/admin
            http_basic:
                realm: "Secured Admin Area"
# OAuth login configuration
        oauth_authorize:
            pattern: ^/oauth/v2/auth
            form_login:
                provider: acme_apibundle_provider
                check_path: /oauth/v2/auth_login_check
                login_path: /oauth/v2/auth_login
            anonymous: true
# OAuth token management
        oauth_token:
            pattern: ^/oauth/v2/token_app
            security: false

# You should choose your own access control
    access_control:
        - { path: ^/oauth/v2/auth_login$, role: IS_AUTHENTICATED_ANONYMOUSLY }

Step 7: Configure ElasticsearchFOSOAuthServerBundle

Import the routing.yml configuration file in app/config/routing.yml:

# app/config/routing.yml

fos_oauth_server_token:
    resource: "@FOSOAuthServerBundle/Resources/config/routing/token.xml"

fos_oauth_server_authorize:
    resource: "@FOSOAuthServerBundle/Resources/config/routing/authorize.xml"

Add ElasticsearchFOSOAuthServerBundle settings in app/config/config.yml:

# app/config/config.yml

fos_oauth_server:
    db_driver: propel
    client_class:        Acme\ApiBundle\Entity\Client
    access_token_class:  Acme\ApiBundle\Entity\AccessToken
    refresh_token_class: Acme\ApiBundle\Entity\RefreshToken
    auth_code_class:     Acme\ApiBundle\Entity\AuthCode
# If you want do login without form (With a service), you must configure the following handler
    authorize:
        form:
            handler:            vgr.fos_oauth_server.authorize.form.handler.default
    service:
        user_provider:          acme_apibundle_provider
        client_manager:         vgr.fos_oauth_server.client_manager.default
        access_token_manager:   vgr.fos_oauth_server.access_token_manager.default
        refresh_token_manager:  vgr.fos_oauth_server.refresh_token_manager.default
        auth_code_manager:      vgr.fos_oauth_server.auth_code_manager.default

Creating Login service

If you want create a client always.

# app/config/routing.yml

acme_apibundle:
    resource: "@AcmeApiBundle/Resources/config/routing.yml"
# src/Acme/ApiBundle/Resources/config/routing.yml

acme_apibundle_oauth_token:
    pattern:  /oauth/v2/token_app
    defaults: { _controller: AcmeApiBundle:Default:tokenApp }
    methods:  [GET]

acme_apibundle_oauth_auth_code:
    pattern:  /oauth/v2/auth_code
    defaults: { _controller: AcmeApiBundle:Default:getCode }
    methods:  [GET]

acme_apibundle_oauth_social_login:
    pattern:  /oauth/v2/auth_login
    defaults: { _controller: AcmeApiBundle:Default:login }
    methods:  [GET]

acme_apibundle_oauth_social_login_check:
    pattern:  /oauth/v2/auth_login_check
    defaults: { _controller: AcmeApiBundle:Default:loginCheck }
    methods:  [GET]
<?php
// src/Acme/ApiBundle/Controller/DefaultController.php

namespace Acme\ApiBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Acme\ApiBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

class DefaultController extends Controller
{
    public function getCodeAction(Request $request)
    {
        $code = $request->get('code');
        if(empty($code))
        {
            return new Response('{"error": {"field": "code", "message": "Code not found"}}', 400);
        }

        return new Response('{"code" : "'.$code.'"}');
    }

    public function loginCheckAction(Request $request)
    {
        return new Response('');
    }

    public function tokenAppAction(Request $request)
    {
        $code = $request->get('code');
        if(empty($code))
        {
            return new Response('{"error": {"field": "code", "message": "Code not found"}}', 400);
        }

        $redirectUrl = $this->generateUrl('acme_apibundle_oauth_auth_code');

        $authCodeManager = $this->get('vgr.fos_oauth_server.auth_code_manager.default');
        $authCode = $authCodeManager->findAuthCodeBy(array("token"=>$code));

        return $this->redirect($this->generateUrl('fos_oauth_server_token', array(
            'code'          => $code,
            'grant_type'    => 'authorization_code',
            'client_id'     => $authCode->getClientId(),
            'redirect_uri'  => $redirectUrl,
            'client_secret' => $authCode->getClient()->getSecret()
        )));
    }

    public function loginAction(Request $request)
    {
        $mail = $request->get('mail');
        if(empty($mail))
        {
            return new Response('{"error": {"field": "mail", "message": "Email not found"}}', 400);
        }

        $em = $this->get('vgr.elasticsearch.manager.elasticsearch');
        $userRepository = $em->getRepository('Acme\ApiBundle\Entity\User');
	$user = $userRepository->getOneByObject('GET', '/'.User::getIndex().'/'.User::getType().'/'.base64_encode($mail));

        if($user === null)
        {
            $user = new User();
            $user->setEmail($mail);
            $em->persist($user);
        }

	$redirectUrl = $this->generateUrl('acme_apibundle_oauth_auth_code');
        $clientManager = $this->get('vgr.fos_oauth_server.client_manager.default');
        $client = $clientManager->createClient();
	$client->setRedirectUris(array($redirectUrl));
        $client->setAllowedGrantTypes(array('token', 'authorization_code'));
        $clientManager->updateClient($client);

        $session = $request->getSession();
        $session->setId($client->getPublicId());

        $token = new UsernamePasswordToken($user, $user->getPassword(), "oauth_token", $user->getRoles());
        $securityContext = $this->get('security.context');
        $securityContext->setToken($token);

        $redirectResponse = new RedirectResponse($this->generateUrl('fos_oauth_server_authorize', array(
            'client_id'     => $client->getPublicId(),
            'redirect_uri'  => $redirectUrl,
            'response_type' => 'code',
            'token'         => $client->getPublicId()
        )));

        return $redirectResponse;
    }
}