In this article I would like to describe implementation of an OAuth2 Client. Please keep in mind that this is not an authentification provider. To authenticate against third party services there are well maintained bundles that do just that. My target is to provide a solution to consume the API from the OAuth2 Server we provided in the previous articles.
Overview
For this implementation I had two options. To use lightweight OAuth2 client library or to implement Guzzle Plugin. I have chosen the first approach, since it covers several grants at once, when Guzzle plugin will solve the more specific problem, and might be a better solution in your specific case.
In this article I will focus on authorization_code and client_credentials grant types.
Setup
Let’s start by requiring the library from the project root directory
1
composer require adoy/oauth2:dev-master
This is shorthand for adding the adoy/oauth2:dev-master to your composer.json file and executing composer install afterwards.
In case you don’t have a client on the server side, please create one. Execute
redirect-uri specifies where will the server redirect, to provide the code from authorization_code grant. As OAuth2 consumer we will need to provide an endpoint for that, and the url for that endpoint is specified here. I will cover how to do that later in this article, all you need to know for now is the url for this endpoint.
As a result of execution you will have client_id and client_secret. We will need this right away. Specify the following parameters in parameters.yml.dist
Now you just need to execute composer install and it will apply the parameters into parameters.yml.
Since we don’t have a bundle for Symfony2 for this library at the moment of writing, we will build a facade for it for our specific needs and register the facade with Symfony2 Dependency Injection Container.
<?php// src/StarkIndustries/ClientBundle/Service/OAuth2Client.phpnamespaceStarkIndustries\ClientBundle\Service;useOAuth2;classOAuth2Client{protected$client;protected$authEndpoint;protected$tokenEndpoint;protected$redirectUrl;protected$grant;protected$params;publicfunction__construct(OAuth2\Client$client,$authEndpoint,$tokenEndpoint,$redirectUrl,$grant,$params){$this->client=$client;$this->authEndpoint=$authEndpoint;$this->tokenEndpoint=$tokenEndpoint;$this->redirectUrl=$redirectUrl;$this->grant=$grant;$this->params=$params;}publicfunctiongetAuthenticationUrl(){return$this->client->getAuthenticationUrl($this->authEndpoint,$this->redirectUrl);}publicfunctiongetAccessToken($code=null){if($code!==null){$this->params['code']=$code;}$response=$this->client->getAccessToken($this->tokenEndpoint,$this->grant,$this->params);if(isset($response['result'])&&isset($response['result']['access_token'])){$accessToken=$response['result']['access_token'];$this->client->setAccessToken($accessToken);return$accessToken;}thrownewOAuth2\Exception(sprintf('Unable to obtain Access Token. Response from the Server: %s ',var_export($response)));}publicfunctionfetch($url){return$this->client->fetch($url);}}
As you can see, we have done the following. Registered the library class itself in the DIC, then we have created two clients with different configurations and parameter sets, for client_credentials and authorization code accordingly. Let’s see how one can use them in the code. We will create a console command for client credentials, and controller for authorization code (since it has to provide a uri we mentioned earlier to be redirected to).
After obtaining access token you can use other HTTP libraries to make the request, Guzzle for instance, just don’t forget to append access_token GET parameter or transmit a Bearer header, or use one of OAuth2 supported methods to transport the token. Or use the fetch method from the library, as it is done in the example.
Authorization Code Controller
This one is a bit more complicated. You can split this into two pieces. One that makes the call, other that handles redirect, but for simplicity I combined all into one controller.
<?php// src/StarkIndustries/ClientBundle/Controller/AuthController.phpnamespaceStarkIndustries\ClientBundle\Controller;useSymfony\Bundle\FrameworkBundle\Controller\Controller;useSymfony\Component\HttpFoundation\Response;useSymfony\Component\HttpFoundation\RedirectResponse;useSymfony\Component\HttpFoundation\Request;// these import the "@Route" and "@Template" annotationsuseSensio\Bundle\FrameworkExtraBundle\Configuration\Route;useOAuth2;classAuthControllerextendsController{/** * @Route("/authorize", name="auth") */publicfunctionauthAction(Request$request){$authorizeClient=$this->container->get('stark_industries_client.authorize_client');if(!$request->query->get('code')){returnnewRedirectResponse($authorizeClient->getAuthenticationUrl());}$authorizeClient->getAccessToken($request->query->get('code'));returnnewResponse($authorizeClient->fetch('http://oauth-server.local/api/articles'));}}
That’s basically everything what you need to consume an OAuth2 API. In case you need a working example, please refer to a reference implementation.