This blog post follows on from Extending Ian Selby’s RESTful API in PHP – Part 1 and will talk about how to add security to your RESTful api.
It is important that we can implement the authentication for our RESTful api’s on a call-by-call basis as, for example, maybe we would want to lock down a POST call that adds a user to the database but we are not looking to lock down a call to get a users list of favourite bands. The following code snippets will show you how to implement call authentication through one line of code in your api calls.
The first thing we do is add a series of constants to our configuration file to determine username, password and realm. You will see in Part 1 of this article that we are setting up these in our config.inc.php. (see below)
//API CREDENTIALS
//set the api login credentials
define("AUTH_REALM", $_SERVER["HTTP_HOST"]);
define("AUTH_USERNAME", "ENTER_API_USERNAME");
define("AUTH_PASSWORD", "ENTER_API_PASSWORD");
define("AUTH_ON", false);
You can see that we enter our username and password in this configuration and change our AUTH_ON constant to be true to switch on authentication for our API. To implement security on the api I have amended Ian Selby’s authentication code found on this page (last snippet) to also allow both Basic and Digest authentication as we do much Flash work here so the applications we built would require Basic security as well as Digest.
public static function performAuthentication($server, $security = "Basic")
{
//check if the user agent has been set
if (isset($_SERVER['HTTP_USER_AGENT']))
{
//here we determine the user agent, if it is flash - force basic authentication
$flash = strpos($_SERVER['HTTP_USER_AGENT'], "Flash Player");
//if flash is found
if ($flash)
{
//force security to basic authentication as Flash does not
//support digest
$security = "Basic";
}
}
//get the calls request variables
$data = $server->GetRequestVars();
//check if authentication is switched on in inc/config.inc.php
if (AUTH_ON)
{
//determine security
switch($security)
{
//basic authentication
case "Basic":
//handle all basic authentication
//check if the username and password are set
if (!isset($_SERVER['PHP_AUTH_USER']) && !isset($_SERVER['PHP_AUTH_PW']))
{
//set the 401 permission error response headers
header('WWW-Authenticate: Basic realm="' . AUTH_REALM . '"');
header('HTTP/1.0 401 Unauthorized');
//401 > Unauthorized
die(RestUtils::SendResponse(401));
}
else
{
//determine if credentials are correct
if ($_SERVER['PHP_AUTH_USER'] != AUTH_USERNAME || $_SERVER['PHP_AUTH_PW'] != AUTH_PASSWORD)
{
//set the 401 permission error response headers
header('WWW-Authenticate: Basic realm="' . AUTH_REALM . '"');
header('HTTP/1.0 401 Unauthorized');
//401 > Unauthorized
die(RestUtils::SendResponse(401));
}
}
break;
//digest authentication
case "Digest":
//handle all digest authentication
// figure out if we need to challenge the user
if(empty($_SERVER['PHP_AUTH_DIGEST']))
{
//set 401 headers (AUTH_REALM set in config.inc.php)
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="' . AUTH_REALM . '",qop="auth",nonce="' . uniqid() . '",opaque="' . md5(AUTH_REALM) . '"');
// show the error if they hit cancel
//401 > Unauthorized
die(RestUtils::SendResponse(401));
}
// now, analayze the PHP_AUTH_DIGEST var
if(!($data = RestUtils::HTTPDigestParse($_SERVER['PHP_AUTH_DIGEST'])) || AUTH_USERNAME != $data['username'])
{
// show the error due to bad auth
//401 > Unauthorized
die(RestUtils::SendResponse(401));
}
// so far, everything's good, let's now check the response a bit more...
$A1 = md5($data['username'] . ':' . AUTH_REALM . ':' . AUTH_PASSWORD);
$A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']);
$validResponse = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
// check responses match
if($data['response'] != $validResponse)
{
//401 > Unauthorized
die(RestUtils::SendResponse(401));
}
break;
}
}
return;
}
Inside the setAuth method in the RestRequestClient class we amend as follows:
protected function setAuth(&$curlHandle)
{
if ($this->username !== null && $this->password !== null)
{
switch($this->GetSecurity())
{
//basic authentication
case "Basic":
curl_setopt($curlHandle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
break;
//digest authentication
case "Digest":
curl_setopt($curlHandle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
break;
}
curl_setopt($curlHandle, CURLOPT_USERPWD, $this->username . ':' . $this->password);
}
}
You can see that the function takes two arguments, the server instance and the type of security to implement. The server instance is used to look at the request variables and determine if the security has been successful. The type of security is just a flag to implement the security for that particular call. You can also see at the top of the function that there is some code to force flash calls to use Basic authentication for any calls that are made. Obviously this function could be extended to do a database lookup instead of using static variables inside the configuration file….
The next and final step is to add the function call to your api calls. This is achieved very easily by adding the following line to your call.
//check if authentication is on
if (AUTH_ON)
{
//perform authentication on this api call
RestUtils::performAuthentication($requestResponse);
}
You can see that this function call is made if the authorisation has been switched on. If you do not want the authentication to be implemented for a certain call you would just omit this line from your code.
Thats it!
I hope you found this useful and thanks to Ian Selby for his excellent RESTful api code which has helped me build various projects with his implementation.


Filed Under:
Tags:
Bookmark: