Skip to content

Instantly share code, notes, and snippets.

@mirkorap
Last active February 22, 2024 14:51
Show Gist options
  • Select an option

  • Save mirkorap/cd9f2933304eb868e489e7b6b573074c to your computer and use it in GitHub Desktop.

Select an option

Save mirkorap/cd9f2933304eb868e489e7b6b573074c to your computer and use it in GitHub Desktop.

Revisions

  1. mirkorap revised this gist Nov 15, 2018. 1 changed file with 7 additions and 7 deletions.
    14 changes: 7 additions & 7 deletions symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -75,7 +75,7 @@ In the `config/packages/jms_serializer.yaml` file insert these configuration opt
    Documentation: [https://jmsyst.com/bundles/JMSSerializerBundle](https://jmsyst.com/bundles/JMSSerializerBundle)

    ### NelmioApiDocBundle ###
    NelmioApiDocBundle is a Symfony's bundle that allow us to generate documentation for our APIs.
    NelmioApiDocBundle is a Symfony's bundle that allows us to generate documentation for our APIs.

    In the `config/packages/nelmio_api_doc.yaml` file insert these configuration options:

    @@ -105,7 +105,7 @@ In the `config/routes.yaml` file insert this route option:
    Documentation: [https://symfony.com/doc/current/bundles/NelmioApiDocBundle/index.html](https://symfony.com/doc/current/bundles/NelmioApiDocBundle/index.html)

    ### FOSUserBundle ###
    FOSUserBundle allow us to manage users (store and load users, authenticate users, manage roles etc.)
    FOSUserBundle allows us to manage users (store and load users, authenticate users, manage roles etc.)

    In the `config/packages/fos_user.yaml` file insert these configuration options (if the file doesn't exist, create it!):

    @@ -160,7 +160,7 @@ Now let's create our entity User class:
    Documentation: [https://symfony.com/doc/current/bundles/FOSUserBundle/index.html](https://symfony.com/doc/current/bundles/FOSUserBundle/index.html)

    ### FOSOAuthServerBundle ###
    FOSOAuthServerBundle is a Symfony's bundle that allow us to create our oauth2 server app. In this gist I will explain you how to use it with the main oauth2 code flows (Authorization code, Implicit, Password credentials). Let's take a look to the configuration options:
    FOSOAuthServerBundle is a Symfony's bundle that allows us to create our oauth2 server app. In this gist I will explain you how to use it with the main oauth2 code flows (Authorization code, Implicit, Password credentials). Let's take a look to the configuration options:

    In the `config/packages/fos_oauth_server.yaml` file insert these configuration options (if the file doesn't exist, create it!):

    @@ -499,7 +499,7 @@ Create an ApiController that will serve our resources. In this example we will c
    * @param Request $request
    * @return View
    */
    public function index(Request $request)
    public function getAllUsersAction(Request $request)
    {
    $offset = $request->query->get('offset', 0);
    $limit = $request->query->get('limit', 10);
    @@ -512,7 +512,7 @@ Create an ApiController that will serve our resources. In this example we will c
    }
    }

    In the `src/Repository/UserRepository` let's add a `getAllUsers` function:
    In the `src/Repository/UserRepository` let's add the `getAllUsers` function:

    <?php

    @@ -719,7 +719,7 @@ Then to get a new `access_token` you need to call the route: `/oauth/v2/token` w
    Content-Type: application/json

    ## Implicit ##
    This flow is principally used in SPA. A difference from the previous flow is that we will receive an access_token immediately, so we don't need to exchange an authorization code for an access_token. You may think "Why I should use the Authorization code flow if the Implicit one is more simple?". The answer is easy, you MUST use the authorization code flow if you need to call APIs even when the user is offline. In fact with the Implicit flow you will not receive a refresh_token (for security reason), so when the access_token expires the user must authorize access again. For this reason this flow is more suitable in the SPA. Let's see how it works:
    This flow is principally used in SPA. A difference from the previous flow is that we will receive an `access_token` immediately, so we don't need to exchange an authorization code for an `access_token`. You may think "Why I should use the Authorization code flow if the Implicit one is more simple?". The answer is easy, you MUST use the authorization code flow if you need to call APIs even when the user is offline. In fact with the Implicit flow you will not receive a `refresh_token` (for security reason), so when the `access_token` expires the user must authorize access again. For this reason this flow is more suitable in the SPA. Let's see how it works:

    Again we need to create a client, so open your terminal and run:

    @@ -795,7 +795,7 @@ With these data we can call the route to get the `access_token`, but this time w
    Connection: close
    Content-Type: application/json

    The username and password that you see above come from the user created by FOSUserBundle's command `php bin/console fos:user:create`. Now we can call our APIs
    The username and password that you see above come from the user created by FOSUserBundle's command `php bin/console fos:user:create`. Now we can call our API

    http GET localhost:8000/api/users \
    "Authorization:Bearer MmQ5MzRlOTRhNWUzMzY0ZGU1ZjMzMjkwY2I0YjlhN2NlOTBhZjg5NGJjMGM5Yzk2ZGFiYWI5YTBkYjBiMWIyNw"
  2. mirkorap revised this gist Nov 15, 2018. 1 changed file with 9 additions and 9 deletions.
    18 changes: 9 additions & 9 deletions symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -644,14 +644,14 @@ Then we need to create a `SecurityController` with two routes: `/oauth/v2/auth_l
    }
    }

    To complete the `SecurityController` we need to have a login page where the user will be redirect if he isn't already logged in. For simplicity let's copy the `FOSUserBundle` login template files. You can find them inside the directory `vendor/friendsofsymfony/user-bundle/Resources/views`. From that directory you should copy the directory `Security` and the file `layout.html.twig` and put them inside the directory `templates/bundles/FOSUserBundle`.
    To complete the `SecurityController` we need to have a login page where the user will be redirect if he isn't already logged in. For simplicity let's copy the `FOSUserBundle` template files. You can find them inside the directory `vendor/friendsofsymfony/user-bundle/Resources/views`. From that directory you should copy the directory `Security` and the file `layout.html.twig` and put them inside the directory `templates/bundles/FOSUserBundle`.

    More information about the overriding of templates you can find here: [Symfony's bunde override](https://symfony.com/doc/current/bundles/override.html "Symfony's bunde override"). <br/>
    Now open the file `templates/bundles/FOSUserBundle/Security/login_content.html.twig` and in the action of form replace the path `fos_user_security_check` with `auth_login_check`.

    To verify that all works properly, we should call this route: `http://localhost:8000/oauth/v2/auth?client_id=1_3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck&redirect_uri=http://localhost:8000/redirect-uri-example&response_type=code`

    The client_id query parameter should be replaced by the client_id created previously. The response should be a page with two buttons: "allow" and "deny". After clicking in the "allow" button you will redirect to the `redirect_uri`. In the url you can notice a code query parameter, that is our authorization code! Now we need to exchange that code with an `access_token`. So let's create a request to our `/oauth/v2/token` route:
    The `client_id` query parameter should be replaced by the `client_id` created previously. The response should be a page with two buttons: "allow" and "deny". After clicking in the "allow" button you will redirect to the `redirect_uri`. In the url you can notice a `code` query parameter, that is our authorization code! Now we need to exchange that code with an `access_token`. So let's create a POST request to `/oauth/v2/token`:

    http POST http://localhost:8000/oauth/v2/token \
    grant_type=authorization_code \
    @@ -674,7 +674,7 @@ The response should looks like this:
    "refresh_token": "NDFiNjY3MWU3YmZhMDkxNjI5NjBjZDhhNmYzNmJkYjI1NTIzMGJiZTQzYTA0NzlkYjU2OTgzYmM5MzZlODg2NQ"
    }

    Now we can finally call our APIs! Create a request to the route: `localhost:8000/api/users` and pass in the header the `access_token`:
    Now we can finally call our APIs! Create a request to `localhost:8000/api/users` and pass in the header the `access_token`:

    http GET localhost:8000/api/users \
    "Authorization:Bearer ZTFiZWIwMzI4ZTg4MmYzNTExNjkxMzYwODBlMzQ3NTgxNzM1ODMxYTQzNGZkZDI0ZjQ4MmRkN2ZjMDRhMWU2YQ"
    @@ -719,7 +719,7 @@ Then to get a new `access_token` you need to call the route: `/oauth/v2/token` w
    Content-Type: application/json

    ## Implicit ##
    This flow is principally used in SPA. A difference from the previous flow is that we will receive an access_token immediately, so we don't need to exchange an authorization code for an access_token. You may think "Why I should use the Authorization code flow if the Implicit one is more simple?". The answer is easy, you MUST use the authorization code flow if you need to call API even when the user is offline. In fact with the Implicit flow you will not receive a refresh_token (for security reason), so when the access_token expire the user must authorize access again. For this reason this flow is more suitable in the SPA. Let's see how it works:
    This flow is principally used in SPA. A difference from the previous flow is that we will receive an access_token immediately, so we don't need to exchange an authorization code for an access_token. You may think "Why I should use the Authorization code flow if the Implicit one is more simple?". The answer is easy, you MUST use the authorization code flow if you need to call APIs even when the user is offline. In fact with the Implicit flow you will not receive a refresh_token (for security reason), so when the access_token expires the user must authorize access again. For this reason this flow is more suitable in the SPA. Let's see how it works:

    Again we need to create a client, so open your terminal and run:

    @@ -733,12 +733,12 @@ In my case the client created by the bundle has these data:
    | 2 | 2shspbzhca2ogw44kckw00s4s800g0s484004ccokcw8gogwks | 3fcdpac3iw6cw8k8gs48kwc8ows4s4wkcg8gwgsgck84g48k0s |
    +------------+---------------------------------------------------------+------------------------------------------------------+

    client_id: `2_2shspbzhca2ogw44kckw00s4s800g0s484004ccokcw8gogwks` (id + random_id)
    client_id: `2_2shspbzhca2ogw44kckw00s4s800g0s484004ccokcw8gogwks` (id + random_id) <br/>
    secret: `3fcdpac3iw6cw8k8gs48kwc8ows4s4wkcg8gwgsgck84g48k0s`

    With these data we can call immediately the route `http://localhost:8000/oauth/v2/auth?client_id=2_2shspbzhca2ogw44kckw00s4s800g0s484004ccokcw8gogwks&redirect_uri=http://localhost:8000/redirect-uri-example&response_type=token`
    With these data we can call the route `http://localhost:8000/oauth/v2/auth?client_id=2_2shspbzhca2ogw44kckw00s4s800g0s484004ccokcw8gogwks&redirect_uri=http://localhost:8000/redirect-uri-example&response_type=token`

    The client_id query parameter should be replaced by the client_id created previously. The response should be a page with two buttons: "allow" and "deny". After clicking in the "allow" button you will redirect to the `redirect_uri`. In the url you can notice an hash with the access_token. Now we can call our API to get all users (remember to pass a Bearer header with the access_token):
    The `client_id` query parameter should be replaced by the `client_id` created previously. The response should be a page with two buttons: "allow" and "deny". After clicking in the "allow" button you will redirect to the `redirect_uri`. In the url you can notice an hash with the `access_token`. Now we can call our API to get all users (remember to pass a Bearer header with the `access_token`):

    http GET localhost:8000/api/users \
    "Authorization:Bearer NDYxMmUwZGI5OTQ4Y2UzNDQ5M2JhY2YwYmU3YmM2NDlkMDUyNTk4MjE2N2ZhNDU2OWQ2MTc4NjM1MmYxMDBmOQ"
    @@ -779,7 +779,7 @@ In my case the client created by the bundle has these data:
    | 3 | 492ohmxbo3k0gggwg08wso4cs0k4sswcwg8wo8so0cgo4cwo0s | 64jack8z62888k4sgcgssow080wcgsk04408wogkc4owgkgokc |
    +------------+---------------------------------------------------------+------------------------------------------------------+

    client_id: `3_492ohmxbo3k0gggwg08wso4cs0k4sswcwg8wo8so0cgo4cwo0s` (id + random_id)
    client_id: `3_492ohmxbo3k0gggwg08wso4cs0k4sswcwg8wo8so0cgo4cwo0s` (id + random_id) <br/>
    secret: `64jack8z62888k4sgcgssow080wcgsk04408wogkc4owgkgokc`

    With these data we can call the route to get the `access_token`, but this time we should send also username and password!
    @@ -795,7 +795,7 @@ With these data we can call the route to get the `access_token`, but this time w
    Connection: close
    Content-Type: application/json

    The username and password that you see above come from the user created by FOSUserBundle command `php bin/console fos:user:create`. Now we can call our APIs
    The username and password that you see above come from the user created by FOSUserBundle's command `php bin/console fos:user:create`. Now we can call our APIs

    http GET localhost:8000/api/users \
    "Authorization:Bearer MmQ5MzRlOTRhNWUzMzY0ZGU1ZjMzMjkwY2I0YjlhN2NlOTBhZjg5NGJjMGM5Yzk2ZGFiYWI5YTBkYjBiMWIyNw"
  3. mirkorap revised this gist Nov 14, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -646,7 +646,7 @@ Then we need to create a `SecurityController` with two routes: `/oauth/v2/auth_l

    To complete the `SecurityController` we need to have a login page where the user will be redirect if he isn't already logged in. For simplicity let's copy the `FOSUserBundle` login template files. You can find them inside the directory `vendor/friendsofsymfony/user-bundle/Resources/views`. From that directory you should copy the directory `Security` and the file `layout.html.twig` and put them inside the directory `templates/bundles/FOSUserBundle`.

    More information about the overriding of templates you can find here: [https://symfony.com/doc/current/bundles/override.html](https://symfony.com/doc/current/bundles/override.html "Symfony's bunde override"). <br/>
    More information about the overriding of templates you can find here: [Symfony's bunde override](https://symfony.com/doc/current/bundles/override.html "Symfony's bunde override"). <br/>
    Now open the file `templates/bundles/FOSUserBundle/Security/login_content.html.twig` and in the action of form replace the path `fos_user_security_check` with `auth_login_check`.

    To verify that all works properly, we should call this route: `http://localhost:8000/oauth/v2/auth?client_id=1_3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck&redirect_uri=http://localhost:8000/redirect-uri-example&response_type=code`
  4. mirkorap revised this gist Nov 14, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -646,7 +646,7 @@ Then we need to create a `SecurityController` with two routes: `/oauth/v2/auth_l

    To complete the `SecurityController` we need to have a login page where the user will be redirect if he isn't already logged in. For simplicity let's copy the `FOSUserBundle` login template files. You can find them inside the directory `vendor/friendsofsymfony/user-bundle/Resources/views`. From that directory you should copy the directory `Security` and the file `layout.html.twig` and put them inside the directory `templates/bundles/FOSUserBundle`.

    More information about the overriding of templates you can find here: [https://symfony.com/doc/current/bundles/override.html](https://symfony.com/doc/current/bundles/override.html). <br/>
    More information about the overriding of templates you can find here: [https://symfony.com/doc/current/bundles/override.html](https://symfony.com/doc/current/bundles/override.html "Symfony's bunde override"). <br/>
    Now open the file `templates/bundles/FOSUserBundle/Security/login_content.html.twig` and in the action of form replace the path `fos_user_security_check` with `auth_login_check`.

    To verify that all works properly, we should call this route: `http://localhost:8000/oauth/v2/auth?client_id=1_3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck&redirect_uri=http://localhost:8000/redirect-uri-example&response_type=code`
  5. mirkorap revised this gist Nov 14, 2018. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -644,7 +644,9 @@ Then we need to create a `SecurityController` with two routes: `/oauth/v2/auth_l
    }
    }

    To complete the `SecurityController` we need to have a login page where the user will be redirect if he isn't already logged in. For simplicity let's copy the `FOSUserBundle` login template files. You can find them inside the directory `vendor/friendsofsymfony/user-bundle/Resources/views`. From that directory you should copy the directory `Security` and the file `layout.html.twig` and put them inside the directory `templates/bundles/FOSUserBundle`. More information about the overriding of templates you can find here: [https://symfony.com/doc/current/bundles/override.html](https://symfony.com/doc/current/bundles/override.html)
    To complete the `SecurityController` we need to have a login page where the user will be redirect if he isn't already logged in. For simplicity let's copy the `FOSUserBundle` login template files. You can find them inside the directory `vendor/friendsofsymfony/user-bundle/Resources/views`. From that directory you should copy the directory `Security` and the file `layout.html.twig` and put them inside the directory `templates/bundles/FOSUserBundle`.

    More information about the overriding of templates you can find here: [https://symfony.com/doc/current/bundles/override.html](https://symfony.com/doc/current/bundles/override.html). <br/>
    Now open the file `templates/bundles/FOSUserBundle/Security/login_content.html.twig` and in the action of form replace the path `fos_user_security_check` with `auth_login_check`.

    To verify that all works properly, we should call this route: `http://localhost:8000/oauth/v2/auth?client_id=1_3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck&redirect_uri=http://localhost:8000/redirect-uri-example&response_type=code`
  6. mirkorap revised this gist Nov 14, 2018. 1 changed file with 34 additions and 31 deletions.
    65 changes: 34 additions & 31 deletions symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -9,8 +9,8 @@
    3. [NelmioApiDocBundle](#nelmioapidocbundle "NelmioApiDocBundle")
    4. [FOSUserBundle](#fosuserbundle "FOSUserBundle")
    5. [FOSOAuthServerBundle](#fosoauthserverbundle "FOSOAuthServerBundle")
    4. [Make migrations](#make-migrations "Make migrations")
    5. [Security](#security "Security")
    4. [Security](#security "Security")
    5. [Make migrations](#make-migrations "Make migrations")
    6. [Create an ApiController](#create-an-apicontroller "Create an ApiController")
    7. [Authorization code](#authorization-code "Authorization code")
    1. [Refresh token](#refresh-token "Refresh token")
    @@ -24,12 +24,12 @@ The flows that we implement will be:
    2. Implicit
    3. Password credentials

    I will separate the gist into two configuration parts. The first part is the common configuration that is equal for all Oauth2 code flows above. In the second part I will explain, for each code flows above, the basic configuration type that you need. So let's coding!
    I will separate the gist into two configuration parts. The first part is the common configuration that is equal for all Oauth2 code flows above. In the second part I will explain, for each flow, the basic configuration type that you need. So let's coding!

    ## Install bundles ##
    The first step is to download Symfony and the related bundles.

    composer create-project symfony/skeleton oauth2-server
    composer create-project symfony/website-skeleton oauth2-server
    cd oauth2-server
    composer require friendsofsymfony/rest-bundle
    composer require jms/serializer-bundle
    @@ -42,7 +42,7 @@ Below I will explain for each bundle what they do and how to configure them.
    ## Configuration ##

    ### FOSRestBundle ###
    FOSRestBundle allow us to configure our APIs and serve resources in different format in an easy way. There a different configuration options that you can use, but in our example we need to serve resources in json/xml format for all routes starting with `/api`, instead for all others we will serve simple html pages.
    FOSRestBundle allow us to configure our APIs and serve resources in different format in an easy way. There are different configuration options that you can use, but in our example we will serve resources in json/xml format for all routes starting with `/api`, instead for all others we will serve simple html pages.

    In the `config/packages/fos_rest.yaml` file insert these configuration options:

    @@ -105,9 +105,9 @@ In the `config/routes.yaml` file insert this route option:
    Documentation: [https://symfony.com/doc/current/bundles/NelmioApiDocBundle/index.html](https://symfony.com/doc/current/bundles/NelmioApiDocBundle/index.html)

    ### FOSUserBundle ###
    FOSUserBundle allow us to manage users (store and load users, register and authenticate users etc.)
    FOSUserBundle allow us to manage users (store and load users, authenticate users, manage roles etc.)

    In the `config/packages/fos_user.yaml` file insert these configuration options:
    In the `config/packages/fos_user.yaml` file insert these configuration options (if the file doesn't exist, create it!):

    fos_user:
    db_driver: orm
    @@ -128,7 +128,7 @@ In the `config/packages/framework.yaml` file insert this line at the end of file
    templating:
    engines: twig

    Now let's to create our entity User class:
    Now let's create our entity User class:

    <?php

    @@ -160,9 +160,9 @@ Now let's to create our entity User class:
    Documentation: [https://symfony.com/doc/current/bundles/FOSUserBundle/index.html](https://symfony.com/doc/current/bundles/FOSUserBundle/index.html)

    ### FOSOAuthServerBundle ###
    FOSOAuthServerBundle is a Symfony bundle that allow us to create our oauth2 server app. In this gist I will exaplain you how to use it with the main oauth2 code flows (Authorization code, Implicit, Password credentials). Let's take a look to the configuration options:
    FOSOAuthServerBundle is a Symfony's bundle that allow us to create our oauth2 server app. In this gist I will explain you how to use it with the main oauth2 code flows (Authorization code, Implicit, Password credentials). Let's take a look to the configuration options:

    In the `config/packages/fos_oauth_server.yaml` file insert these configuration options:
    In the `config/packages/fos_oauth_server.yaml` file insert these configuration options (if the file doesn't exist, create it!):

    fos_oauth_server:
    db_driver: orm
    @@ -178,7 +178,7 @@ In the `config/routes.yaml` file insert these route options:
    fos_oauth_server_token:
    resource: "@FOSOAuthServerBundle/Resources/config/routing/token.xml"

    # Add this route only if you are going to use the Authorization code flow
    # Add this route option only if you are going to use the Authorization code flow
    fos_oauth_server_authorize:
    resource: "@FOSOAuthServerBundle/Resources/config/routing/authorize.xml"

    @@ -364,23 +364,6 @@ Above you can see the annotation `@ORM\AttributeOverrides`. This is required to

    Documentation: [https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/blob/master/Resources/doc/index.md](https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/blob/master/Resources/doc/index.md)

    ## Make migrations ##

    We have finally finished to configure our bundles. Now we can start to see oauth2 code flows, but first we need to create migrations!

    So let's open your terminal and run:

    php bin/console make:migrations
    php bin/console doctrine:migration:migrate

    Then we need to create a user, so again open your terminal and run:

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

    ## Security ##
    In the `config/packages/security.yaml` file insert these configuration options:

    @@ -443,8 +426,25 @@ In the `config/packages/security.yaml` file insert these configuration options:
    - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

    ## Make migrations ##

    We have finally finished to configure our bundles. Now we can start to see oauth2 code flows, but first we need to create migrations!

    So let's open your terminal and run:

    php bin/console make:migration
    php bin/console doctrine:migration:migrate

    Then we need to create a user, so again open your terminal and run:

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

    ## Create an ApiController ##
    Create an ApiController that will serve our resouces. In this example we will create a simple API that returns all users.
    Create an ApiController that will serve our resources. In this example we will create a simple API that returns all users.

    <?php

    @@ -460,7 +460,7 @@ Create an ApiController that will serve our resouces. In this example we will cr
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\Routing\Annotation\Route;
    use FOS\RestBundle\Controller\FOSRestController;
    use Swagger\Annotations\Swagger as SWG;
    use Swagger\Annotations as SWG;

    class ApiController extends FOSRestController
    {
    @@ -566,7 +566,7 @@ In my case the client created by the bundle has these data:
    | 1 | 3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck | 4t1922al57ack8wckcgoc84ko4oogogcoso40kwwkc4csw4cwk |
    +------------+---------------------------------------------------------+------------------------------------------------------+

    client_id: `1_3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck` (id + random_id)
    client_id: `1_3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck` (id + random_id) <br/>
    secret: `4t1922al57ack8wckcgoc84ko4oogogcoso40kwwkc4csw4cwk`

    Then we need to create a `SecurityController` with two routes: `/oauth/v2/auth_login` and `/oauth/v2/auth_login_check`. These are the routes that we have specified above in the `oauth_authorize` firewall inside the `security.yaml` file.
    @@ -644,6 +644,9 @@ Then we need to create a `SecurityController` with two routes: `/oauth/v2/auth_l
    }
    }

    To complete the `SecurityController` we need to have a login page where the user will be redirect if he isn't already logged in. For simplicity let's copy the `FOSUserBundle` login template files. You can find them inside the directory `vendor/friendsofsymfony/user-bundle/Resources/views`. From that directory you should copy the directory `Security` and the file `layout.html.twig` and put them inside the directory `templates/bundles/FOSUserBundle`. More information about the overriding of templates you can find here: [https://symfony.com/doc/current/bundles/override.html](https://symfony.com/doc/current/bundles/override.html)
    Now open the file `templates/bundles/FOSUserBundle/Security/login_content.html.twig` and in the action of form replace the path `fos_user_security_check` with `auth_login_check`.

    To verify that all works properly, we should call this route: `http://localhost:8000/oauth/v2/auth?client_id=1_3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck&redirect_uri=http://localhost:8000/redirect-uri-example&response_type=code`

    The client_id query parameter should be replaced by the client_id created previously. The response should be a page with two buttons: "allow" and "deny". After clicking in the "allow" button you will redirect to the `redirect_uri`. In the url you can notice a code query parameter, that is our authorization code! Now we need to exchange that code with an `access_token`. So let's create a request to our `/oauth/v2/token` route:
  7. mirkorap revised this gist Nov 13, 2018. No changes.
  8. mirkorap revised this gist Nov 13, 2018. 1 changed file with 15 additions and 15 deletions.
    30 changes: 15 additions & 15 deletions symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -1,21 +1,21 @@
    # How to implement a REST APIs with Symfony 4 + FOSRestBundle + FOSUserBundle + FOSOauthServerBundle using main Oauth2 code flows #

    ## Table of contents ##
    1. [introduction](#introduction "Introduction")
    2. [install-bundles](#install-bundles "Install bundles")
    3. [configuration](#configuration "Configuration")
    1. [fosrestbundle](#fosrestbundle "FOSRestBundle")
    2. [jmsserializerbundle](#jmsserializerbundle "JMSSerializerBundle")
    3. [nelmioapidocbundle](#nelmioapidocbundle "NelmioApiDocBundle")
    4. [fosuserbundle](#fosuserbundle "FOSUserBundle")
    5. [fosoauthserverbundle](#fosoauthserverbundle "FOSOAuthServerBundle")
    4. [make-migrations](#make-migrations "Make migrations")
    5. [security](#security "Security")
    6. [create-an-apicontroller](#create-an-apicontroller "Create an ApiController")
    7. [authorization-code](#authorization-code "Authorization code")
    1. [refresh-token](#refresh-token "Refresh token")
    8. [implicit](#implicit "Implicit")
    9. [password-credentials](#password-credentials "Password credentials")
    1. [Introduction](#introduction "Introduction")
    2. [Install bundles](#install-bundles "Install bundles")
    3. [Configuration](#configuration "Configuration")
    1. [FOSRestBundle](#fosrestbundle "FOSRestBundle")
    2. [JMSSerializerBundle](#jmsserializerbundle "JMSSerializerBundle")
    3. [NelmioApiDocBundle](#nelmioapidocbundle "NelmioApiDocBundle")
    4. [FOSUserBundle](#fosuserbundle "FOSUserBundle")
    5. [FOSOAuthServerBundle](#fosoauthserverbundle "FOSOAuthServerBundle")
    4. [Make migrations](#make-migrations "Make migrations")
    5. [Security](#security "Security")
    6. [Create an ApiController](#create-an-apicontroller "Create an ApiController")
    7. [Authorization code](#authorization-code "Authorization code")
    1. [Refresh token](#refresh-token "Refresh token")
    8. [Implicit](#implicit "Implicit")
    9. [Password credentials](#password-credentials "Password credentials")

    ## Introduction ##
    In this gist I will explain you how to create a basic REST APIs system with Symfony 4 and FOSOauthServerBundle using main Oauth2 code flows.
  9. mirkorap revised this gist Nov 13, 2018. 1 changed file with 15 additions and 1 deletion.
    16 changes: 15 additions & 1 deletion symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,21 @@
    # How to implement a REST APIs with Symfony 4 + FOSRestBundle + FOSUserBundle + FOSOauthServerBundle using main Oauth2 code flows #

    ## Table of contents ##
    1. [Introduction](#Introduction "Introduction")
    1. [introduction](#introduction "Introduction")
    2. [install-bundles](#install-bundles "Install bundles")
    3. [configuration](#configuration "Configuration")
    1. [fosrestbundle](#fosrestbundle "FOSRestBundle")
    2. [jmsserializerbundle](#jmsserializerbundle "JMSSerializerBundle")
    3. [nelmioapidocbundle](#nelmioapidocbundle "NelmioApiDocBundle")
    4. [fosuserbundle](#fosuserbundle "FOSUserBundle")
    5. [fosoauthserverbundle](#fosoauthserverbundle "FOSOAuthServerBundle")
    4. [make-migrations](#make-migrations "Make migrations")
    5. [security](#security "Security")
    6. [create-an-apicontroller](#create-an-apicontroller "Create an ApiController")
    7. [authorization-code](#authorization-code "Authorization code")
    1. [refresh-token](#refresh-token "Refresh token")
    8. [implicit](#implicit "Implicit")
    9. [password-credentials](#password-credentials "Password credentials")

    ## Introduction ##
    In this gist I will explain you how to create a basic REST APIs system with Symfony 4 and FOSOauthServerBundle using main Oauth2 code flows.
  10. mirkorap revised this gist Nov 13, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    # How to implement a REST APIs with Symfony 4 + FOSRestBundle + FOSUserBundle + FOSOauthServerBundle using main Oauth2 code flows #

    ## Table of contents ##
    1. [#Introduction](Introduction "Introduction")
    1. [Introduction](#Introduction "Introduction")

    ## Introduction ##
    In this gist I will explain you how to create a basic REST APIs system with Symfony 4 and FOSOauthServerBundle using main Oauth2 code flows.
  11. mirkorap revised this gist Nov 13, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    # How to implement a REST APIs with Symfony 4 + FOSRestBundle + FOSUserBundle + FOSOauthServerBundle using main Oauth2 code flows #

    ## Table of contents ##
    1. [Introduction](Introduction "Introduction")
    1. [#Introduction](Introduction "Introduction")

    ## Introduction ##
    In this gist I will explain you how to create a basic REST APIs system with Symfony 4 and FOSOauthServerBundle using main Oauth2 code flows.
  12. mirkorap revised this gist Nov 13, 2018. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,8 @@
    # How to implement a REST APIs with Symfony 4 + FOSRestBundle + FOSUserBundle + FOSOauthServerBundle using main Oauth2 code flows #

    ## Table of contents ##
    1. [Introduction](Introduction "Introduction")

    ## Introduction ##
    In this gist I will explain you how to create a basic REST APIs system with Symfony 4 and FOSOauthServerBundle using main Oauth2 code flows.
    The flows that we implement will be:
  13. mirkorap revised this gist Nov 13, 2018. 1 changed file with 381 additions and 7 deletions.
    388 changes: 381 additions & 7 deletions symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -161,11 +161,11 @@ In the `config/routes.yaml` file insert these route options:
    fos_oauth_server_token:
    resource: "@FOSOAuthServerBundle/Resources/config/routing/token.xml"

    # Add this route only if you are using the Authorization code flow
    # Add this route only if you are going to use the Authorization code flow
    fos_oauth_server_authorize:
    resource: "@FOSOAuthServerBundle/Resources/config/routing/authorize.xml"

    Now we need to create the entities that we have just specified in the `fos_oauth_server.yaml` configuration file:
    Now we need to create the entities that we have just specified in the `fos_oauth_server.yaml` file:

    #### App\Entity\Client ####

    @@ -294,7 +294,6 @@ Now we need to create the entities that we have just specified in the `fos_oauth
    }
    }


    #### App\Entity\AuthCode ####

    <?php
    @@ -346,24 +345,26 @@ Now we need to create the entities that we have just specified in the `fos_oauth

    Above you can see the annotation `@ORM\AttributeOverrides`. This is required to solve the error: `SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes` during migrations.

    ### Make migrations ###
    Documentation: [https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/blob/master/Resources/doc/index.md](https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/blob/master/Resources/doc/index.md)

    ## Make migrations ##

    Now that we have finally finished to configure our bundles we can start to see oauth2 code flows, but first we need to create migrations!
    We have finally finished to configure our bundles. Now we can start to see oauth2 code flows, but first we need to create migrations!

    So let's open your terminal and run:

    php bin/console make:migrations
    php bin/console doctrine:migration:migrate

    Now we need to create our user, so again open your terminal and run:
    Then we need to create a user, so again open your terminal and run:

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

    ### Security ###
    ## Security ##
    In the `config/packages/security.yaml` file insert these configuration options:

    security:
    @@ -424,3 +425,376 @@ In the `config/packages/security.yaml` file insert these configuration options:
    - { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

    ## Create an ApiController ##
    Create an ApiController that will serve our resouces. In this example we will create a simple API that returns all users.

    <?php

    namespace App\Controller;

    use App\Entity\User;
    use App\Repository\UserRepository;
    use FOS\RestBundle\Controller\Annotations as Rest;
    use FOS\RestBundle\View\View;
    use Nelmio\ApiDocBundle\Annotation\Model;
    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\Routing\Annotation\Route;
    use FOS\RestBundle\Controller\FOSRestController;
    use Swagger\Annotations\Swagger as SWG;

    class ApiController extends FOSRestController
    {

    /**
    * List all users
    * @SWG\Parameter(
    * in="query",
    * type="number",
    * minimum="0",
    * name="offset",
    * description="Offset from which to start listing users.",
    * default="0"
    * )
    * @SWG\Parameter(
    * in="query",
    * type="integer",
    * name="limit",
    * description="How many users to return.",
    * default="10"
    * )
    * @SWG\Response(
    * response="200",
    * description="Success",
    * @SWG\Schema(
    * type="array",
    * @Model(type=User::class)
    * )
    * )
    * @SWG\Response(
    * response="401",
    * description="Unauthorized"
    * )
    * @Rest\Get("/api/users")
    *
    * @param Request $request
    * @return View
    */
    public function index(Request $request)
    {
    $offset = $request->query->get('offset', 0);
    $limit = $request->query->get('limit', 10);

    /* @var UserRepository $userRepository */
    $userRepository = $this->getDoctrine()->getRepository(User::class);
    $users = $userRepository->getAllUsers($offset, $limit);

    return $this->view($users);
    }
    }

    In the `src/Repository/UserRepository` let's add a `getAllUsers` function:

    <?php

    namespace App\Repository;

    use App\Entity\User;
    use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
    use Symfony\Bridge\Doctrine\RegistryInterface;

    /**
    * @method User|null find($id, $lockMode = null, $lockVersion = null)
    * @method User|null findOneBy(array $criteria, array $orderBy = null)
    * @method User[] findAll()
    * @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
    */
    class UserRepository extends ServiceEntityRepository
    {
    public function __construct(RegistryInterface $registry)
    {
    parent::__construct($registry, User::class);
    }

    /**
    * @param int $offset
    * @param int $limit
    * @return mixed
    */
    public function getAllUsers(int $offset = 0, int $limit = 10)
    {
    return $this->createQueryBuilder('u')
    ->setMaxResults($limit)
    ->setFirstResult($offset)
    ->getQuery()
    ->getResult();
    }

    }

    ## Authorization code ##
    Finally (after a lot of configuration) we can start to see the integration of oauth2 code flows with Symfony! Let's start with the Authorization code flow.

    First of all we need to create a client, so open your terminal and run:

    `php bin/console fos:oauth-server:create-client --redirect-uri="http://localhost:8000/redirect-uri-example" --grant-type="authorization_code"`

    In my case the client created by the bundle has these data:

    +------------+---------------------------------------------------------+------------------------------------------------------+
    | id | random_id | secret |
    +------------+---------------------------------------------------------+------------------------------------------------------+
    | 1 | 3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck | 4t1922al57ack8wckcgoc84ko4oogogcoso40kwwkc4csw4cwk |
    +------------+---------------------------------------------------------+------------------------------------------------------+

    client_id: `1_3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck` (id + random_id)
    secret: `4t1922al57ack8wckcgoc84ko4oogogcoso40kwwkc4csw4cwk`

    Then we need to create a `SecurityController` with two routes: `/oauth/v2/auth_login` and `/oauth/v2/auth_login_check`. These are the routes that we have specified above in the `oauth_authorize` firewall inside the `security.yaml` file.

    <?php

    namespace App\Controller;

    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\HttpFoundation\Session\Session;
    use Symfony\Component\Routing\Annotation\Route;
    use Symfony\Component\Security\Core\Exception\AuthenticationException;
    use Symfony\Component\Security\Core\Security;
    use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;

    class SecurityController extends AbstractController
    {

    private $tokenManager;

    public function __construct(CsrfTokenManagerInterface $tokenManager = null)
    {
    $this->tokenManager = $tokenManager;
    }

    /**
    * @Route("/oauth/v2/auth_login", name="auth_login")
    * @param Request $request
    * @return Response
    */
    public function loginAction(Request $request)
    {
    /** @var $session Session */
    $session = $request->getSession();

    $authErrorKey = Security::AUTHENTICATION_ERROR;
    $lastUsernameKey = Security::LAST_USERNAME;

    if ($request->attributes->has($authErrorKey)) {
    $error = $request->attributes->get($authErrorKey);
    } elseif (null !== $session && $session->has($authErrorKey)) {
    $error = $session->get($authErrorKey);
    $session->remove($authErrorKey);
    } else {
    $error = null;
    }

    if (!$error instanceof AuthenticationException) {
    $error = null;
    }

    $lastUsername = (null === $session) ? '' : $session->get($lastUsernameKey);

    $csrfToken = $this->tokenManager ? $this->tokenManager->getToken('authenticate')->getValue() : null;

    $securityCheckPath = $this->generateUrl('auth_login_check');

    return $this->render('@FOSUser/Security/login.html.twig', array(
    'last_username' => $lastUsername,
    'error' => $error,
    'csrf_token' => $csrfToken,
    'security_check_path' => $securityCheckPath,
    ));
    }

    /**
    * @Route("/oauth/v2/auth_login_check", name="auth_login_check")
    * @param Request $request
    */
    public function loginCheckAction(Request $request)
    {

    }
    }

    To verify that all works properly, we should call this route: `http://localhost:8000/oauth/v2/auth?client_id=1_3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck&redirect_uri=http://localhost:8000/redirect-uri-example&response_type=code`

    The client_id query parameter should be replaced by the client_id created previously. The response should be a page with two buttons: "allow" and "deny". After clicking in the "allow" button you will redirect to the `redirect_uri`. In the url you can notice a code query parameter, that is our authorization code! Now we need to exchange that code with an `access_token`. So let's create a request to our `/oauth/v2/token` route:

    http POST http://localhost:8000/oauth/v2/token \
    grant_type=authorization_code \
    client_id=1_3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck \
    client_secret=4t1922al57ack8wckcgoc84ko4oogogcoso40kwwkc4csw4cwk \
    code=NWVmMDY4NjE3OWUwOTI4MmZkN2FiNmI4NTgwMGNhNzg3ZTRjYmMzZmI5MmUzMWFiYzk0YWJjMmZiM2MxNmExZQ \
    redirect_uri=http://localhost:8000/redirect-uri-example \
    HTTP/1.1 200 OK
    Cache-Control: no-store, private
    Connection: close
    Content-Type: application/json

    The response should looks like this:

    {
    "access_token": "ZTFiZWIwMzI4ZTg4MmYzNTExNjkxMzYwODBlMzQ3NTgxNzM1ODMxYTQzNGZkZDI0ZjQ4MmRkN2ZjMDRhMWU2YQ",
    "expires_in": 3600,
    "token_type": "bearer",
    "scope": null,
    "refresh_token": "NDFiNjY3MWU3YmZhMDkxNjI5NjBjZDhhNmYzNmJkYjI1NTIzMGJiZTQzYTA0NzlkYjU2OTgzYmM5MzZlODg2NQ"
    }

    Now we can finally call our APIs! Create a request to the route: `localhost:8000/api/users` and pass in the header the `access_token`:

    http GET localhost:8000/api/users \
    "Authorization:Bearer ZTFiZWIwMzI4ZTg4MmYzNTExNjkxMzYwODBlMzQ3NTgxNzM1ODMxYTQzNGZkZDI0ZjQ4MmRkN2ZjMDRhMWU2YQ"
    HTTP/1.1 200 OK
    Cache-Control: no-cache
    Connection: close
    Content-Type: application/json

    The response should looks like this:

    [
    {
    "id": 1,
    "username": "admin",
    "username_canonical": "admin",
    "email": "admin@example.com",
    "email_canonical": "admin@example.com",
    "enabled": true,
    "password": "$2y$13$HMApz6BLBHXbsLx2GZWJcOTxx3bqv2yKWg7vst5c7C10SXpNgVlE.",
    "last_login": "2018-11-11T20:40:54+01:00",
    "groups": [],
    "roles": []
    }
    ]

    ### Refresh token ###
    In the authorization code flow (and in the password credentials flow too) you gain another important token, the `refresh_token`! This token help you to refresh the `access_token` when expires, without having to ask the user to re-grant access to his data to the application he is using. To use the `refresh_token` you need to add it as `grant_type` during the creation of client:

    `php bin/console fos:oauth-server:create-client --redirect-uri="http://localhost:8000/redirect-uri-example" --grant-type="authorization_code" --grant-type="refresh_token"`

    Then to get a new `access_token` you need to call the route: `/oauth/v2/token` with the `refresh_token` in the body of request:

    http POST http://localhost:8000/oauth/v2/token \
    grant_type=refresh_token \
    client_id=1_3pwul4sg6ge8s444og4wk4g88480cosowosg0k080owo0o0wck \
    client_secret=4t1922al57ack8wckcgoc84ko4oogogcoso40kwwkc4csw4cwk \
    refresh_token=YzFlMjQ3ZjRkZTVmZTA5ODZhZmI0YzA4Y2Y5YzY4ZmEwYjNhNTNhNWY0YTVkYmRiZjUxZjBlZTQ1NWNlZjY4OQ \
    redirect_uri=http://localhost:8000/redirect-uri-example \
    HTTP/1.1 200 OK
    Cache-Control: no-store, private
    Connection: close
    Content-Type: application/json

    ## Implicit ##
    This flow is principally used in SPA. A difference from the previous flow is that we will receive an access_token immediately, so we don't need to exchange an authorization code for an access_token. You may think "Why I should use the Authorization code flow if the Implicit one is more simple?". The answer is easy, you MUST use the authorization code flow if you need to call API even when the user is offline. In fact with the Implicit flow you will not receive a refresh_token (for security reason), so when the access_token expire the user must authorize access again. For this reason this flow is more suitable in the SPA. Let's see how it works:

    Again we need to create a client, so open your terminal and run:

    `php bin/console fos:oauth-server:create-client --redirect-uri="http://localhost:8000/redirect-uri-example" --grant-type="implicit"`

    In my case the client created by the bundle has these data:

    +------------+---------------------------------------------------------+------------------------------------------------------+
    | id | random_id | secret |
    +------------+---------------------------------------------------------+------------------------------------------------------+
    | 2 | 2shspbzhca2ogw44kckw00s4s800g0s484004ccokcw8gogwks | 3fcdpac3iw6cw8k8gs48kwc8ows4s4wkcg8gwgsgck84g48k0s |
    +------------+---------------------------------------------------------+------------------------------------------------------+

    client_id: `2_2shspbzhca2ogw44kckw00s4s800g0s484004ccokcw8gogwks` (id + random_id)
    secret: `3fcdpac3iw6cw8k8gs48kwc8ows4s4wkcg8gwgsgck84g48k0s`

    With these data we can call immediately the route `http://localhost:8000/oauth/v2/auth?client_id=2_2shspbzhca2ogw44kckw00s4s800g0s484004ccokcw8gogwks&redirect_uri=http://localhost:8000/redirect-uri-example&response_type=token`

    The client_id query parameter should be replaced by the client_id created previously. The response should be a page with two buttons: "allow" and "deny". After clicking in the "allow" button you will redirect to the `redirect_uri`. In the url you can notice an hash with the access_token. Now we can call our API to get all users (remember to pass a Bearer header with the access_token):

    http GET localhost:8000/api/users \
    "Authorization:Bearer NDYxMmUwZGI5OTQ4Y2UzNDQ5M2JhY2YwYmU3YmM2NDlkMDUyNTk4MjE2N2ZhNDU2OWQ2MTc4NjM1MmYxMDBmOQ"
    HTTP/1.1 200 OK
    Cache-Control: no-cache
    Connection: close
    Content-Type: application/json

    The response should looks like this:

    [
    {
    "id": 1,
    "username": "admin",
    "username_canonical": "admin",
    "email": "admin@example.com",
    "email_canonical": "admin@example.com",
    "enabled": true,
    "password": "$2y$13$HMApz6BLBHXbsLx2GZWJcOTxx3bqv2yKWg7vst5c7C10SXpNgVlE.",
    "last_login": "2018-11-11T20:40:54+01:00",
    "groups": [],
    "roles": []
    }
    ]

    ## Password credentials ##
    This flow is used when we need to serve resources to a trusted application. Normally is used when the application that will use our APIs is our application itself. During the request to retrieve the `access_token`, the external application must send the username and password too. Let's start to see how it works:

    Again we need to create a client, so open your terminal and run:

    `php bin/console fos:oauth-server:create-client --redirect-uri="http://localhost:8000/redirect-uri-example" --grant-type="password"`

    In my case the client created by the bundle has these data:

    +------------+---------------------------------------------------------+------------------------------------------------------+
    | id | random_id | secret |
    +------------+---------------------------------------------------------+------------------------------------------------------+
    | 3 | 492ohmxbo3k0gggwg08wso4cs0k4sswcwg8wo8so0cgo4cwo0s | 64jack8z62888k4sgcgssow080wcgsk04408wogkc4owgkgokc |
    +------------+---------------------------------------------------------+------------------------------------------------------+

    client_id: `3_492ohmxbo3k0gggwg08wso4cs0k4sswcwg8wo8so0cgo4cwo0s` (id + random_id)
    secret: `64jack8z62888k4sgcgssow080wcgsk04408wogkc4owgkgokc`

    With these data we can call the route to get the `access_token`, but this time we should send also username and password!

    http POST http://localhost:8000/oauth/v2/token \
    grant_type=password \
    client_id=3_492ohmxbo3k0gggwg08wso4cs0k4sswcwg8wo8so0cgo4cwo0s \
    client_secret=64jack8z62888k4sgcgssow080wcgsk04408wogkc4owgkgokc \
    username=admin \
    password=admin
    HTTP/1.1 200 OK
    Cache-Control: no-store, private
    Connection: close
    Content-Type: application/json

    The username and password that you see above come from the user created by FOSUserBundle command `php bin/console fos:user:create`. Now we can call our APIs

    http GET localhost:8000/api/users \
    "Authorization:Bearer MmQ5MzRlOTRhNWUzMzY0ZGU1ZjMzMjkwY2I0YjlhN2NlOTBhZjg5NGJjMGM5Yzk2ZGFiYWI5YTBkYjBiMWIyNw"
    HTTP/1.1 200 OK
    Cache-Control: no-cache
    Connection: close
    Content-Type: application/json

    The response should looks like this:

    [
    {
    "id": 1,
    "username": "admin",
    "username_canonical": "admin",
    "email": "admin@example.com",
    "email_canonical": "admin@example.com",
    "enabled": true,
    "password": "$2y$13$HMApz6BLBHXbsLx2GZWJcOTxx3bqv2yKWg7vst5c7C10SXpNgVlE.",
    "last_login": "2018-11-11T20:40:54+01:00",
    "groups": [],
    "roles": []
    }
    ]
  14. mirkorap revised this gist Nov 10, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -418,7 +418,7 @@ In the `config/packages/security.yaml` file insert these configuration options:
    anonymous: true

    access_control:
    - { path: ^/oauth/v2/auth_login$, role: IS_AUTHENTICATED_ANONYMOUSLY } # This control is necessary only in the Authorization code flow
    - { path: ^/oauth/v2/auth_login$, role: IS_AUTHENTICATED_ANONYMOUSLY } # Add this only in the Authorization code flow
    - { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
    - { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
  15. mirkorap revised this gist Nov 10, 2018. 1 changed file with 294 additions and 7 deletions.
    301 changes: 294 additions & 7 deletions symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -25,6 +25,8 @@ Below I will explain for each bundle what they do and how to configure them.
    ## Configuration ##

    ### FOSRestBundle ###
    FOSRestBundle allow us to configure our APIs and serve resources in different format in an easy way. There a different configuration options that you can use, but in our example we need to serve resources in json/xml format for all routes starting with `/api`, instead for all others we will serve simple html pages.

    In the `config/packages/fos_rest.yaml` file insert these configuration options:

    fos_rest:
    @@ -41,29 +43,29 @@ In the `config/packages/fos_rest.yaml` file insert these configuration options:
    view:
    view_response_listener: true

    With the rules above our application will serve for the routes starting with /api only resources formatted in json/xml, instead for all remaining routes it will render simple html pages. The configuration option `view_response_listener` makes it possible to simply return a `View` instance from action controllers.

    Documentation: [https://symfony.com/doc/master/bundles/FOSRestBundle/index.html](https://symfony.com/doc/master/bundles/FOSRestBundle/index.html)

    ### JMSSerializerBundle ###
    JMSSerializerBundle allows you to serialize your data into a requested output format such as JSON, XML, or YAML.

    In the `config/packages/jms_serializer.yaml` file insert these configuration options:

    jms_serializer:
    visitors:
    xml:
    format_output: '%kernel.debug%'

    JMSSerializerBundle allows you to serialize your data into a requested output format such as JSON, XML, or YAML.

    Documentation: [https://jmsyst.com/bundles/JMSSerializerBundle](https://jmsyst.com/bundles/JMSSerializerBundle)

    ### NelmioApiDocBundle ###
    NelmioApiDocBundle is a Symfony's bundle that allow us to generate documentation for our APIs.

    In the `config/packages/nelmio_api_doc.yaml` file insert these configuration options:

    nelmio_api_doc:
    documentation:
    info:
    title: Oauth Server App
    title: Oauth2 Server App
    description: This is my oauth2 server app
    version: 1.0.0

    @@ -83,11 +85,11 @@ In the `config/routes.yaml` file insert this route option:
    resource: "@NelmioApiDocBundle/Resources/config/routing/swaggerui.xml"
    prefix: /api/doc

    NelmioApiDocBundle is a Symfony's bundle that allow us to generate documentation for our APIs.

    Documentation: [https://symfony.com/doc/current/bundles/NelmioApiDocBundle/index.html](https://symfony.com/doc/current/bundles/NelmioApiDocBundle/index.html)

    ### FOSUserBundle ###
    FOSUserBundle allow us to manage users (store and load users, register and authenticate users etc.)

    In the `config/packages/fos_user.yaml` file insert these configuration options:

    fos_user:
    @@ -137,3 +139,288 @@ Now let's to create our entity User class:
    return $this->id;
    }
    }

    Documentation: [https://symfony.com/doc/current/bundles/FOSUserBundle/index.html](https://symfony.com/doc/current/bundles/FOSUserBundle/index.html)

    ### FOSOAuthServerBundle ###
    FOSOAuthServerBundle is a Symfony bundle that allow us to create our oauth2 server app. In this gist I will exaplain you how to use it with the main oauth2 code flows (Authorization code, Implicit, Password credentials). Let's take a look to the configuration options:

    In the `config/packages/fos_oauth_server.yaml` file insert these configuration options:

    fos_oauth_server:
    db_driver: orm
    client_class: App\Entity\Client
    access_token_class: App\Entity\AccessToken
    refresh_token_class: App\Entity\RefreshToken
    auth_code_class: App\Entity\AuthCode
    service:
    user_provider: fos_user.user_provider.username

    In the `config/routes.yaml` file insert these route options:

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

    # Add this route only if you are using the Authorization code flow
    fos_oauth_server_authorize:
    resource: "@FOSOAuthServerBundle/Resources/config/routing/authorize.xml"

    Now we need to create the entities that we have just specified in the `fos_oauth_server.yaml` configuration file:

    #### App\Entity\Client ####

    <?php

    namespace App\Entity;

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

    /**
    * @ORM\Table("oauth2_clients")
    * @ORM\Entity(repositoryClass="App\Repository\ClientRepository")
    */
    class Client extends BaseClient
    {

    /**
    * @ORM\Id()
    * @ORM\GeneratedValue(strategy="AUTO")
    * @ORM\Column(type="integer")
    */
    protected $id;

    public function getId(): ?int
    {
    return $this->id;
    }
    }

    #### App\Entity\AccessToken ####

    <?php

    namespace App\Entity;

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

    /**
    * @ORM\Table("oauth2_access_tokens")
    * @ORM\Entity(repositoryClass="App\Repository\AccessTokenRepository")
    * @ORM\AttributeOverrides({
    * @ORM\AttributeOverride(name="token",
    * column=@ORM\Column(
    * name = "token",
    * type = "string",
    * length = 128
    * )
    * )
    * })
    */
    class AccessToken extends BaseAccessToken
    {

    /**
    * @ORM\Id()
    * @ORM\GeneratedValue(strategy="AUTO")
    * @ORM\Column(type="integer")
    */
    protected $id;

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

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

    public function getId(): ?int
    {
    return $this->id;
    }
    }

    #### App\Entity\RefreshToken ####

    <?php

    namespace App\Entity;

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

    /**
    * @ORM\Table("oauth2_refresh_tokens")
    * @ORM\Entity(repositoryClass="App\Repository\RefreshTokenRepository")
    * @ORM\AttributeOverrides({
    * @ORM\AttributeOverride(name="token",
    * column=@ORM\Column(
    * name = "token",
    * type = "string",
    * length = 128
    * )
    * )
    * })
    */
    class RefreshToken extends BaseRefreshToken
    {

    /**
    * @ORM\Id()
    * @ORM\GeneratedValue(strategy="AUTO")
    * @ORM\Column(type="integer")
    */
    protected $id;

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

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

    public function getId(): ?int
    {
    return $this->id;
    }
    }


    #### App\Entity\AuthCode ####

    <?php

    namespace App\Entity;

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

    /**
    * @ORM\Table("oauth2_auth_codes")
    * @ORM\Entity(repositoryClass="App\Repository\AuthCodeRepository")
    * @ORM\AttributeOverrides({
    * @ORM\AttributeOverride(name="token",
    * column=@ORM\Column(
    * name = "token",
    * type = "string",
    * length = 128
    * )
    * )
    * })
    */
    class AuthCode extends BaseAuthCode
    {

    /**
    * @ORM\Id()
    * @ORM\GeneratedValue(strategy="AUTO")
    * @ORM\Column(type="integer")
    */
    protected $id;

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

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

    public function getId(): ?int
    {
    return $this->id;
    }
    }

    Above you can see the annotation `@ORM\AttributeOverrides`. This is required to solve the error: `SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes` during migrations.

    ### Make migrations ###

    Now that we have finally finished to configure our bundles we can start to see oauth2 code flows, but first we need to create migrations!

    So let's open your terminal and run:

    php bin/console make:migrations
    php bin/console doctrine:migration:migrate

    Now we need to create our user, so again open your terminal and run:

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

    ### Security ###
    In the `config/packages/security.yaml` file insert these configuration options:

    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:
    dev:
    pattern: ^/(_(profiler|wdt)|css|images|js)/
    security: false

    oauth_token:
    pattern: ^/oauth/v2/token
    security: false

    # Add this firewall only in the Authorization code flow
    oauth_authorize:
    pattern: ^/oauth/v2/auth
    form_login:
    provider: fos_userbundle
    check_path: /oauth/v2/auth_login_check
    login_path: /oauth/v2/auth_login
    anonymous: true

    api_doc:
    pattern: ^/api/doc
    fos_oauth: false
    stateless: true
    anonymous: true

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

    main:
    pattern: ^/
    form_login:
    provider: fos_userbundle
    csrf_token_generator: security.csrf.token_manager
    logout: true
    fos_oauth: false
    anonymous: true

    access_control:
    - { path: ^/oauth/v2/auth_login$, role: IS_AUTHENTICATED_ANONYMOUSLY } # This control is necessary only in the Authorization code flow
    - { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
    - { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
  16. mirkorap revised this gist Nov 6, 2018. 1 changed file with 139 additions and 1 deletion.
    140 changes: 139 additions & 1 deletion symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -1 +1,139 @@
    TODO
    # How to implement a REST APIs with Symfony 4 + FOSRestBundle + FOSUserBundle + FOSOauthServerBundle using main Oauth2 code flows #

    ## Introduction ##
    In this gist I will explain you how to create a basic REST APIs system with Symfony 4 and FOSOauthServerBundle using main Oauth2 code flows.
    The flows that we implement will be:
    1. Authorization code
    2. Implicit
    3. Password credentials

    I will separate the gist into two configuration parts. The first part is the common configuration that is equal for all Oauth2 code flows above. In the second part I will explain, for each code flows above, the basic configuration type that you need. So let's coding!

    ## Install bundles ##
    The first step is to download Symfony and the related bundles.

    composer create-project symfony/skeleton oauth2-server
    cd oauth2-server
    composer require friendsofsymfony/rest-bundle
    composer require jms/serializer-bundle
    composer require nelmio/api-doc-bundle
    composer require friendsofsymfony/user-bundle "~2.0@dev"
    composer require friendsofsymfony/oauth-server-bundle

    Below I will explain for each bundle what they do and how to configure them.

    ## Configuration ##

    ### FOSRestBundle ###
    In the `config/packages/fos_rest.yaml` file insert these configuration options:

    fos_rest:
    routing_loader:
    default_format: html
    include_format: true

    format_listener:
    enabled: true
    rules:
    - { path: '^/api', priorities: ['json', 'xml'], fallback_format: json, prefer_extension: false }
    - { path: '^/', priorities: ['html'], fallback_format: html, prefer_extension: false }

    view:
    view_response_listener: true

    With the rules above our application will serve for the routes starting with /api only resources formatted in json/xml, instead for all remaining routes it will render simple html pages. The configuration option `view_response_listener` makes it possible to simply return a `View` instance from action controllers.

    Documentation: [https://symfony.com/doc/master/bundles/FOSRestBundle/index.html](https://symfony.com/doc/master/bundles/FOSRestBundle/index.html)

    ### JMSSerializerBundle ###
    In the `config/packages/jms_serializer.yaml` file insert these configuration options:

    jms_serializer:
    visitors:
    xml:
    format_output: '%kernel.debug%'

    JMSSerializerBundle allows you to serialize your data into a requested output format such as JSON, XML, or YAML.

    Documentation: [https://jmsyst.com/bundles/JMSSerializerBundle](https://jmsyst.com/bundles/JMSSerializerBundle)

    ### NelmioApiDocBundle ###
    In the `config/packages/nelmio_api_doc.yaml` file insert these configuration options:

    nelmio_api_doc:
    documentation:
    info:
    title: Oauth Server App
    description: This is my oauth2 server app
    version: 1.0.0

    securityDefinitions:
    Bearer:
    type: apiKey
    description: 'Value: Bearer {access_token}'
    name: Authorization
    in: header
    areas:
    path_patterns:
    - ^/api(?!/doc$)

    In the `config/routes.yaml` file insert this route option:

    NelmioApiDocBundle:
    resource: "@NelmioApiDocBundle/Resources/config/routing/swaggerui.xml"
    prefix: /api/doc

    NelmioApiDocBundle is a Symfony's bundle that allow us to generate documentation for our APIs.

    Documentation: [https://symfony.com/doc/current/bundles/NelmioApiDocBundle/index.html](https://symfony.com/doc/current/bundles/NelmioApiDocBundle/index.html)

    ### FOSUserBundle ###
    In the `config/packages/fos_user.yaml` file insert these configuration options:

    fos_user:
    db_driver: orm
    user_class: App\Entity\User
    firewall_name: main

    from_email:
    address: john@example.com
    sender_name: john@example.com

    In the `config/routes.yaml` file insert this route option:

    fos_user:
    resource: "@FOSUserBundle/Resources/config/routing/all.xml"

    In the `config/packages/framework.yaml` file insert this line at the end of file:

    templating:
    engines: twig

    Now let's to create our entity User class:

    <?php

    namespace App\Entity;

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

    /**
    * @ORM\Table("users")
    * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
    */
    class User extends BaseUser
    {

    /**
    * @ORM\Id()
    * @ORM\GeneratedValue(strategy="AUTO")
    * @ORM\Column(type="integer")
    */
    protected $id;

    public function getId(): ?int
    {
    return $this->id;
    }
    }
  17. mirkorap created this gist Nov 5, 2018.
    1 change: 1 addition & 0 deletions symfony4-fos-oauth-server-bundle.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    TODO