Basic HTTP Authentication with the Slim PHP Framework REST API

When it comes to securing your REST API — authenticating and authorizing users — the situation is a bit interesting because the principle of an API being truly RESTful is that things remain stateless on the server side with each request. What that means is that the server isn’t really supposed to keep track of state in the form of sessions or anything else. To be truly RESTful all of the information that the web application needs to properly handle each request should be contained in the request itself.

If you wanted to, you could just cheat and handle everything using sessions. But according to the Internet, if you do this many kittens will die. So in the interest of at least trying to do things properly and saving the lives of millions of kittens, in the following bit we’re going to look at another one of the more common implementations for REST API authentication and authorization: Basic HTTP Authentication. We’ll be using the Slim Framework — a lightweight PHP REST API to demonstrate this, but the same principles apply if you’re using another framework or even another language.

To have user authentication within your app’s API and remain truly RESTful, it usually inevitably boils down to 2 choices: Basic HTTP Authentication and OAuth. OAuth was discussed previously in this article about using Google’s OAuth in order to access many different Google’s APIs. The developer docs at Twitter also have some good information on these two different forms of authentication.

OAuth is another great option for securing your REST API but it’s slightly more complex to both set up and code against compared to basic HTTP authentication, what we’ll be looking at shortly. Basic HTTP authentication is probably the quickest and easiest way to add some security to your REST API, but if you wanted to try to implement OAuth, you could definitely do that as well. It all just depends on your needs and requirements.

The Basics of Basic HTTP Authentication

So what exactly is basic HTTP authentication? As it turns out, just about any webpage could use HTTP authentication if it was desired (so long as the page was able to set HTTP headers). Did you ever visit a page and have the little password prompt come up (one that was system-level and not part of the website you were visiting)?

Basic HTTP Authentication

What is this? That indicates that a page is likely using HTTP authentication in asking you to provide a username and password. How is this being done? Take a look at the following PHP page below…

<?php 
if(!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) || $_SERVER['PHP_AUTH_USER'] !== 'demo' || $_SERVER['PHP_AUTH_PW'] !== 'demo') {

    header("WWW-Authenticate: Basic realm=\"Secure Page\"");
    header("HTTP\ 1.0 401 Unauthorized");
    echo 'No soup for you';
    exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Basic HTTP Authentication</title>
</head>
<body>

<h1>Secure Page</h1>

<p>This is a page with secure content...</p>

</body>
</html>

Because the WWW-Authenticate headers are included, the user will be prompted to provide a username and password (in this case both need to be ‘demo’) or else the user will not be allowed to see the content in the page. By doing this you can secure basically any web page or request route that you want. Any page or route secured by this method requires the username and password to be sent with each request. One thing to mention on that note, however, is that it goes without saying that you’d want to be sure to do this authentication over HTTPS in production using an SSL certificate. As is stated on the Wikipedia page for basic access authentication

The BA mechanism provides no confidentiality protection for the transmitted credentials. They are merely encoded with BASE64 in transit, but not encrypted or hashed in any way. Basic Authentication is, therefore, typically used over HTTPS.

You want to be sure that your communication is encrypted. Otherwise someone with a packet sniffer could potentially see your username and password credentials being transferred over the web. Actually if your website our web application has any sort of user login system you should be doing things over HTTPS (but not everyone does).

You may have noticed that there’s also mention of a “realm” in basic HTTP authentication. The realm is a case sensitive string used to describe groupings of pages that use a particular set of credentials. If you authenticate on one page with a certain set of credentials, you should be able to access other pages or routes within the same realm. In this way, you can have different levels and different types of authentication for different pages in different realms.

Basic HTTP Authentication with the Slim Framework

So let’s take a look at how we might be able to add some basic HTTP authentication to a REST API using the Slim Framework to protect our routes. Fortunately the Slim Framework provides middleware to allows us to easily incorporate this. You can find these middleware libraries in the Slim Extras repository here. What we’re interested in is the basic auth component. The code for this (minus the large introductory legal text) can be found below…

<?php

class HttpBasicAuth extends \Slim\Middleware
{
    /**
     * @var string
     */
    protected $realm;

    /**
     * @var string
     */
    protected $username;

    /**
     * @var string
     */
    protected $password;

    /**
     * Constructor
     *
     * @param   string  $username   The HTTP Authentication username
     * @param   string  $password   The HTTP Authentication password
     * @param   string  $realm      The HTTP Authentication realm
     */
    public function __construct($username, $password, $realm = 'Protected Area')
    {
        $this->username = $username;
        $this->password = $password;
        $this->realm = $realm;
    }

    /**
     * Call
     *
     * This method will check the HTTP request headers for previous authentication. If
     * the request has already authenticated, the next middleware is called. Otherwise,
     * a 401 Authentication Required response is returned to the client.
     */
    public function call()
    {
        $req = $this->app->request();
        $res = $this->app->response();
        $authUser = $req->headers('PHP_AUTH_USER');
        $authPass = $req->headers('PHP_AUTH_PW');
        if ($authUser && $authPass && $authUser === $this->username && $authPass === $this->password) {
            $this->next->call();
        } else {
            $res->status(401);
            $res->header('WWW-Authenticate', sprintf('Basic realm="%s"', $this->realm));
        }
    }
}

More information about Slim Framework Middleware can be found in the documentation. The public “call” method is essentially required any middleware in the Slim Framework. It’s the method that gets invoked when middle ware is instantiated.

Modifying the Middleware

Now, in my personal opinion, I don’t find it tremendously useful to have to pass credentials into the constructor if our API was going to have multiple users. So let’s modify this a bit and also do a bit of preparation for running a SQL query on

<?php

class HttpBasicAuth extends \Slim\Middleware
{
    /**
     * @var string
     */
    protected $realm;

    /**
     * Constructor
     *
     * @param   string  $realm      The HTTP Authentication realm
     */
    public function __construct($realm = 'Protected Area')
    {
        $this->realm = $realm;
    }

    /**
     * Deny Access
     *
     */	
    public function deny_access() {
        $res = $this->app->response();
        $res->status(401);
        $res->header('WWW-Authenticate', sprintf('Basic realm="%s"', $this->realm));        
    }

    /**
     * Authenticate 
     *
     * @param   string  $username   The HTTP Authentication username
     * @param   string  $password   The HTTP Authentication password	 
     *
     */
    public function authenticate($username, $password) {
        if(!ctype_alnum($username))
            return false;
        
        if(isset($username) && isset($password)) {
            $password = crypt($password);
            // Check database here with $username and $password
            return true;
        }
        else
            return false;
    }

    /**
     * Call
     *
     * This method will check the HTTP request headers for previous authentication. If
     * the request has already authenticated, the next middleware is called. Otherwise,
     * a 401 Authentication Required response is returned to the client.
     */
    public function call()
    {
        $req = $this->app->request();
        $res = $this->app->response();
        $authUser = $req->headers('PHP_AUTH_USER');
        $authPass = $req->headers('PHP_AUTH_PW');
		
        if ($this->authenticate($authUser, $authPass)) {
            $this->next->call();
        } else {
            $this->deny_access();
        }
    }
}

What we’ve done here is pull the logic that checks the users credentials into its own “authenticate” method. We’ve also created a deny_access method as well (though that might not be entirely necessary at the moment, it may be beneficial to have that present in the future as the auth code becomes more complex as we write it).

Now in this code it should be fairly obvious that it accepts any username and password combination (as long as there is something set). The only case of failure is that the username has to be alphanumeric (assuming that was a requirement for your system). We won’t go that deep into it, but it is here where we’d want to go a step further, connect to our database and validate the user’s credentials against what is stored in the database (or do whatever authentication implementation that you to wanted here). We’d obviously also want to be sure that we’d do the proper sanitation of user input before running our SQL queries. I’m not a black-belt security kung-fu master but I believe that it’s possible that requiring the username to be alphanumeric and creating a crypt hash of whatever the password input was (assuming that was the encryption method you were using to store passwords — and you are using encryption right? ;)) is pretty clean already. But we could also do the additional sanitization methods. In this past, this was using things like mysql_real_escape_string, but now other libraries are preferred. And in the future these in turn will be replaced with others.

Now we have to make sure that we remember to load the middleware. So in the index.php file in the root directory of our main Slim Framework API directory (the one where we instantiate slim and define routes) we just have to load up the middleware…

require 'Slim/Slim.php';
require 'Slim/Middleware.php';
require 'Slim/Middleware/HttpBasicAuth.php';

\Slim\Slim::registerAutoloader();

And after we do that we are going to want to create a new instance of our Basic HTTP Authentication class after we initialize the Slim Framework…

$app = new \Slim\Slim();
$app->add(new \HttpBasicAuth());

What this will do by default is protect all routes by requiring authentication. If we wanted to open up some routes to be publicly available we could perhaps change the code in our HttpBasicAuth class to automatically allow access certain routes, but that’s another discussion for another day.

, , , , 9bit Studios E-Books

Like this post? How about a share?

Stay Updated with the 9bit Studios Newsletter

15 Responses to Basic HTTP Authentication with the Slim PHP Framework REST API

  1. AH says:

    When you login at the web application, I guess the username and the password must be encrypted in the client side right? Or HTTPS is enough?

    Since using SESSION is not RESTful, I guess we should send back a identificator to the user so we can identify him on other pages and requests later right?

    I hope I could avoid the ugly authentification window with some jquery & ajax on the client side…

    Thanks for the article, very useful.

    • Ian says:

      Hi AH — From what I’ve read, HTTPS is enough, but I’m sure there are some security experts out there who would offer suggestions for somehow adding an additional layer of security.

      As for the second part, I think how you implement something like that really depends on what type of application you are building so there’s probably no one exact answer. Username and password are going to be sent with each request so while you’re authenticating that you could grab whatever additional user info you wanted from your DB and send it back. I think it’s pretty open-ended as to what you do beyond basic auth. That would be another really interesting topic to explore: different methods of integrating Basic HTTP Auth user logins into applications (especially with regard to UI/UX).

  2. Arend says:

    Thanks, very helpful. I will use this in combination with Backbone.js and https://github.com/fiznool/backbone.basicauth

  3. Sean says:

    I am a newbie. So how does the web app know your logged in if you don’t set sessions? You would have to send username and password for every single request? Wouldn’t that open yourself up to a security breach?

    • Ian says:

      Hi there Sean. Yes, that always is a concern with Basic Authentication. Like with anything on the web, your credentials are plain text over the wire. Without SSL, if someone on the same network was monitoring network traffic they could see it… but the situation is essentially the same with sessions as those can also be hijacked.

  4. Brandon says:

    I’ve been experimenting with this. I have my API setup to require basic authentication. Would it be useful to require my static basic auth api key (whatever you call it) and check with every api call that the username/password is a match in the db?

    So the header has
    Basic Auth: username
    Basic Auth: password

    DB: username
    DB: password

    —–

    Essentially my setup is you HAVE to use SSL
    You HAVE to have the correct Basic Auth key
    Then I can check with each api call if the username/password matches in the db.

    Any thoughts?

  5. Raheel says:

    Hi Thanks for the tut. I am facing a problem that it ask for authentication only 1 time. after than i visit any route or even close the browser my middleware doesn’t ask for authentication. One more thing is that how would an iphone developer send username and password while making call to my service ?

    Thanks

  6. Josh says:

    In return the user ID after certified for the rest of the application?

    • Ian says:

      Hi Josh — Yes, with the headers set a trust relationship has been set between the client and server so you will not have to sign in every time you make a request.

  7. John says:

    Hi, I follow your instruction but the authentication method always return false and the browser keep asking me the credentials.
    I’m sure I enter the right username and password.

    Also, I’m afraid my $authUser and $authPass are unset/empty. I tried to simply authenticate checking if $authUser is set, and always get false.

Leave a Reply

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