Using OAuth-enabled APIs with CakePHP

Published on and tagged with cakephp  component  oauth

A growing number of APIs support OAuth, i.e. it is possible to give service A access to service B without giving B’s username/password to A. In this article I’m going to show you how to use OAuth-enabled APIs with CakePHP (and the OAuth consumer component I have written).

First, there are some preparations necessary, namely the download of the required files:

  • Get the OAuth component and place its content in “app/controllers/components”
  • Get the OAuth library and put it in “vendors/OAuth”
  • Set the security level in app/config/core.php to “low” (thanks to Bryan Young for mentioning in the comments)

The next step is to register your application at the service provider (e.g. Yahoo’s Fire Eagle) to get consumer key and consumer secret. Those values are then stored in a class called FireEagleConsumer if we go on using Fire Eagle as example service provider in this article:

// app/controllers/components/oauth_consumers/fire_eagle_consumer.php
class FireEagleConsumer extends AbstractConsumer {
    public function __construct() {
        parent::__construct('THE KEY', 'THE SECRET');
    }
}

This class is then used by the component to access consumer key and secret.

Using an OAuth-enabled API consists of four steps:

  • Get RequestToken
  • Authorize RequestToken
  • Exchange RequestToken for AccessToken
  • Access the API using the AccessToken

In the ideal case the first three steps are performed once. But it is also possible that for example the service provider expires the AccessToken after some time, and so those steps have to be repeated.

Anyway, let’s have a look at how those steps look in code. We start with getting the RequestToken:

// app/controller/oauth_consumer_example_controller.php
class OauthConsumerExampleController extends AppController {
    public $uses = array();
    public $components = array('OauthConsumer');
	
    public function index() {
        $requestToken = $this->OauthConsumer->getRequestToken('FireEagle', 'https://fireeagle.yahooapis.com/oauth/request_token');
        $this->Session->write('requestToken', $requestToken);
    }
}

The code is probably self-explanatory, we have to add the component to the $components array, and then we get the RequestToken from the specified url (while writing this I just realized it would make sense to move the url to the FireEagleConsumer class…).

The next step is simple: we have to redirect the user to the authorize page of the service provider:

// app/controller/oauth_consumer_example_controller.php
class OauthConsumerExampleController extends AppController {
    public $uses = array();
    public $components = array('OauthConsumer');
	
    public function index() {
        $requestToken = $this->OauthConsumer->getRequestToken('FireEagle', 'https://fireeagle.yahooapis.com/oauth/request_token');
        $this->Session->write('requestToken', $requestToken);
        $this->redirect('http://fireeagle.yahoo.net/oauth/authorize?oauth_token='.$requestToken->key);
    }
}

After the RequestToken is authorized, we get redirected to a callback url we specified while registering the application. We can now exchange the authorized RequestToken for an AccessToken:

// app/controller/oauth_consumer_example_controller.php
class OauthConsumerExampleController extends AppController {
    public $uses = array();
    public $components = array('OauthConsumer');

    ...

    public function callback() {
        $requestToken = $this->Session->read('requestToken');
        $accessToken = $this->OauthConsumer->getAccessToken('FireEagle', 'https://fireeagle.yahooapis.com/oauth/access_token', $requestToken);
    }
}

In a real use case you would also save the data of the AccessToken (key and secret) to the database, so you don’t have to perform all those steps every time you want to call the API.

The last step is to call the API, in this example to get the latest location of the user:

// app/controller/oauth_consumer_example_controller.php
class OauthConsumerExampleController extends AppController {
    public $uses = array();
    public $components = array('OauthConsumer');

    ...

    public function callback() {
        $requestToken = $this->Session->read('requestToken');
        $accessToken = $this->OauthConsumer->getAccessToken('FireEagle', 'https://fireeagle.yahooapis.com/oauth/access_token', $requestToken);
        $data = $this->OauthConsumer->get('FireEagle', $accessToken->key, $accessToken->secret, 'https://fireeagle.yahooapis.com/api/0.1/user');
        // do something with the data
    }
}

That’s it. Feedback is welcome!

Update 2008-09-15: Slightly adapted for new version of the component.
Update 2009-01-14: Adding hint about security level.

78 comments baked

  • cakebaker

    @Kakakatt: Hm, I just tried it myself and it worked fine without removing the param. Do you use the latest version of the oauth component?

    What is the problem with picasa? At least from this ticket http://code.google.com/p/gdata-issues/issues/detail?id=701 it seems like OAuth is supported by picasa.

  • Kakakatt

    May I read your code ? I have used Oauth component 2009-09-05 version.
    The link you provided do not deal with the problem, it was just warning of mistake. I have read sources from Google API that: Picasa is just compatible with Auth http://code.google.com/apis/picasaweb/docs/1.0/developers_guide_php.html

  • cakebaker

    @Kakakatt: Sure, here it is. The consumer class:

    // app/controllers/components/oauth_consumers/google_consumer.php
    class GoogleConsumer extends AbstractConsumer {
        public function __construct() {
            parent::__construct('anonymous', 'anonymous');
        }
    }

    And the controller:
    // app/controllers/google_example_controller.php
    class GoogleExampleController extends AppController {
        public $uses = array();
        public $components = array('OauthConsumer');
    	
        public function index() {
            $requestToken = $this->OauthConsumer->getRequestToken('Google', 'https://www.google.com/accounts/OAuthGetRequestToken', 'http://test.localhost/google_example/callback', 'POST', array('scope' => 'http://picasaweb.google.com/data'));
            $this->Session->write('request_token', $requestToken);
    	
            $this->redirect('https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=' . $requestToken->key);
        }
    	
        public function callback() {
            $requestToken = $this->Session->read('request_token');
            $accessToken = $this->OauthConsumer->getAccessToken('Google', 'https://www.google.com/accounts/OAuthGetAccessToken', $requestToken);
            
            debug($accessToken);
            exit;
        }
    }

    I hope this helps.

    And regarding OAuth and Picasa: at least according to Google’s OAuth announcement Picasa should also be supported.

  • CantonDog

    Would you know any reason why the callback would return NULL in a Google Chrome browser but work fine in FireFox and IE? So far I can’t figure it out.

    The first error I get when using chrome is Trying to get property of non-object

    The line it’s throwing the error on is:
    $credentials = $consumer->get($accessToken->key, $accessToken->secret, 'http://twitter.com/account/verify_credentials.json');

  • cakebaker

    @CantonDog: Hm, I assume you get the error when accessing the key and secret properties of $accessToken, right? Did you check whether you got an access token back after calling the getAccessToken() method?

    I’m not sure if this helps…

  • Implementing a Two-Legged OAuth Server in CakePHP · Dunedinmusic.com Development Blog

    […] are no available CakePHP components or plugins that implement an OAuth server (although there are these for doing client […]

  • amjedonline

    Hii, thanks for this great component.
    Am facing the same problem as ryan and cantondog.
    either requestToken is getting destroyed from the session, although I have set my security level to low.

  • cakebaker

    @amjedonline: Hm, so it works with some browsers, but not with others? In which browsers do you encounter issues? On which platform are you? And which CakePHP version do you use?

    Here on Linux with Cake v1.3.2 it works fine with FF 3.6.8 and Chrome 5.0.375.125 if the security level is set to “low”.

  • Ken

    Hello, I have a Google Apps for Education account and we are going to be using it for the students.We have already setup Single Sign On to connect to Active Directory and we want to be able to view the students emails without there consent if a harassment case comes up or something. Am I heading in the right direction with the info here? Any help to guide me in the right direction.

  • cakebaker

    @Ken: I don’t think OAuth is what you are looking for. With OAuth, a student would have to give your app access to his mailbox, so you can’t access the mailbox without their consent. And depending on the country you live in, it might be even illegal to access someone’s mailbox without their consent…

  • Ken

    @cakebaker: Alright, thanks. We are in the US. It would be in the students aup that they have to sign and their parents have to sign before they get an email address. It would only be used if an incident happens like harassment or bullying. Thanks for letting me know that it isn’t what I am looking for.

  • Kamy

    I use this component for twitter and it very good. But when I use it for yahoo, I can’t get requestToken, it always return null. Can you show me the code to use this component for yahoo ? My code here

    function yahoo() {
            $requestUrl = 'https://api.login.yahoo.com/oauth/v2/get_request_token';
            $requestToken = $this->OauthConsumer->getRequestToken('Yahoo', $requestUrl,'yahoo_callback');
            
            $this->Session->write('requestToken', $requestToken);
            $this->redirect('https://api.login.yahoo.com/oauth/v2/request_auth?oauth_token='.$requestToken->key);
        }

    $requestToken is always null !!!.

    My consumer:

    <?php
    	class YahooConsumer extends AbstractConsumer {
    		public function __construct() {
    			parent::__construct(YAHOO_API_CONSUMER_KEY, YAHOO_API_CONSUMER_SECRET);
    		}
    	}	
    
    ?>

    Thanks for component. It’s very useful !!!

  • cakebaker

    @Kamy: Hm, your code looks ok. Can you add the following snippet after the call to the getRequestToken() method?

    debug($this->OauthConsumer->getFullResponse());
    exit;

    What output do you get?

  • Kamy

    Thanks for your comment. I solved it. Because I forgot enable SSL in php.ini. Sorry !!! I’m very glad for your help. Thank you

  • cakebaker

    @Kamy: No problem, it is good to hear it is working now :)

  • Julius Hermosura

    Is this still working today?
    And also my openssl is not working. Is it the problem why I couldn’t make it work?

  • cakebaker

    @Julius: Yes, it still works (at least it is working with the Twitter API).

    Yes, OpenSSL not working could be a reason that the OAuth stuff doesn’t work. Do you get some errors? And what API do you try to use?

  • Kevin

    Hi, I am using the Twitter OAuth. I can get $requestToken at function twitter_authentication, but at twitter_login, it unable to read the session that I wrote the $requestToken into the session, then ended up to PHP incomplete class. Can anyone please help me? Thanks.

  • Mariano Rossin

    Hi, Kevin, i’m trying to make the Twitter OAuth work and I reach to the same problem. I resolved it by making these

    public function index() {
            $requestToken = $this->OAuthConsumer->getRequestToken('Twitter', 'https://api.twitter.com/oauth/request_token', 'http://www.deliverya.com/tweets/callback');
    //        $this->Session-&gt;write('requestToken', $requestToken); //no funciona
    
            $array_datos = array();
            $array_datos['key'] = $requestToken->key;
            $array_datos['secret'] = $requestToken->secret;
    
            $this->Session->write('requestToken', $array_datos);
            $this->redirect('https://api.twitter.com/oauth/authorize?oauth_token='.$requestToken->key);
        }
    
    
    public function callback() {
    
            $req = $this->Session->read('requestToken');
            $requestToken = new OAuthToken($req['key'], $req['secret']);
            $parameters['oauth_token'] = $_GET['oauth_token'];
            $parameters['oauth_verifier'] = $_GET['oauth_verifier'];
            $accessToken = $this->OAuthConsumer->getAccessToken('Twitter', 'https://api.twitter.com/oauth/access_token', $requestToken, 'POST', $parameters);
                   
            $data = $this->OAuthConsumer->get('Twitter', $accessToken->key, $accessToken->secret, 'https://api.twitter.com/1/users/show.json', array('screen_name'=>'JDPeron')); //JDPeron is de name of a acount on twitter
            
            pr($data['body']);
            pr(json_decode($data['body']));
            die;
            // do something with the data
        }

    I just need to know how to get the users name, or id and i will finish.
    Hope this helps you, i will continue trying, byeeeee

  • cakebaker

    @Mariano: Hm, I can’t reproduce this issue here. Which versions of CakePHP and PHP do you use?

  • Alexander

    Hello. I have a problem. Everything works great but when I’m trying to use $this->OAuthConsumer->get(…) I have the following error:
    “Fatal error: Call to a member function get() on a non-object”.

    My code:

    public function google_callback() {
    
    		$requestToken = $this->Session->read('google_request_token');
    		$accessToken = $this->OauthConsumer->getAccessToken('Google',   'https://www.google.com/accounts/OAuthGetAccessToken', $requestToken);
    
    		// ERROR IS HERE:
    		$data = $this->OAuthConsumer->get('Google', $accessToken->key, $accessToken->secret, 'https://www.googleapis.com/auth/userinfo');
    	}

    $accessToken is NOT null; I used debug(…) and it showed that the object contains “key” and “secret” values. So I have no idea why it doesn’t work.
    P.S.: I’m using CakePHP 2.1 Stable and PHP 5.3

  • Alexander

    Sorry. I found the problem.

    It took me a few hours to understand that it shows an error just because I used $this->OAuthConsumer->get instead of $this->OauthConsumer->get.

    Thanks for your OAuth Component.

  • cakebaker

    @Alexander: Odd, theoretically your original approach is the correct way to use the component, and the call of getAccessToken() should have failed… How do you load the component? With

    public $components = array('OAuthConsumer');
    

    ?

  • Alexander

    No. Like this:

    public $components = array('OauthConsumer');
    

    Everything works now. Should I use

    public $components = array('OAuthConsumer');
    

    instead?

  • cakebaker

    @Alexander: No, if everything works I wouldn’t change it. However, I would also test it on the (staging) server. I’m still surprised that it works and you don’t get any error messages ;-)

  • Mariano Rossin

    @cakebaker I’m using cakephp 2.1 and php 5.3, i didn’t understand why it gives me and error, but I have resolved it by no using it jajaja. Just asign the values I need to an array and then save that values with de session component. I think it’s a problem of the component when it tryes to save an object. Anyway, thanks for your answer, regards

  • ludovic

    Hello. I have a problem, too. I use Oauth component for google plus. I passed all stages, now, I just need to know how to get the users id and name.
    Is someone could help me, please?

  • cakebaker

    @ludovic: Have a look at https://developers.google.com/+/api/latest/people/get.

    Hope this helps!

  • free classified

    free ads posting

    Universal Free Classified Ads

Bake a comment




(for code please use <code>...</code> [no escaping necessary])

© daniel hofstetter. Licensed under a Creative Commons License