A PHP Twitter API OAuth example using Silex and Guzzle

padlocks illustrate the secure aspect of the PHP Twitter API flow

Do you need a PHP Twitter API tool? If you’re a PHP developer and you want to check your users’ Twitter feeds or send Tweets on their behalf, then yes, yes you do. If your application just needs to send tweets for itself, then things are pretty simple. It gets a little more involved if you want to enable a user to authorise you. For that, you need a process called the three-legged OAuth flow.

From the user’s perspective that flow looks like this:

  1. the user clicks on a link or a button in your application
  2. she is redirected to an authorisation page on Twitter’s server
  3. on OKing the authorisation, she is returned to your application where she benefits from the great Twitter automations you offer

From the developer’s perspective things are little more complex. Here is the PHP Twitter API flow in summary.

  1. the application calls Twitter with its application credentials and a callback URL. It gets temporary credentials back (the request token).
  2. the application then redirects the user to a special authentication URL, passing along the returned request token to identify the request.
  3. The user and Twitter do their authentication and authorisation business
  4. Assuming the user is legitimate and agrees to authorise the application, she is redirected from Twitter to the application’s callback URL with more credentials provided in the query string.
  5. The application uses these credentials to query Twitter for a long-lived token-and-secret combo.
  6. The token and secret are passed back and put somewhere safe by the application. The twittering can now commence.

In this post, I’ll walk though this process. It is possible to do this as a one page script – but I find those examples quite hard to relate to real world applications where different access points are typically used for various stages of the process (the callback does not necessarily point to the same block of code as the initialisation, for example). For that reason, I’ll use my favourite lightweight framework: Silex. To handle the network connections I’ve opted for Guzzle 6 – because it’s very standards compliant and a increasingly a core tool. There is a also a version of this example that runs with Abraham Williams’ excellent TwitterOAuth library. If more than one person pings me for it (on, yes, Twitter) I’ll make that available too.

Getting Started: composer.json

The Composer file is a good starting point for most serious projects these days. Here’s mine:

1
2
3
4
5
6
7
8
9
10
11
12
{
    "require": {
        "guzzlehttp/guzzle": "~6.0",
        "guzzlehttp/oauth-subscriber": "*",
        "silex/silex": "~2.0.0"
    },
    "autoload": {
       "psr-4": {
            "getinstance\\twitdev\\": ["src/", "test/functional", "test/unit"]
        }
    }   
}

So, as you can see, I will be using Guzzle, a plugin named oauth-subscriber, and Silex.

I will also use the namespace getinstance\twitdev in a directory named /src. Let’s get cracking!

The Controller

I am going to assume you’ve run composer update already so we can get right into the code. As you saw in the previous section I’ve mapped my namespace getinstance\twitdev to the directory /src. My namespace here is getinstance\twitdev\controller and the class is named Controller – so I’ll be creating a file at src/controller/Controller.php.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
namespace getinstance\twitdev\controller;
 
use Silex\Provider\TwigServiceProvider;
use Silex\Provider\SessionServiceProvider;
use Symfony\Component\HttpFoundation\Request;
 
use Silex\Application;
 
class Controller
{
    private $app;
 
    public function __construct()
    {
        $this->app = new Application();
    }
 
    public function init()
    {
        $app = $this->app;
 
        $app->register(new SessionServiceProvider());
        // useful stuff omitted
 
        $app->get('/twitterstart', '\getinstance\twitdev\command\TwitterSignup::start');
        $app->get('/twitterauthed', '\getinstance\twitdev\command\TwitterSignup::authed')->bind("twitterauthed");
        $app->get('/twitterfinished', '\getinstance\twitdev\command\TwitterSignup::finished');
    }
 
    public function execute()
    {
        $this->app->run();
    }
}

A service container named Silex\Application lies at the heart of Silex. I create an instance of this in the class constructor and store it in a property.

The meat of this class lies in the init() method. This primarily creates the routing for my little application – that is the mapping from the URL to code. First, though, I register the SessionServiceProvider – which will allow me to store data on the session – quite an important part of this flow since I need to store state between queries.

Now the routing. I map three URL endpoints to corresponding methods in the same class \getinstance\twitdev\command\TwitterSignup. This means that when someone visits /twitterstart (in my case, http://twitdev.vagrant.internal/twitterstart) the start() method in TwitterSignUp will be invoked.

Kicking off a request: index.php

We’ve seen a simple front controller – but something has to invoke it to get the system running. Here’s a simple top level index.php file:

1
2
3
4
5
6
require_once __DIR__.'/../vendor/autoload.php'; 
use getinstance\twitdev\controller\Controller;
 
$controller = new Controller();
$controller->init();
$controller->execute();

Simple stuff. I include the autoload file to set it up for the whole system. Then I instantiate, intialise and execute the controller. Job done.

The Command class – boilerplate

So let’s take a look at TwitterSignUp. In fact, that’s most of the rest of this post. It’s where the magic happens!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
namespace getinstance\twitdev\command;
 
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
 
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
 
use Silex\Application;
 
class TwitterSignup
{
    private $key="XXXXXXXXX";
    private $secret="XXXXXXXXX";
 
    private function getClient($token = null, $secret = null)
    {
        $creds = [
            'consumer_key'    => $this->key,
            'consumer_secret' => $this->secret,
        ];
 
        if (! is_null($token)) {
            $creds['token'] = $token;
        }
 
        if (! is_null($secret)) {
            $creds['token_secret'] = $secret;
        }
        $oauth = new Oauth1($creds);
        $stack = HandlerStack::create();
        $stack->push($oauth);
 
        $client = new Client([
            'base_uri' => 'https://api.twitter.com/',
            'handler' => $stack
        ]);
        return $client;
    }
 
    // ...
    // public function start(Application $app, Request $request)
    // public function authed(Application $app, Request $request)
    // public function finished(Application $app, Request $request)
}

I start with boilerplate – shared stuff. First off the namespace and a bunch of use statements. Next, my application key and secret. This is a prerequisite for doing anything programmatic with Twitter. If you haven’t done this already, head off to apps.twitter.com and create an application. You can get your key and secret then by clicking on the “Keys and Access Tokens” tab. It’s also worth noting that in a real world application I’d put these values in a configuration file rather than adding them to source.

Now to getClient(). Each of my methods will need to configure a client, and since this is a duplication (and a little more involved for Guzzle than for TwitterOAuth), I factor it out into a shared method. First, I set up my credentials. These always consist of my application key and secret. Then, if provided, the user’s token and secret. Remember that these are initially temporary tokens issued just for the authorisation process.

I use these credentials to instantiate an Oauth1 object, which I add to Guzzle’s handler stack – in other words, I make this OAuth available to the client. Finally, I instantiate and return my Client object.

Although I’ve cut out the flow methods, I added some placeholder comments so that you can see the shape of the whole class here. Let’s delve into that cut code now.

PHP Twitter API: First engagement

Let’s begin at.. start(). Remember the PHP Twitter API flow:

the application calls Twitter with its application credentials and a callback URL. It gets temporary credentials back (the request token).

Here’s how the method fulfils this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    public function start(Application $app, Request $request)
    {
        $callbackurl = $app['url_generator']->generate('twitterauthed', [], UrlGeneratorInterface::ABSOLUTE_URL);
        $client = $this->getClient();
        $res = $client->post('oauth/request_token', ['auth' => 'oauth', 'form_params' => ['oauth_callback' => $callbackurl]]);
        parse_str((String)$res->getBody(), $request_token);
 
        $session = $app['session'];
        $session->set('oauth_token', $request_token['oauth_token']);
        $session->set('oauth_token_secret', $request_token['oauth_token_secret']);
 
        $url = "https://api.twitter.com/oauth/authenticate?oauth_token={$request_token['oauth_token']}";
        return $app->redirect($url);
    }

I first need a callback url. I could hard-code one – for my system it will be http://twitdev.vagrant.internal/twitterauthed. But then my application will break when I move to a production server. Instead, I used the url_generator service to create an absolute URL. If you look back at the Controller, you’ll see I added a call to bind() to the twitterauthed routing get() method so that I could refer to the endpoint right here.

Then I call getClient() to er.. get my client. This is the only circumstance in which I do not pass along a token and secret. I make my call to Twitter (to the /oauth/request_token endpoint at https://api.twitter.com to be precise). Note that I use an auth element in my configuration array for this call – that’s necessary for all of these server calls. In return I get my $request_token.

Note that you should never call parse_str() without a second argument – otherwise your scope will be polluted with unknown variables – about as pleasant and useful as opening up a bag of angry wasps.

So once I have my request token, I store it – both its parts – on the session – so that I can use it again in the next stage.

What’s next in our PHP Twitter API flow?

the application then redirects the user to a special authentication URL, passing along the returned request token to identify the request.

And that’s exactly what I do in the last two lines of the method. Onward!

PHP Twitter API: Handling Twitter’s response

So out there in Twitter-land, the user negotiates with Twitter – authenticating herself and authorising us. When she’s finished, her browser is redirected to the callback url – which resolves to /twitterauthed and then our authed() method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    public function authed(Application $app, Request $request)
    {
        $session = $app['session'];
 
        $stored_oauth_token =        $session->get('oauth_token');
        $stored_oauth_token_secret = $session->get('oauth_token_secret');
 
        $oauth_token =      $request->query->get('oauth_token');
        $oauth_verifier =   $request->query->get('oauth_verifier');
 
        if ($stored_oauth_token !== $oauth_token) {
            throw new \Exception("Abort! Something is wrong.");
        }
 
        $client = $this->getClient($stored_oauth_token, $stored_oauth_token_secret);
        $res = $client->post('oauth/access_token', ['auth' => 'oauth', 'form_params' => ["oauth_verifier" => $oauth_verifier]]);
        parse_str((String)$res->getBody(), $access);
 
        $session = $app['session'];
 
        // we would put these into a db probably
        $session->set('token', $access['oauth_token']);
        $session->set('secret', $access['oauth_token_secret']);
        return $app->redirect("/twitterfinished");
    }

In fact the call to us looks something like:

1
http://my.callback.com/twitterauthed?oauth_token=xxxx&oauth_verifier=xxxx

Which corresponds to this stage in the flow:

Assuming the user is legitimate and agrees to authorise the application, she is redirected from Twitter to the application’s callback URL with more credentials provided in the query string.

I get the oauth_token and oauth_token_secret values. Now I must check that the provided oauth_token matches the token I saved in the session – this verifies that I’m on the same page with Twitter’s server about who everyone is, and that nothing sketchy is going on.

Finally I wrap up the PHP Twitter API flow

The application uses these credentials to query Twitter for a long-lived token-and-secret combo.

Note that I pass back the oauth_verifier as part of the request – and I get my long-lived token values – that is oauth_token and oauth_token_secret values. At this stage I’d usually store these in a database keyed to a user table – I keep things simple here and just dump them on the session.

Now, to prove it all worked, I redirect to a final end point which puts the credentials to the test.

PHP Twitter API: The victory lap

In this method I simply prove the last step in my PHP Twitter API flow:

1
2
3
4
5
6
7
8
9
    public function finished(Application $app, Request $request)
    {
        $session = $app['session'];
        $client = $this->getClient($session->get('token'), $session->get('secret'));
 
        $res = $client->get('1.1/account/verify_credentials.json', ['auth' => 'oauth']);
        $verified = json_decode((string)$res->getBody(), true);
        return new Response("hello @{$verified['screen_name']}", 200);
    }

The token and secret are passed back and put somewhere safe by the application. The twittering can now commence.

The first part, I already did when I lodged the long-lived credentials on the session. Now, I retrieve those values and use them to build a client.

I invoke the endpoint: /1.1/account/verify_credentials.json which should return information about the user who authorised us. The response is in json format, so I decode that into an associative array and, finally, greet our new tweep.

Leave a Reply

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