How to implement a REST APIs with Symfony 4 + FOSRestBundle + FOSUserBundle + FOSOauthServerBundle using main Oauth2 code flows
- Introduction
- Install bundles
- Configuration
- Security
- Make migrations
- Create an ApiController
- Authorization code
- Implicit
- Password credentials
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:
- Authorization code
- Implicit
- 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 flow, the basic configuration type that you need. So let's coding!
The first step is to download Symfony and the related bundles.
composer create-project symfony/website-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.
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:
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
Documentation: https://symfony.com/doc/master/bundles/FOSRestBundle/index.html
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%'
Documentation: https://jmsyst.com/bundles/JMSSerializerBundle
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:
nelmio_api_doc:
documentation:
info:
title: Oauth2 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
Documentation: https://symfony.com/doc/current/bundles/NelmioApiDocBundle/index.html
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!):
fos_user:
db_driver: orm
user_class: App\Entity\User
firewall_name: main
from_email:
address: [email protected]
sender_name: [email protected]
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 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;
}
}
Documentation: https://symfony.com/doc/current/bundles/FOSUserBundle/index.html
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!):
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 option 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
file:
<?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;
}
}
<?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;
}
}
<?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;
}
}
<?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.
Documentation: https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/blob/master/Resources/doc/index.md
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 } # 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 }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
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:[email protected]
Please choose a password:admin
Created user admin
Create an ApiController that will serve our resources. 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 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 getAllUsersAction(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 the 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();
}
}
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 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.
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 POST request to /oauth/v2/token
:
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 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": "[email protected]",
"email_canonical": "[email protected]",
"enabled": true,
"password": "$2y$13$HMApz6BLBHXbsLx2GZWJcOTxx3bqv2yKWg7vst5c7C10SXpNgVlE.",
"last_login": "2018-11-11T20:40:54+01:00",
"groups": [],
"roles": []
}
]
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
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:
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 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": "[email protected]",
"email_canonical": "[email protected]",
"enabled": true,
"password": "$2y$13$HMApz6BLBHXbsLx2GZWJcOTxx3bqv2yKWg7vst5c7C10SXpNgVlE.",
"last_login": "2018-11-11T20:40:54+01:00",
"groups": [],
"roles": []
}
]
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's command php bin/console fos:user:create
. Now we can call our API
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": "[email protected]",
"email_canonical": "[email protected]",
"enabled": true,
"password": "$2y$13$HMApz6BLBHXbsLx2GZWJcOTxx3bqv2yKWg7vst5c7C10SXpNgVlE.",
"last_login": "2018-11-11T20:40:54+01:00",
"groups": [],
"roles": []
}
]
I do it step by step as you have, but when i try GET localhost:8000/api/users response is:
{"error": "access_denied", "error_description": "OAuth2 authentication required"}
Could you help me?
Note: previous respone was {"access_token":"ZmFkZjQ1NzdjYTY0ZGMwNDYyZDkyNWM1NjU3MmUwZjNiNjIwYTgxYWNmOWZjZDIwZmUyODIwNDFlOWNhZDRmYQ","expires_in":3600,"token_type":"bearer","scope":null,"refresh_token":"Yjk2MmYzMzA1MWU4YTc1YWU2ZTVlMWQyYTI4N2I3NjA5YWE4ZGM4OGMwZDFjYTc1MzJjMDJiZTA2ODE0ZTFiOQ"}