This example shows us how to create an OAuth2 API client with symfony and depends on OAuth2 API server we touched upon in previous posts so you better read up on it as well. It authenticates with password grant type. Since the API uses role_hierarchy to control controller access with @Security("has_roles('xxxxx')") in annotations, password grant type is the best/simplest option to use. See the reference links at the bottom of the post.


Important


In example below, I expose client_id and client_secret in request URI to obtain access token for demonstration purposes. You should not do that in real world applications. Instead, you should encode both of them with base64_encode function and add it to request header as Authorization.


$clientId = 'i-am-client-id';
$clientSecret = 'i-am-client-secret';

$base64 = base64_encode($clientId.':'.$clientSecret);

$header = 'Basic '.$base64;

So you should use it like Authorization: Basic aS1hbS1jbGllbnQtaWQ6aS1hbS1jbGllbnQtc2VjcmV0 in your request. On top of that, you should remove username, password and grant_type from URI to send them as parameters encoded with application/x-www-form-urlencoded. Your final request should look like the one below.


curl -X POST
-H 'Authorization: Basic aS1hbS1jbGllbnQtaWQ6aS1hbS1jbGllbnQtc2VjcmV0'
-H 'content-type: application/x-www-form-urlencoded'
-d 'grant_type=password&username=inanzzz&password=123123'
http://oauth-server.dev/app_dev.php/oauth/v2/token

Facts



Composer.json


Install "guzzlehttp/guzzle": "6.1.1" package.


Parameters.yml


Sensitive info in URI below is given to you by API administrator.


# oauth-client/app/config/parameters.yml
parameters:
oauth_api_access_token_cache_namespace: OAUTH2_ACCESS_TOKEN
oauth_api_base_url: http://oauth-server.dev/app_dev.php
oauth_api_uri_version: /1
oauth_api_token_uri: /oauth/v2/token?client_id=1_fqnumqc8gvkss8soo44g4g0sw0s0okkk8og84k8ggckwsk4gc&client_secret=1m5d8yl0wk00wogsskoooo4k4o0wgw8k00cw8k00s4g8w8gkw0&grant_type=password&username=inanzzz&password=123123

Controllers.yml


# oauth-client/src/Application/ClientBundle/Resources/config/controllers.yml
services:
application_client.controller.team:
class: Application\ClientBundle\Controller\TeamController
arguments:
- %oauth_api_access_token_cache_namespace%
- %oauth_api_base_url%
- %oauth_api_uri_version%
- %oauth_api_token_uri%

TeamController.php


This example will generate a new access_token per request so the requests will be slow. To solve this issue, you can store access_token or refresh_token in cache and regenerate it when it is expired. Also putting whole logic in controller is a bad practise. You should divide it into classes like service, model, factory, helper so on.


namespace Application\ClientBundle\Controller;

use Exception;
use GuzzleHttp\Client;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

/**
* @Route("team", service="application_client.controller.team")
*/
class TeamController extends Controller
{
private $oauthApiAccessTokenCacheNamespace;
private $oauthApiBaseUrl;
private $oauthApiUriVersion;
private $oauthApiTokenUri;

public function __construct(
$oauthApiAccessTokenCacheNamespace,
$oauthApiBaseUrl,
$oauthApiUriVersion,
$oauthApiTokenUri
) {
$this->oauthApiAccessTokenCacheNamespace = $oauthApiAccessTokenCacheNamespace;
$this->oauthApiBaseUrl = $oauthApiBaseUrl;
$this->oauthApiUriVersion = $oauthApiUriVersion;
$this->oauthApiTokenUri = $oauthApiTokenUri;
}

/**
* @param string $name
*
* @Method({"GET"})
* @Route("/{name}")
*
* @return Response
*/
public function getTeamAction($name)
{
$accessToken = $this->getAccessToken();
$response= $this->call(
'GET',
$this->oauthApiUriVersion.'/server/team/'.$name,
$accessToken
);

return new Response($response->getBody().' with ACCESS TOKEN: '.$accessToken);
}

/**
* @param Request $request
*
* @Method({"POST"})
* @Route("")
*
* @return Response
*/
public function createTeamAction(Request $request)
{
$accessToken = $this->getAccessToken();
$response= $this->call(
'POST',
$this->oauthApiUriVersion.'/server/team',
$accessToken,
$request->getContent()
);

return new Response($response->getBody().' with ACCESS TOKEN: '.$accessToken);
}

/**
* @param Request $request
*
* @Method({"POST"})
* @Route("/club")
*
* @return Response
*/
public function createClubAction(Request $request)
{
$accessToken = $this->getAccessToken();
$response= $this->call(
'POST',
$this->oauthApiUriVersion.'/server/club',
$accessToken,
$request->getContent()
);

return new Response($response->getBody().' with ACCESS TOKEN: '.$accessToken);
}

private function getAccessToken()
{
$response = $this->call('GET', $this->oauthApiTokenUri);
$responseParts = json_decode($response->getBody(), true);

return $responseParts['access_token'];
}

private function call($method, $uri, $auth = null, $postData = null)
{
$client = new Client();

try {
return $client->request(
$method,
$this->oauthApiBaseUrl.$uri,
[
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer '.$auth
],
'body' => $postData
]
);
} catch (Exception $e) {
$message = $e->getCode() == 403 ? 'Missing role.' : $e->getMessage();

throw new AccessDeniedHttpException($message);
}

}
}

Server side methods


# oauth-server/src/Application/ServerBundle/Controller/ServerController.php
/**
* @param string $name
*
* @Method({"GET"})
* @Route("/team/{name}")
*
* @return Response
*/
public function getTeamAction($name)
{
return new Response(sprintf('GET your team [%s] from Server', $name));
}

/**
* @param Request $request
*
* @Security("has_role('ROLE_ADMIN')")
* @Method({"POST"})
* @Route("/team")
*
* @return Response
*/
public function createTeamAction(Request $request)
{
$postData = json_decode($request->getContent(), true);

return new Response(sprintf('POST your team [%s] to Server', $postData['name']));
}

/**
* @param Request $request
*
* @Security("has_role('ROLE_SUPER_ADMIN')")
* @Method({"POST"})
* @Route("/club")
*
* @return Response
*/
public function createClubAction(Request $request)
{
$postData = json_decode($request->getContent(), true);

return new Response(sprintf('POST your club [%s] to Server', $postData['name']));
}

User roles


This is how the API owner created our user. Unfortunately we're not given ROLE_SUPER_USER role although there is a controller method above.


MacBook-Pro:oauth-server inanzzz$ php app/console create:oauth:user --username=inanzzz --password=123123 --email=myemail@mydomain.com --role=ROLE_USER --role=ROLE_ADMIN
OAuth user has been created...
Username: inanzzz
Password: 123123
Roles: ["ROLE_USER","ROLE_ADMIN"]

Tests


# Request
GET http://oauth-client.dev/app_dev.php/team/inanzzz

# Response
GET your team [inanzzz] from Server with ACCESS TOKEN: YjQ0ZjVhMDE4MmRhMDIyYWQyMzhhODM4M2YzMGRmMzc0ODI2ZWU4NWFiMmJhZGUyOTQ0OTA3Y2MyNDhkMzYyMw

# Request
POST http://oauth-client.dev/app_dev.php/team
{
"name": "inanzzz"
}

# Response
POST your team [inanzzz] to Server with ACCESS TOKEN: ZTllODdhZTRlY2VmYzdhYmU4ZmI5MjUxMDQ1MjI0YjMzZjAxN2E3YzQxZmUwNjljMDMyZjg1OTZhODUwMGI0ZA

The reason why the one below didn't work is because if you look at the command how the user was created didn't actually assign ROLE_SUPER_ADMIN to the user.


# Request
POST http://oauth-client.dev/app_dev.php/team/club
{
"name": "inanzzz"
}

# Response
403 Forbidden - AccessDeniedHttpException
Missing role.

References


For more information, you can check links below.