Extending Ian Selby’s RESTful API in PHP – Part 2

Follow Me on Pinterest

Extending Ian Selby’s RESTful API in PHP – Part 2This 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: Blog, Code Share
Tags: , , , , , , , , , , , .
Bookmark: permalink.

Let me know what you think:

Leave a Reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>