Archives de
Month: février 2017

API Platform + FOSUserBundle + LexikJWTAuthenticationBundle

API Platform + FOSUserBundle + LexikJWTAuthenticationBundle

  • Symfony: 3.2.2
  • API Platform: 2.0.3
  • FOSUserBundle: 2.0.0-beta2
  • LexikJWTAuthenticationBundle: 2.1.1

En premier lieu nous allons cloner le dépôt API Plaftorm:

composer create-project api-platform/api-platform bookshop-api

Une fois fait, nous installons FOSUserBundle et LexikJWTAuthenticationBundle via composer:

composer require friendsofsymfony/user-bundle:2.0.0-beta-2
composer require lexik/jwt-authentication-bundle

Puis nous activons les bundles fraichement installé dans AppKernel.php:

public function registerBundles()
{
    return array(
        // ...
        new FOS\UserBundle\FOSUserBundle(),
        new Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle(),
    );
}

Nous créons notre entité User.php:

<?php

namespace AppBundle\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ORM\Entity
 * @ApiResource(attributes={
 *     "normalization_context"={"groups"={"user", "user-read"}},
 *     "denormalization_context"={"groups"={"user", "user-write"}}
 * })
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @Groups({"user"})
     */
    protected $email;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     * @Groups({"user"})
     */
    protected $fullname;

    /**
     * @Groups({"user-write"})
     */
    protected $plainPassword;

    /**
     * @Groups({"user"})
     */
    protected $username;

    public function setFullname($fullname)
    {
        $this->fullname = $fullname;

        return $this;
    }
    public function getFullname()
    {
        return $this->fullname;
    }

    public function isUser(UserInterface $user = null)
    {
        return $user instanceof self && $user->id === $this->id;
    }
}

Nous poursuivons par la génération des clés, notez bien la pass phrase qu’il faudra copié dans le fichier parameters.yml

mkdir -p var/jwt # For Symfony3+, no need of the -p option
openssl genrsa -out var/jwt/private.pem -aes256 4096
openssl rsa -pubout -in var/jwt/private.pem -out var/jwt/public.pem

Nous mettons a jours les fichier suivants:

config.yml:

framework:
    translator:      { fallbacks: ["%locale%"] }

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

lexik_jwt_authentication:
    private_key_path: '%jwt_private_key_path%'
    public_key_path:  '%jwt_public_key_path%'
    pass_phrase:      '%jwt_key_pass_phrase%'
    token_ttl:        '%jwt_token_ttl%'

routing.yml

api:
    resource: '.'
    type:     'api_platform'

app:
    resource: '@AppBundle/Action/'
    type:     'annotation'

api_login_check:
    path: /api/login_check

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

fos_user_profile:
    resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
    prefix: /profile

fos_user_register:
    resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
    prefix: /register

fos_user_resetting:
    resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
    prefix: /resetting

fos_user_change_password:
    resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
    prefix: /profile

security.yml

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt

    role_hierarchy:
        ROLE_READER: ROLE_USER
        ROLE_ADMIN: ROLE_READER

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email

    firewalls:
        login:
            pattern:  ^/login
            stateless: true
            anonymous: true
            provider: fos_userbundle
            form_login:
                check_path: /login_check
                username_parameter: _username
                password_parameter: _password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure
                require_previous_session: false

        main:
            pattern: ^/
            provider: fos_userbundle
            stateless: true
            anonymous: true
            lexik_jwt: ~

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

    access_control:
        - { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/books, roles: [ ROLE_READER ] }
        - { path: ^/, roles: [ ROLE_READER ] }

parameters.yml.dist et parameters.yml, n’oubliez pas la pass phrase pour les clés

parameters:
    # ...
    jwt_private_key_path: '%kernel.root_dir%/../var/jwt/private.pem' # ssh private key path
    jwt_public_key_path:  '%kernel.root_dir%/../var/jwt/public.pem'  # ssh public key path
    jwt_key_pass_phrase:  ''                                         # ssh key pass phrase
    jwt_token_ttl:        3600

Puis nous mettons à jour la base de données et créons notre premier utilisateur:

php bin/console doctrine:schema:update --force
php bin/console fos:user:create testuser test@example.com p@ssword
php bin/console fos:user:promote testuser ROLE_READER

Pour tester ca un petit coup de Curl:

curl -X POST http://127.0.0.1:8000/login_check -d _username=test@example.com -d _password=p@ssword

Et comme réponse nous avons:

{"token":"eyJhbGciOiJSUzI1NiJ9.eyJyb2xlcyI6WyJST0xFX1JFQURFUiIsIlJPTEVfVVNFUiJdLCJ1c2VybmFtZSI6InRlc3R1c2VyIiwiZXhwIjoxNDg2NDk5MjczLCJpYXQiOjE0ODY0OTU2NzN9.RL8VS8MuKhvg0uW23TPnlxsZusUIE86YuE_cg44F4rToe8c7zV6s-2lmuOhP5CyeB50IVfLlb4-RC1frKQFTIrEA1LJ2FewxKFO2Mb2sIQwQonAew3MTwnxbnPRWrS0sQCsb1NbXTxohc5WH-BU1U3IkuAjuTVLcB3g7JlPor0yedUU766BRXjIhklN7IuILKsjhteqGK6F9Ilg0zmLyjQV3-BUg1nnWygQ-O4BLrU_InvhpHZVOnPfikMftcKVAhn_Z4GKGVVORQxplhc4i0lJgzdV83AaImYqOzn3WBJxKnzmitb6rnOJ-SKXjdAYRix_rFwqIzzWNdcAg1-C731b08I3qcfWXmmadI2GsygVpemlagH8v6tcFjwVsyazblqFCA8oy37N0CfSVt7av8GIbenWtIDUS5Tl3a0SMxmcwg6rlTL_1pE0-E8mPa8ZbLho-nON2hKoQcG1LhIeuatzIi7lUSLCQv_uzy29Hpq7PPJ_43cZZDdex-vk3BVHbjZCOcggVxp-mDDTcU7VxZFwmi8AiP9ZAg8AFXviKcfM7D5Er2tB2k3HHsnCqTSGRJe9czNs-GaW55748BK_u0UiEBvBdDHUgmbGw4UTRw8aNTY8p8M_tavezC36p5zilyd2CB6knsLtm0aRkqyZaiK27uvajYMt65BpxqsmXzLw"}

Et voila !!!

Disponible sur github.