Great, our Foller.me Rundown account (@fmrd) now tweets via OAuth and has our application name and link attached to every tweet. It was indeed pretty simple. After reading the specs of the OAuth protocol I came up with a fairly simple solution. I’m using this open source Twitter OAuth php library by Abraham Williams which is quite good, and I do recommend you try some basic OAuth stuff (with sessions, based on Abraham’s example) before proceeding to automated tweeting.
Okay, let me first breifly explain how OAuth at Twitter is supposed to work (focusing on automated work). Step by step:
- You browse to some hidden area (which nobody but you has access to) and initiate the app registration process. At this point, your app should go ask Twitter for a request token and provide you with a link to Twitter authentication (which will contain the received token)
- Next, you click on that link which directs you to Twitter, login, click allow and you’ll be redirected back to your application page (not the hidden area!) with the request token attached to the URL.
- You copy that token, browse back to your hidden area and initiate the app validation process by providing the token in your request (GET)
- Your app will go talk to Twitter again asking them for an access token. It stores that token (in a very safe place) for later use.
That’s pretty much everything. Once you have your access token you can update your status via OAuth as much as you want. Also note that when I mention request token and access token, I mean request token secret and access token secret too. OAuth tokens come in pairs. Token + secret. Yes, it is that simple!
Now let’s get to some coding! Suppse your app is called MyApp and is located at myapp.com. Make sure your hidden area is actually hidden. Choose a nifty directory for your place and make sure you protected it with .htaccess (allow by IP or based on authentication, it’s up to you). Don’t worry, Twitter will not try to access that directory. Twitter (the OAuth Service Provider) doesn’t do anything but give responses to your requests, so block that as strong as possible.
Suppse your hidden server auth place is at myapp.com/hidden/ and there’s an index.php file, your requests would look like this:
myapp.com/hidden/?register
That would mean “initiate the OAuth registration process!”, which will give you the URL to Twitter. And:
myapp.com/hidden/?validate&oauth_token=whatever
Which will get your Twitter OAuth access token and store it somewhere safe. Remember that you’ll have to replace the word “whatever” with the request token provided by Twitter after you authorize.
Let’s look at the php code (omitting the includes and blah blah blah). Just read through the comments, you should be able to understand. Also make sure you got your $consumer_key and $consumer_secret setup in the Twitter OAuth applications settings.
if (isset($_GET["register"])) { // If the "register" parameter is set we create a new TwitterOAuth object // and request a token $oauth = new TwitterOAuth($consumer_key, $consumer_secret); $request = $oauth->getRequestToken(); $request_token = $request["oauth_token"]; $request_token_secret = $request["oauth_token_secret"]; // At this stage you should store the two request tokens somewhere. // Database or file, whatever. Just make sure it's safe and nobody can read it! // I'll dump mine into files using file_put_content: file_put_contents("request_token", $request_token); file_put_contents("request_token_secret", $request_token_secret); // Generate a request link and output it $request_link = $oauth->getAuthorizeURL($request); echo "Request here: <a href="" . $request_link . "">" . $request_link . "</a>"; die(); } elseif (isset($_GET["validate"])) { // This is the validation part. At this point you should read the stored request // tokens. You'll need them to get your access tokens! // Mine are located in two files: $request_token = file_get_contents("request_token"); $request_token_secret = file_get_contents("request_token_secret"); // Initiate a new TwitterOAuth object. This time we provide them with more details: // The request token and the request token secret $oauth = new TwitterOAuth($consumer_key, $consumer_secret, $request_token, $request_token_secret); // Ask Twitter for an access token (and an access token secret) $request = $oauth->getAccessToken(); // There we go $access_token = $request['oauth_token']; $access_token_secret = $request['oauth_token_secret']; // Now store the two tokens into another file (or database or whatever): file_put_contents("access_token", $access_token); file_put_contents("access_token_secret", $access_token_secret); // Great! Now we've got the access tokens stored. // Let's verify credentials and output the username. // Note that this time we're passing TwitterOAuth the access tokens. $oauth = new TwitterOAuth($consumer_key, $consumer_secret, $access_token, $access_token_secret); // Send an API request to verify credentials $credentials = $oauth->oAuthRequest( "https://twitter.com/account/verify_credentials.xml", array(), "GET" ); // Parse the result (assuming you've got simplexml installed) $credentials = simplexml_load_string($credentials); // And finaly output some text echo "Access token saved! Authorized as @" . $credentials->screen_name; die(); }
That’s all. It wasn’t difficult, was it? Now that you’ve got your access tokens stored, you can call Twitter API using OAuth at any time! Here’s a brief example:
// Read the access tokens $access_token = file_get_contents("path/to/access_token"); $access_token_secret = file_get_contents("path/to/access_token_secret"); // Initiate a TwitterOAuth using those access tokens $oauth = new TwitterOAuth($consumer_key, $consumer_key_secret, $access_token, $access_token_secret); // Post an update to Twitter via your application: $oauth->OAuthRequest('https://twitter.com/statuses/update.xml', array('status' => "Hey! I'm posting via #OAuth!"), 'POST');
Then setup a cron job to access that page URL and you’ll be automatically tweeting! That’s about it.
Now, in conclusion, a few security suggestions. Never, NEVER place your tokens into a publicly visible folder. Deny all HTTP access to them via .htaccess (look at the Files directive) and yes, I’m going to let you finish, but please, PLEASE secure that hidden place we talked about earlier. Yes I’m repeating this and I’ll keep repeating it over and over. If hackers gain access to your hidden place, they’ll be able to swap your account with another one (or just break it). If they get to your access tokens, then they might spam through your account. That’s not very nice, is it? So please, IP based security, password protected, whatever. I close the whole directory down with a “deny from all” rule in .htaccess once I got my access tokens, so if for any reason I’d have to update or change them, I’d have to do more than just browse there.
That’s all. Have a good time with Twitter OAuth and I hope everything goes well. Feel free to post questions or any kind of feedback in the comments section.
Nice howto. I'm glad you are getting use out of my code.
Thanks Abraham, there's still more to come ;) Your code is awesome, so flexible!
Hi Konstantin,
You have done a wonderful job. It helped me a lot to understand the things so easily.
Thanks Rajiv, glad you liked it.
[…] you haven’t read my previous articles about Automated Serverside Tweeting Using OAuth and PIN-based OAuth Using PHP then make sure you do, because I wouldn’t like to go through […]
[…] few weeks ago we discussed Automated Serverside Tweeting Using OAuth (read this before going on) and I kept looking deeper into security issues, so I found a way to […]
I have used the above code and i have stored the access token and secret in the
database , now in other page when i try to use same access token and access token secret it is giving me "incorrect signature". not sure where i am goin to wrong.
Make sure your servers time is synced with NTP.
what is "NTP" ?
just checked in the net about the term you have mentioned above
I will check with configuration of NTP and will let you know.
yes .. our server time is synched with NTP then also it is giving following array
in return of the status update.
Array
(
[request] => /statuses/update.xml
[error] => Incorrect signature
)
Ok, I have made this and it works!
But what about errors in return values ? How do i catch them to make an user friendly error message or log ?
What happends if there is a timeout error (perhaps twitter down) ??
Maybe try/catch? Or just look at the output before parsing it. And watch for Curl error logging for timeouts.
Dumb question: What exactly is the seven-digit pin that Twitter provides? In other words, once we have that pin, are we sticking it in the URL of our validation page? If so, what variable name are we giving it? I see that you have an "oauth_token=whatever" in your URL, but this seems to be a completely different value (a long, alphanumeric hashed value provided by Twitter). What obvious piece of information am I missing?
Look for oauth_verifier, and read more about OAuth 2 here.
I can't post the same tweet twice!
I've setup a cron job to access the page for every hour, but it won't post until I change the tweet!
how can I make it to tweet the same tweet without problem,
or how can I make twitter think this is a new tweet not the same as previous one?
Essa, actually Twitter doesn't allow you to post duplicate tweets, you might want to change the texts to let them through, or shorten links with different services.
Hi,
i keep getting this error
Warning: array_merge() [function.array-merge]: Argument #2 is not an array in OAuth.php on line 301
Any help would be great
You might need to switch to the newer method of making requests:
<pre>$oauth->post('statuses/update', array('status' => "Hey! I'm posting via #OAuth!"));
</pre>
I'm having the same warning when i'm validating and I believe it has something to do with this line
<pre>$credentials = $oauth->oAuthRequest("https://twitter.com/account/verify_credentials.xml", array(), "GET");
</pre>
If Abrahams solutions also aplies to this, then I'm not getting it :)
You will want to use:
$user = $oauth->get("account/verify_credentials");
Thanks for being here :)