Protecting resources with HTTP auth
The Tonic resource and response classes have methods for sending HTTP auth response headers and for processing HTTP auth request headers. All we need to do to protect a resource is to add calls to these methods into the response handling methods we want to protect.
class BasicProtectedResource extends Resource {
var $config = array();
function basicDigestProtectedResource(&$adapter, $data = array()) {
$this->config['username'] = 'user';
$this->config['password'] = 'xyzzy';
$this->config['realm'] = 'My Realm';
parent::resource($adapter, $data);
}
function &get(&$request)
{
if (!$this->_authorisedByBasicAuth($request, $this->config)) {
$response =& new Response(401);
$response->sendBasicAuthHeader($this->config, $this);
return $response;
}
return parent::get($request);
}
}
lib/protectedResource.php
Our get() method above, checks to make sure that the username and password in the resource class match those given in the HTTP Basic auth request header. If not, we create a 401 response object and make it send Basic auth headers for the given realm.
class DigestProtectedResource extends Resource {
var $config = array();
function digestProtectedResource(&$adapter, $data = array()) {
$this->config['username'] = 'user';
$this->config['password'] = 'xyzzy';
$this->config['realm'] = 'My Realm';
$this->config['opaque'] = 'my opaque value';
$this->config['privateKey'] = 'my secret key';
$this->config['life'] = 5;
$this->config['clientAddress'] = $_SERVER['HTTP_REMOTE_IP'];
parent::resource($adapter, $data);
}
function &get(&$request)
{
if (!$this->_authorisedByDigestAuth($request, $this->config)) {
$response =& new Response(401);
$response->sendDigestAuthHeader($this->config, $this);
return $response;
}
return parent::get($request);
}
}
lib/digestProtectedResource.php
So Tonic allows us to easily protect our resources with HTTP auth and to control access from within PHP.
HTTP cookies
Some people aren't a fan of HTTP authentication, I don't know why, those Web browser auth dialogue boxes are awesome, but whatever the reasons, please like using HTML forms and cookies for authenticating people.
The problem with this of course is that people need to re-invent the wheel over and over again, and when it comes to making secure authentication, you want to make sure you've got it right.
So Tonic provides an easy way to use HTTP Cookies for authentication. It works pretty similar to HTTP Digest auth, identifying clients via a time and host dependent hash stored in a HTTP cookie. It is a little more involved than HTTP Basic or Digest auth due to the need to build a form resource and wire them together.
So let's build a form:
mimetype: text/html
class: SmartyResource
<h1>Cookie Auth</h1>
<form action="{$this->url}" method="post">
<label>Username: <input type="text" name="username"></label>
<label>Password: <input type="password" name="password"></label>
<input type="submit">
</form>
resources/form.html
Now we need to protect our resource in a similar way to if we were using HTTP Digest, except on failure we send the form resource as the response, and on receiving a POST request containing the correct username and password we set a cookie containing an authorisation hash.
class CookieProtectedResource extends Resource {
var $config = array();
function CookieProtectedResource(&$adapter, $data = array()) {
$this->config['username'] = 'user';
$this->config['password'] = 'xyzzy';
$this->config['realm'] = 'My Realm';
$this->config['privateKey'] = 'my secret key';
$this->config['life'] = 5;
parent::resource($adapter, $data);
}
function &get(&$request)
{
if ($this->_authorisedByCookieAuth($request, $this->config)) {
return parent::get($request);
} else { // output login form
$form =& Resource::find($this->_adapter, '/form.html');
$response =& $form->get($request);
if ($response->success()) {
$response->statusCode = 401;
$response->setDefaultHeaders($this, $request);
$response->sendCookieAuthHeader($this->config, $this);
}
return $response;
}
}
function &post(&$request)
{
if (
isset($_POST['username']) && $_POST['username'] == $this->config['username'] &&
isset($_POST['password']) && $_POST['password'] == $this->config['password']
) {
$response =& new Response(302, NULL, array('Location' => $request->rootUrl.$this->url)); // success, redirect to GET request on this resource
$response->sendAuthCookie($this->config, $this);
return $response;
}
return $this->get($request);
}
}
lib/cookieProtectedResource.php
We need to pass in the HTTP method and the names of the fields used in our HTML
form for the WWW-Authenticate header that will be sent in a failed HTTP response.
Also, once we've generated our response from our form resource, we need to reset
our response headers with a call to setDefaultHeaders() since we've
changed our status code from a 200 success, to a 401 error response.