OAuth Support for FreshBooks and the Gory Technical Details
A new feature we recently introduced was OAuth authentication with our FreshBooks integration. What is OAuth? Glad you asked!
OAuth solves the problem of how to share information between sites without giving your password from one site to another.
We wanted to add OAuth support to our FreshBooks integration to give our users a better, more secure experience when they link their FreshBooks account with our service. Instead of having our users enter their API key or password directly into our application, they now grant our service access to their FreshBooks account, entering their login information on the FreshBooks server (see image). This gives our users a better experience when setting up the service and keeps them in control of 3rd party access!
If you’re wondering how to implement an OAuth client I’d like to show you; PHP examples are scarce and the current OAuth libraries leave a lot to be desired, here’s how we implemented the FreshBooks OAuth client:
Register as a consumer
The first step is to register with FreshBooks as a OAuth consumer. Once you register as a consumer, freshbooks will give you a consumer key and a consumer secret, and both will be used in your code.
Getting a request token
As a registered consumer, the first step in an OAuth request is getting a request token; we will make the initial request to the FreshBooks server. First we set some parameters that we will pass to FreshBooks, $oauth_consumer_key and $oauth_consumer_secret are the values FreshBooks gave us once we became a consumer, $callback is the URL that FreshBooks will redirect to after we request the request token. “oauth_nonce” is a random string of characters; in this case a length of 20.
$params = array(
'oauth_consumer_key' => $oauth_consumer_key
,'oauth_callback' => $callback
,'oauth_signature' => $oauth_consumer_secret. '&'
,'oauth_signature_method' => 'PLAINTEXT'
,'oauth_version' => '1.0'
,'oauth_timestamp' => time()
,'oauth_nonce' => $prog->generateKey(20)
);
The next step is to URL encode these params and send them to FreshBooks:
// URL encode our params
$postdata ='';
foreach($params as $key => $value) {
if(!empty($postdata)) $postdata .= '&';
$postdata .= "{$key}=";
$postdata .= urlencode($value);
}
// send the request to FreshBooks
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://{$site_name}.freshbooks.com/oauth/oauth_request.php");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$res= curl_exec($ch);
// parse the request
$r = array();
parse_str($res, $r);
Authorizing the request token
If all goes well, $r['oauth_token], $r['oauth_token_secret'], and $r['oauth_callback_confirmed'] should be set. We can now redirect to FreshBooks server to get our request token authorized by the user. We will pass the $r['oauth_token] to FreshBooks when we do this:
header('location: '. "https://{$site_name}.freshbooks.com/oauth/oauth_authorize.php?oauth_token={$r['oauth_token']}");
Swapping the authorized request token with an access token
Once the request token is authorized the user will be redirected to the callback URL that was specified in the initial request token. We are now ready to swap the authorized request token with an access token. The access token will give us enough information to authenticate with FreshBook’s API. Again, we set up our parameters to send to Freshbooks. oauth_token and oauth_verifier were sent to us by FreshBooks in the query string upon redirect.
$params = array(
'oauth_consumer_key' => $oauth_consumer_key
,'oauth_token' => $_GET['oauth_token']
,'oauth_verifier' => $_GET['oauth_verifier']
,'oauth_signature' => $oauth_consumer_secret. '&'
,'oauth_signature_method' => 'PLAINTEXT'
,'oauth_version' => '1.0'
,'oauth_timestamp' => time()
,'oauth_nonce' => $prog->generateKey(20)
);
Once again, we URL encode our request, send it to FreshBooks server, and parse the response:
// URL encode our params
$postdata ='';
foreach($params as $key => $value) {
if(!empty($postdata)) $postdata .= '&';
$postdata .= "{$key}=";
$postdata .= urlencode($value);
}
// send the request to FreshBooks
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://{$site_name}.freshbooks.com/oauth/oauth_access.php");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$res= curl_exec($ch);
// parse the request
$r = array();
parse_str($res, $r)
If everything went smoothly, $r['oauth_token] and $r['oauth_token_secret'] should be set. We now have all the pieces to make an authenticated API call.
Building the Authorization Header
All that is left is to build the Authentication header that will be sent along with the API request. Here’s how to do it:
$params = array(
'oauth_consumer_key' => self::$oauth_consumer_key
,'oauth_token' => $key['oauth_token']
,'oauth_signature_method' => 'PLAINTEXT'
,'oauth_signature' => self::$oauth_consumer_secret. '&' .$key['oauth_token_secret']
,'oauth_timestamp' => time()
,'oauth_nonce' => $prog->generateKey(20)
,'oauth_version' => '1.0');
$auth ='Authorization: OAuth realm=""';
foreach($params as $kk => $vv)
$auth .= ",{$kk}" . '="'. urlencode($vv) . '"';


5 Responses to “OAuth Support for FreshBooks and the Gory Technical Details”
Tejas
Hi Michael,
Thanks for the Nice article.
I have few questions If you can help me out.
Do I really need the authorization header to send OAuth Parameters once I receive the access token or I can send the parameters in URL Query string as well ?
If yes, How to build authorization header in VB.NET or C#.NET ?
I’m using .NET OAuth Library.
Sorry that I dont understand PHP, So What is “self::” keyword at consumer_key and signature parameters and what it should be in .NET ?
your help will be appreciated.
Thanks
Tejas
michael
Hi Tejas,
The authorization header is a part of the HTTP header can not be a part of the HTTP POST / GET variables. This blog post shows you how to perform the OAuth request manually (making an OAuth request without a library). Your .NET OAuth library should provide nearly all the functionality that is in this blog post. For this blogs purpose you can ignore the “self::” keyword; it’s how you access a static field inside it’s decelerated class.
Thanks,
Michael
Matt
Hi, thanks for the post. Can you demonstrate how to include the Authorization header with cURL? This is my final step, and I am stuck. I create the authorization header string, then I tried using something like this:
curl_setopt($request, CURLOPT_HTTPHEADER, $httpheader);
I get no curl errors returned, but I do get this from Freshbooks: “Authentication failed.”
Any ideas or suggestions would be appreciated. Thanks!
michael
Hi Matt,
Your $httpheader should be an array, each element a line in the header. You would want to do something like:
curl_setopt($request, CURLOPT_HTTPHEADER, array("Authorization: $auth_header"));Let me know if you need any more help.
Thanks,
Michael
Matt
Figured it out. Besides the auth header thing (your suggestion helped also, so thanks), my main mistake was I was not using the client’s FreshBooks account name in the curl calls (when retrieving the request and access tokens), as well as the authorization URL. I was instead using the consumer FreshBooks account name (where the OAuth was enabled), so I was not able to log-in to authorize access as another account.
Thanks again for the help!