Consuming Magento REST service using Zend_OAuth_Consumer

ZendConsumer

Ok folks. Until today, trying to cover this area completely, I wrote two articles about Magento oAuth and REST services:

This Magento REST and oAuth functionality and ways on which it could be used are so wide that I think that even in this three articles will not cover everything that I wanna share with you, so stay tuned for next articles about this topic in near future :-)

In official Magento documentation, one plain PHP class is written on how to authenticate with oAuth and how to Consume Magento REST web service.
Here I am going to show how you can do it from inside your Magento controller that is acting as REST client this time. Of course, for simplicity, I am not going to handle errors in examples and I am going to consume REST on same Magento installation, but I think that it will be enough to show the point.
Let’s start.

  1. Configure your local Magento installation following last article steps: How to configure Magento REST and oAuth settings
  2. Create Magento controller class in your test module.
  3. Paste source code inside your controller to look like this:
<?php
 
/**
 *
 * @author     Darko Goleš <darko.goles@inchoo.net>
 * @package    Inchoo
 * @subpackage RestConnect
 * 
 * Url of controller is: http://magento.loc/restconnect/test/[action] 
 */
class Inchoo_RestConnect_TestController extends Mage_Core_Controller_Front_Action {
 
    public function indexAction() {
 
        //Basic parameters that need to be provided for oAuth authentication
        //on Magento
        $params = array(
            'siteUrl' => 'http://magento.loc/oauth',
            'requestTokenUrl' => 'http://magento.loc/oauth/initiate',
            'accessTokenUrl' => 'http://magento.loc/oauth/token',
            'authorizeUrl' => 'http://magento.loc/admin/oAuth_authorize',//This URL is used only if we authenticate as Admin user type
            'consumerKey' => 'h7n8qjybu78cc3n8cdd5dr7ujtl33uqh',//Consumer key registered in server administration
            'consumerSecret' => '2smfjx37a6e4w23jlcrya6iyv5v32fxr',//Consumer secret registered in server administration
            'callbackUrl' => 'http://magento.loc/restconnect/test/callback',//Url of callback action below
        );
 
        // Initiate oAuth consumer with above parameters
        $consumer = new Zend_Oauth_Consumer($params);
        // Get request token
        $requestToken = $consumer->getRequestToken();
        // Get session
        $session = Mage::getSingleton('core/session');
        // Save serialized request token object in session for later use
        $session->setRequestToken(serialize($requestToken));
        // Redirect to authorize URL
        $consumer->redirect();
 
        return;
    }
 
    public function callbackAction() {
 
        //oAuth parameters
        $params = array(
            'siteUrl' => 'http://magento.loc/oauth',
            'requestTokenUrl' => 'http://magento.loc/oauth/initiate',
            'accessTokenUrl' => 'http://magento.loc/oauth/token',
            'consumerKey' => 'h7n8qjybu78cc3n8cdd5dr7ujtl33uqh',
            'consumerSecret' => '2smfjx37a6e4w23jlcrya6iyv5v32fxr'
        );
 
        // Get session
        $session = Mage::getSingleton('core/session');
        // Read and unserialize request token from session
        $requestToken = unserialize($session->getRequestToken());
        // Initiate oAuth consumer
        $consumer = new Zend_Oauth_Consumer($params);
        // Using oAuth parameters and request Token we got, get access token
        $acessToken = $consumer->getAccessToken($_GET, $requestToken);
        // Get HTTP client from access token object
        $restClient = $acessToken->getHttpClient($params);
        // Set REST resource URL
        $restClient->setUri('http://magento.loc/api/rest/products');
        // In Magento it is neccesary to set json or xml headers in order to work
        $restClient->setHeaders('Accept', 'application/json');
        // Get method
        $restClient->setMethod(Zend_Http_Client::GET);
        //Make REST request
        $response = $restClient->request();
        // Here we can see that response body contains json list of products
        Zend_Debug::dump($response);
 
        return;
    }
 
}

Now open the indexAction of this controller inside your browser. If everything configured correctly, you should be able to get list of products. At my Magento installation, URL is: http://magento.loc/restconnect/test

oAuthLogin1

oAuthAuthorize

Let’s explain above steps a little bit:

  • First we had to define basic neccesary oAuth parameters for Zend_OAuth_Client.
  • After initiating the client, first we get RequestToken. After request token is retreived, we save it into the session for usage in callback action later.
  • Then we call $client->redirect action and we are redirected to the server user login screen.
  • After successful login, we are redirected to Authorize URL in order user to Authorize application.
  • After authorization, server redirects to our callbackUrl we defined in $params. Here we are getting the RequestToken from session and requesting the AccessToken.
  • After AccessToken is retreived from server, oAuth dance is finished and we are preparing the Http client for making the REST request.
  • We make REST request to server resource URL and retreiving the response

Better way

Uf, let me share something more with you dear visitors:
In the meantime I investigated how to power up Magento oAuth, I developed one Model that can be used ro perform above operations even simpler and cleaner from inside your controller, and I wanna share it with you here.

<?php
/**
 *
 * @author     Darko Goleš <darko.goles@inchoo.net>
 * @package    Inchoo
 * @subpackage RestConnect
 */
class Inchoo_RestConnect_Model_Oauth_Client extends Mage_Core_Model_Abstract
{
    private $_callbackUrl;
    private $_siteUrl;
    private $_consumerKey;
    private $_consumerSecret;
    private $_requestTokenUrl;
    private $_accessTokenUrl;
    private $_consumer;
    private $_authorizeUrl;
    private $_userAuthorizationUrl;
 
    private $_authorized_token;
 
    const OAUTH_STATE_NO_TOKEN = 0;
    const OAUTH_STATE_REQUEST_TOKEN = 1;
    const OAUTH_STATE_ACCESS_TOKEN = 2;
    const OAUTH_STATE_ERROR = 3;
 
 
    public function init($config)
    {
        $this->setOAuthConfig($config);
        return $this;
    }
 
    public function setAuthorizedToken($token)
    {
        $this->_authorized_token = $token;
    }
 
    public function getAuthorizedToken()
    {
        if ($this->_authorized_token) {
            return $this->_authorized_token;
        }
        return false;
    }
 
 
    public function reset()
    {
        return $this->resetSessionParams();
    }
 
    public function authenticate()
    {
        $state = $this->getOAuthState();
        $consumer = $this->_getOAuthConsumer();
 
        try {
            switch ($state) {
                case self::OAUTH_STATE_NO_TOKEN:
                    $requestToken = $this->getRequestToken();
                    $this->setOAuthState(self::OAUTH_STATE_REQUEST_TOKEN);
                    $consumer->redirect();
                    return self::OAUTH_STATE_REQUEST_TOKEN;
                    break;
 
                case self::OAUTH_STATE_REQUEST_TOKEN:
                    $accessToken = $this->getAccessToken($this->getRequestToken());
                    $this->setAuthorizedToken($accessToken);
                    $this->setOAuthState(self::OAUTH_STATE_ACCESS_TOKEN);
                    return self::OAUTH_STATE_ACCESS_TOKEN;
                    break;
                case self::OAUTH_STATE_ACCESS_TOKEN:
                    return self::OAUTH_STATE_ACCESS_TOKEN;
                default:
                    $this->resetSessionParams();
                    return self::OAUTH_STATE_NO_TOKEN;
                    return;
                    break;
            }
 
        } catch (Zend_Oauth_Exception $e) {
            $this->resetSessionParams();
            Mage::logException($e);
        } catch (Exception $e) {
            Mage::logException($e);
        }
        return self::OAUTH_STATE_NO_TOKEN;
    }
 
    private function resetSessionParams()
    {
        $this->getSession()->unsetData('o_auth_state');
        $this->getSession()->unsetData('request_token');
        $this->getSession()->unsetData('o_auth_config');
        $this->getSession()->unsetData('access_token');
 
        return $this;
    }
 
 
    public function getRequestToken()
    {
        $token = $this->_getRequestTokenFromSession();
        if ($token && $token instanceof Zend_Oauth_Token_Request) {
            return $token;
        }
 
        $token = $this->_getRequestTokenFromServer();
        if ($token && $token instanceof Zend_Oauth_Token_Request) {
            $this->_saveRequestTokenInSession($token);
            return $token;
        }
 
        return false;
    }
 
    public function getAccessToken($requestToken)
    {
        $token = $this->_getAccessTokenFromSession();
        if ($token && $token instanceof Zend_Oauth_Token_Access) {
            return $token;
        }
 
        $token = $this->_getAcessTokenFromServer($requestToken);
        if ($token && $token instanceof Zend_Oauth_Token_Access) {
            $this->_saveAccessTokenInSesion($token);
            return $token;
        }
    }
 
    private function _getAcessTokenFromServer($requestToken)
    {
        if ($requestToken && $requestToken instanceof Zend_Oauth_Token_Request) {
            $accToken = $this->_getOAuthConsumer()->getAccessToken(
                $_GET,
                $requestToken
            );
        }
 
        if ($accToken && $accToken instanceof Zend_Oauth_Token_Access) {
            return $accToken;
        }
 
        return false;
    }
 
    private function _saveAccessTokenInSesion($accessToken)
    {
        $this->getSession()->setAccessToken(serialize($accessToken));
    }
 
    private function _getAccessTokenFromSession()
    {
        $accessToken = unserialize($this->getSession()->getAcessToken());
        if ($accessToken && $accessToken instanceof Zend_Oauth_Token_Access) {
            return $accessToken;
        }
        return false;
    }
 
    private function _getRequestTokenFromServer()
    {
        $token = $this->_getOAuthConsumer()->getRequestToken();
        return $token;
    }
 
    private function _saveRequestTokenInSession(Zend_Oauth_Token_Request $requestToken)
    {
        $this->getSession()->setRequestToken(serialize($requestToken));
    }
 
    private function _getRequestTokenFromSession()
    {
        $requestToken = unserialize($this->getSession()->getRequestToken());
        if ($requestToken && $requestToken instanceof Zend_Oauth_Token_Request) {
            return $requestToken;
        }
        return false;
    }
 
    public function getSession()
    {
        return Mage::getSingleton('core/session');
    }
 
    public function getOAuthToken()
    {
        return $this->getRequest()->getParam('oauth_token', false);
    }
 
    public function getRequest()
    {
        return Mage::app()->getRequest();
    }
 
    private function _getOAuthConsumer()
    {
        if ($consumer = $this->_consumer) {
            if ($consumer instanceof Zend_Oauth_Consumer) {
                return $this->_consumer;
            }
        }
        $this->_consumer = new Zend_Oauth_Consumer($this->getOAuthConfig());
        return $this->_consumer;
    }
 
    private function getOAuthConfig()
    {
        $config = array(
            'callbackUrl'     => $this->_callbackUrl,
            'siteUrl'         => $this->_siteUrl,
            'consumerKey'     => $this->_consumerKey,
            'consumerSecret'  => $this->_consumerSecret,
            'requestTokenUrl' => $this->_requestTokenUrl,
            'accessTokenUrl'  => $this->_accessTokenUrl,
        );
 
        if ($this->_authorizeUrl && $this->_authorizeUrl != '') {
            $config['authorizeUrl'] = $this->_authorizeUrl;
        }
 
        if ($this->_userAuthorizationUrl && $this->_userAuthorizationUrl != '') {
            $config['userAuthorizationUrl'] = $this->_userAuthorizationUrl;
        }
 
        return $config;
 
    }
 
    private function setOAuthConfig($config)
    {
        $this->getSession()->setOAuthConfig(serialize($config));
        foreach ($config as $key => $val) {
            $_key = '_' . $key;
            $this->$_key = $val;
        }
    }
 
    public function getConfigFromSession()
    {
        $config = unserialize($this->getSession()->getOAuthConfig());
        if ($config && is_array($config)) {
            return $config;
        }
        return false;
    }
 
    private function setOAuthState($state)
    {
        $this->getSession()->setOAuthState($state);
    }
 
    public function getOAuthState()
    {
        $state = $this->getSession()->getOAuthState();
        if ($state == null) {
            return self::OAUTH_STATE_NO_TOKEN;
        }
 
        $paramOAuthToken = $this->getOAuthToken();
        if ($paramOAuthToken == false && $state == self::OAUTH_STATE_REQUEST_TOKEN) {
            $this->resetSessionParams();
            return self::OAUTH_STATE_NO_TOKEN;
        }
 
        return $state;
 
    }
 
}

Create your own Model file inside your extension dir, and paste above source code there (of course, don’t forget to rename the class to fit your needs).

After that, you just need to have controller like this:

<?php
 
/**
 *
 * @author     Darko Goleš <darko.goles@inchoo.net>
 * @package    Inchoo
 * @subpackage RestConnect
 * 
 * Url of controller is: http://magento.loc/restconnect/test/[action] 
 */
class Inchoo_RestConnect_TestController extends Mage_Core_Controller_Front_Action {
 
    public function indexAction() {
 
        //Basic parameters that need to be provided for oAuth authentication
        //on Magento
        $params = array(
            'siteUrl' => 'http://magento.loc/oauth',
            'requestTokenUrl' => 'http://magento.loc/oauth/initiate',
            'accessTokenUrl' => 'http://magento.loc/oauth/token',
            'authorizeUrl' => 'http://magento.loc/admin/oAuth_authorize', //This URL is used only if we authenticate as Admin user type
            'consumerKey' => 'h7n8qjybu78cc3n8cdd5dr7ujtl33uqh', //Consumer key registered in server administration
            'consumerSecret' => '2smfjx37a6e4w23jlcrya6iyv5v32fxr', //Consumer secret registered in server administration
            'callbackUrl' => 'http://magento.loc/restconnect/test/callback', //Url of callback action below
        );
 
 
        $oAuthClient = Mage::getModel('inchoo_restconnect/oauth_client');
        $oAuthClient->reset();
 
        $oAuthClient->init($params);
        $oAuthClient->authenticate();
 
        return;
    }
 
    public function callbackAction() {
 
        $oAuthClient = Mage::getModel('inchoo_restconnect/oauth_client');
        $params = $oAuthClient->getConfigFromSession();
        $oAuthClient->init($params);
 
        $state = $oAuthClient->authenticate();
 
        if ($state == Inchoo_RestConnect_Model_OAuth_Client::OAUTH_STATE_ACCESS_TOKEN) {
            $acessToken = $oAuthClient->getAuthorizedToken();
        }
 
        $restClient = $acessToken->getHttpClient($params);
        // Set REST resource URL
        $restClient->setUri('http://magento.loc/api/rest/products');
        // In Magento it is neccesary to set json or xml headers in order to work
        $restClient->setHeaders('Accept', 'application/json');
        // Get method
        $restClient->setMethod(Zend_Http_Client::GET);
        //Make REST request
        $response = $restClient->request();
        // Here we can see that response body contains json list of products
        Zend_Debug::dump($response);
 
        return;
    }
 
}

If you use this source, please leave the credit at the top of the file. Thanks.

I hope this article helped to better understand oAuth and REST web services with Magento. :-)

Interested in hiring us?

Have a chat with us. You would be surprised how small changes can make your business even more successful.


12 comments

  1. Hi Drako,
    I am not expert in Magento. can you help me, where in my magento setup should i do this:
    “Create Magento controller class in your test module.”
    I mean which file or folder of my magento setup e.g.”app/code”,”skin”…

  2. Hi,

    How can we create new REST API with new resources. For example if i am creating a new extension and then i have to create API for it using new resources like event module then how i can i create REST API for it. Please guide me.

    Thanks

  3. Hi Darko

    thanks for your articles, very well explained. I have a question for you, are Magento REST APIs meant to be consumed within a user module? Let me explain. I am trying to connect with an external desktop app, however I cannot even get the unathorized token. I tracked than this problem to Server.php in Oauth module, and it looks like the following line is not getting the headers when trying to consume the service from an external application:

    $authHeaderValue = $this->_request->getHeader(‘Authorization’);

    I checked the request object and is not supposed to have the getHeader anyway, so that makes me confused. Originally, the request object is set on the constructor of the said class like this:

    if (is_object($request)) {
    if (!$request instanceof Zend_Controller_Request_Http) {
    throw new Exception(‘Invalid request object passed’);
    }
    $this->_request = $request;

    } else {
    $this->_request = Mage::app()->getRequest();

    }

    Thus, I am wondering if I am supposed to create the module to be able to consume the service, or if there is way to do it directly from an external app. Thanks!

  4. Many thanks for author.

    Only thing that I want to add:

    ~~~ Oauth_Client line 71
    case self::OAUTH_STATE_ACCESS_TOKEN:
    $accessToken = $this->_getAccessTokenFromSession();
    if ($accessToken && $accessToken instanceof Zend_Oauth_Token_Access) {
    $this->setAuthorizedToken($accessToken);
    }
    return self::OAUTH_STATE_ACCESS_TOKEN;
    ~~~
    you shouldn’t authorize each time with this code applied while session dies.

  5. Hi Darko, After successful login I’m being redirected to the customer account panel. As in your example you are redirected to the Authorise URL.
    Do you have any idea if this is to be expected or am i missing something?

    Any help would be appreciated.
    Thanks for the tutorial.

  6. @fabrice, you should create standard Magento module with regular configuration. Please try to Google for: create own controller magento and you will find the answer :-)

  7. Hi Darko, I’m stucked at
    2.Create Magento controller class in your test module.
    3. Paste source code inside your controller to look like this:[...]

    Could you explain me which structure I have to create exactly?
    I created this in code/local, my module name is Vac
    /Vac/RestConnect/controllers/Test.php
    /Vac/RestConnect/etc/config.xml (but I don’t know what to put here exactly)
    And my module is declared in etc/modules/Vac_RestConnect.xml
    But I only get a 404 when trying.
    Any help would be much appreciated!
    cheers from Switzerland

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.