Skip to content

Instantly share code, notes, and snippets.

@yusufiga
Last active May 16, 2016 17:16
Show Gist options
  • Save yusufiga/1ca580c4fd8e12e3663b8e5f6578a555 to your computer and use it in GitHub Desktop.
Save yusufiga/1ca580c4fd8e12e3663b8e5f6578a555 to your computer and use it in GitHub Desktop.

#Install Symfony and the bundles

Create New Symfony Project

symfony new {project_name} lts

Require Bundles

composer require jms/serializer-bundle
composer require friendsofsymfony/user-bundle
composer require friendsofsymfony/oauth-server-bundle
composer require nelmio/api-doc-bundle

##Add Bundles to AppKernel.php to enable the downloaded bundles

// app/AppKernel.php
class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new FOS\UserBundle\FOSUserBundle(),
            new FOS\OAuthServerBundle\FOSOAuthServerBundle(),
            new JMS\SerializerBundle\JMSSerializerBundle(),
            new Nelmio\ApiDocBundle\NelmioApiDocBundle(),
            //..
        );
        // ...
    }
}

#Configuration ##Edit config.yml file app/config/config.yml

#...
nelmio_api_doc: ~

fos_user:
    db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
    firewall_name: main
    user_class: AppBundle\Entity\User

fos_oauth_server:
    db_driver: orm
    client_class:        AppBundle\Entity\Client
    access_token_class:  AppBundle\Entity\AccessToken
    refresh_token_class: AppBundle\Entity\RefreshToken
    auth_code_class:     AppBundle\Entity\AuthCode
    service:
        user_provider: fos_user.user_manager
        options:
            supported_scopes: user
            # Changing tokens and authcode lifetime
            access_token_lifetime: 86400 # 1 day 
            refresh_token_lifetime: 1209600 # 14 days
            #auth_code_lifetime: 30

#Security

# To get started with security, check out the documentation:
# http://symfony.com/doc/current/book/security.html
security:

    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_ADMIN

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username

      firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        oauth_token:
            pattern:      ^/oauth/v2/token
            security:     false
            
        api_doc:
            pattern:    ^/api/doc
            security:   false

        api:
            pattern:    ^/api
            fos_oauth:  true
            stateless:  true

        main:
            pattern: ^/
            form_login:
                provider: fos_userbundle
                csrf_token_generator: security.csrf.token_manager


            logout:       true
            anonymous:    true



    access_control:
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, role: IS_AUTHENTICATED_FULLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

#        - { path: ^/api, roles: [ IS_AUTHENTICATED_FULLY ] }

#Routing

# app/config/routing.yml
NelmioApiDocBundle:
    resource: "@NelmioApiDocBundle/Resources/config/routing.yml"
    prefix:   /api/doc

app:
    resource: "@AppBundle/Controller/"
    type:     annotation

api:
    resource: "@AppBundle/Controller/UsersController.php"
    type:     annotation
    prefix:   "/api"

user:
    resource: "@FOSUserBundle/Resources/config/routing/security.xml"

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

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

#Creating Models ##Create User Model

<?php
// src/AppBundle/Entity/User.php

namespace AppBundle\Entity;

use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="user")
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    public function __construct()
    {
        parent::__construct();
        // your own logic
    }
}

##Create Model Class for OAuth

<?php
// src/AppBundle/Entity/Client.php

namespace AppBundle\Entity;

use FOS\OAuthServerBundle\Entity\Client as BaseClient;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class Client extends BaseClient
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    public function __construct()
    {
        parent::__construct();
    }
}
<?php
// src/AppBundle/Entity/AccessToken.php

namespace AppBundle\Entity;

use FOS\OAuthServerBundle\Entity\AccessToken as BaseAccessToken;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class AccessToken extends BaseAccessToken
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\ManyToOne(targetEntity="Client")
     * @ORM\JoinColumn(nullable=false)
     */
    protected $client;

    /**
     * @ORM\ManyToOne(targetEntity="User")
     */
    protected $user;
}
<?php
// src/AppBundle/Entity/RefreshToken.php

namespace AppBundle\Entity;


use FOS\OAuthServerBundle\Entity\RefreshToken as BaseRefreshToken;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class RefreshToken extends BaseRefreshToken
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\ManyToOne(targetEntity="Client")
     * @ORM\JoinColumn(nullable=false)
     */
    protected $client;

    /**
     * @ORM\ManyToOne(targetEntity="User")
     */
    protected $user;
}
<?php
// src/AppBundle/Entity/AuthCode.php

namespace AppBundle\Entity;

use FOS\OAuthServerBundle\Entity\AuthCode as BaseAuthCode;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class AuthCode extends BaseAuthCode
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\ManyToOne(targetEntity="Client")
     * @ORM\JoinColumn(nullable=false)
     */
    protected $client;

    /**
     * @ORM\ManyToOne(targetEntity="User")
     */
    protected $user;
}

You can now update your database schema :

php app/console doctrine:schema:update --force

#Creating Sample Controller We already have the default controller from fresh installation #UserController

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\User;
use AppBundle\Form\UserType;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class UsersController extends Controller
{

   /**
     * Response with all users registered on the database
     *
     * @ApiDoc(
     *  section="User",
     *  resource=true,
     *  description="Get all users",
     *  statusCodes={
     *         200="Returned when successful"
     *  },
     *  tags={
     *   "stable" = "#4A7023",
     *   "need validations" = "#ff0000"
     *  }
     * )
     * @Route("users")
     * @Method("GET")
     */
    public function getAction()
    {
        //security.yml is configured to allow anonymous access to controllers
        //checking for authorization in each controller allows more flexibility
        //to change this remove anonymous: true in security.yml on firewall
        if (!$this->isGranted('IS_AUTHENTICATED_FULLY')) { //ROLE_USER ROLE_ADMIN
            throw $this->createAccessDeniedException();
        }

        $em = $this->getDoctrine()->getManager();
        $repository = $em->getRepository("AppBundle:User");

        $users = $repository->findAll();
        $data = $this->container->get('jms_serializer')
            ->serialize($users, 'json');

        return new Response($data, 200, array("content-type"=>"application/json"));
    }

    /**
     * Create a new user
     *
     * @ApiDoc(
     *  section="User",
     *  description="Create a new User",
     *  input="AppBundle\Form\UserType",
     *  output="AppBundle\Entity\User",
     *  statusCodes={
     *         200="Returned when successful"
     *  },
     *  tags={
     *   "stable" = "#4A7023",
     *   "need validations" = "#ff0000"
     *  }
     * )
     * @Route("users")
     * @Method("POST")
     */
    public function postAction(Request $request)
    {
        $userManager = $this->get('fos_user.user_manager');

        $user = $userManager->createUser();
        $user->setEnabled(true);

        $form = $this->createForm(UserType::class, $user);

        $form->handleRequest($request);
       // $form->setData(json_decode($request->getContent(),true));
        $form->submit(json_decode($request->getContent(),true));

        if ($form->isValid()) {

            $user->setPlainPassword($user->getPassword());
            $userManager->updateUser($user);

            $data = $this->container->get('jms_serializer')
                ->serialize($user, 'json');
            return new Response($data, 200);
        }

        $data = $this->container->get('jms_serializer')
            ->serialize($form->getErrors(true), 'json');
        return new Response($data, 400);
    }
}

##Create UserType Form

<?php
// src/AppBundle/Form/UserType.php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class UserType extends AbstractType
{

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('username', TextType::class)
            ->add('email', EmailType::class)
            ->add('password', PasswordType::class);
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\User',
            'csrf_protection' => false
        ));
    }
}

##Create admin user

$ php app/console fos:user:create
Please choose a username:admin
Please choose an email:admin@example.com
Please choose a password:admin
Created user admin

##Promote User

php app/console fos:user:promote admin --super

#Create Command File

Execute function creates the client if you don't wan't to use console command file you can user this function to create client. For more information visit official docs : Creating A Client

<?php
//src/AppBundle/Command/CreateClientCommand.php

namespace AppBundle\Command;

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

class CreateClientCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this
            ->setName('acme:oauth-server:client:create')
            ->setDescription('Creates a new client')
            ->addOption(
                'redirect-uri',
                null,
                InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
                'Sets redirect uri for client. Use this option multiple times to set multiple redirect URIs.',
                null
            )
            ->addOption(
                'grant-type',
                null,
                InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
                'Sets allowed grant type for client. Use this option multiple times to set multiple grant types..',
                null
            )
            ->setHelp(
                <<<EOT
                    The <info>%command.name%</info>command creates a new client.

<info>php %command.full_name% [--redirect-uri=...] [--grant-type=...] name</info>

EOT
            );
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $clientManager = $this->getContainer()->get('fos_oauth_server.client_manager.default');
        $client = $clientManager->createClient();
        $client->setRedirectUris($input->getOption('redirect-uri'));
        $client->setAllowedGrantTypes($input->getOption('grant-type'));
        $clientManager->updateClient($client);
        $output->writeln(
            sprintf(
                'Added a new client with public id <info>%s</info>, secret <info>%s</info>',
                $client->getPublicId(),
                $client->getSecret()
            )
        );
    }
}

#Create An OAuth Client Only use the grant-type you intend to use.

php app/console acme:oauth-server:client:create --redirect-uri="http://127.0.0.1:8000/" --grant-type="authorization_code" --grant-type="password" --grant-type="refresh_token" --grant-type="token" --grant-type="client_credentials"

#Run app

php app/console server:run

##Get User Access & Refresh Tokens Browse to the following URL while Replacing CLIENTID, CLIENTSECRET, USERNAME, & PASSWORD with your values:

http://127.0.0.1:8000/app_dev.php/oauth/v2/token?client_id=__CLIENTID__&client_secret=__CLIENTSECRET__&grant_type=password&username=USERNAME&password=PASSWORD 

##Get New Access & Refresh Tokens with Refresh Token Browse to the following URL while Replacing CLIENTID, CLIENTSECRET, REFRESHTOKEN with your values:

http://127.0.0.1:8000/app_dev.php/oauth/v2/token?client_id=__CLIENTID__&client_secret=__CLIENTSECRET__&grant_type=refresh_token&refresh_token=__REFRESHTOKEN__

##See Authorization Failure:

The following will return a access_denied error:

curl http://127.0.0.1:8000/app_dev.php/users

##See Authorization Success:

The following will return successful set of users (json).

curl -H "Authorization: Bearer ACCESS_TOKEN" -H "Accept: application/json" http://127.0.0.1:8000/app_dev.php/users

##Create user with curl

curl -X POST -H "Authorization: Bearer ACCESS_TOKEN" -H "Content-Type: application/json" -H "Accept: application/json" -d '{"user":{"username": "username", "password": "secretpass", "email": "test@test.com"}}' http://127.0.0.1:8000/app_dev.php/api/users

##Api Docs

http://127.0.0.1:8000/app_dev.php/api/doc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment