11/01/2016 - SYMFONY
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.
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
refresh_token by default.access_token contains "user" information association with it, therefore there will be "user" information retrieved in server/API site.access_token and refresh_token can be obtained by exchanging the current refresh_token in a request.access_token and it doesn't cause redirect after authentication unlike Authorization Code grant type.Install "guzzlehttp/guzzle": "6.1.1" package.
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
# 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%
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);
}
}
}
# 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']));
}
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"]
# 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.
For more information, you can check links below.