Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using HTTP Authentication

Tags:

I am building a REST API in PHP to work with a JavaScript based app. All requests are handled as JSON and some requests require authentication.

An example request would be:

$.ajax({
    type: 'GET',
    url: 'http://domain.com/api/posts/recent.json',
    headers: {
        Authorization: 'X-TRUEREST ' + $.param({
            username: 'johndoe',
            password: '********'
        })
    },
    success: function(response){
            // Handle response here (includes auth errors too)
    },
    error: function(a,b,c) {

    }   
});

I have based the use of HTTP Authentication headers on the code in this plugin: https://github.com/kvz/cakephp-rest-plugin/blob/master/Controller/Component/RestComponent.php#L532

As you can see they take passed parameters with the header and then they are used to log a user into the system if they are not already. From what I can tell they expect the auth credentials to be passed WITH the request for data.

An example of this is (note not using CakePHP in my example app):

if ( $loggedIn ) { // logged in is true of false based on session existing

    // Then return the JSON as normal

} else {

    // Grab HEADERS INFO

    $headers = $_SERVER['HTTP_AUTHORIZATION'];
    $parts = explode(' ', $_SERVER['HTTP_AUTHORIZATION']);

    // Then use the parts to find a user in the DB... and populate $user

    if($user){

        $this->Auth->login($user); // login the user with our authentication code

        // Then return JSON as normal

    } else {
        print json_encode(array('auth'=>false))
    }

}

A few questions that I have though:

Question 1: Why use the HTTP Authentication and headers? As as far as I can tell, they are not offering me anything unless I am using them incorrectly? Why not just pass the username and password in the data param of the JS?

Question 2: As stated earlier I have based my API design on the Cake plugin above, but from what I can see they ALWAYS pass the username and password with every request and then decide to use it or not if the user is logged in. Is this correct? To me, it seems more likely that authentication and requests for data should be handled separately. Otherwise I would have to store the username and password in my JavaScript somewhere so that I can send it with each request.

Which brings me to the next question...

Question 3: How do I know if a user is logged in? I could set a variable to true and then pass that as part of my JSON with every request, but can a session work with an external device?

like image 270
Cameron Avatar asked Jan 31 '13 20:01

Cameron


People also ask

How do I get http authentication?

A client that wants to authenticate itself with the server can then do so by including an Authorization request header with the credentials. Usually a client will present a password prompt to the user and will then issue the request including the correct Authorization header.

Does HTTP provide authentication?

HTTP provides a general framework for access control and authentication, via an extensible set of challenge-response authentication schemes, which can be used by a server to challenge a client request and by a client to provide authentication information.

How do I access authenticated URLS with HTTP?

We can do HTTP basic authentication URL with @ in password. We have to pass the credentials appended with the URL. The username and password must be added with the format − https://username:password@URL.

How do I set up HTTP basic authentication?

For HTTP basic authentication, each request must include an authentication header, with a base-64 encoded value. Where siteName is the company name you use to log in to Eloqua, and username and password are your Eloqua username and password.

What is HTTP Basic authentication?

HTTP basic authentication is a simple challenge and response mechanism with which a server can request authentication information (a user ID and password) from a client. The client passes the authentication information to the server in an Authorization header. The authentication information is in base-64 encoding.

What is WWW-Authenticate in http?

The HTTP WWW-Authenticate response header defines the authentication method that should be used to gain access to a resource. The WWW-Authenticate header is sent along with a 401 Unauthorized response. Authentication type. A common type is "Basic". IANA maintains a list of Authentication schemes.

What is authentication and how does it work?

It involves communication between client and server using HTTP header where server requests user’s credentials for authentication. The client in response provides the information in the header. Here’s the concept is based on web authentication through HTTP standards to ensure the security of users’ information.

How do I set up HTTP authentication in Apache?

For basic HTTP authentication to work, you will need a file to act as a database of usernames and their corresponding passwords. You can create this with the htpasswd utility, which should be installed with your Apache installation through the apache2-utils library.


2 Answers

If you are designing a REST api, you should adhere to REST principles. There are two important ones to highlight for authentication:

  1. Identification of resources through URIs
  2. State transitions through server-supplied links.

To adhere to principle 1, you need to keep your authentication out of your URI. http://example.org/list-of-stuff?auth-token=12345 is not a different resource from http://example.org/list-of-stuff?auth-token=67890, so it should not have a different URI. Having different URIs also makes it impossible to cache the resource across different users.

In general, if the resource would be different depending on some condition, that condition needs to be in the URI. For example, many websites have a /profile url, but what profile you see depends on the invisible "who is logged in" state. This is not RESTful. Instead the url should contain the user, e.g. /profiles/username. Whether you actually get to see that resource depends on whether you are authorized to see it, which depends on whether you are authenticated as a user that is authorized. Authentication is a separate layer from resource identification. (For example, suppose you have an admin user which can see other people's profiles. If you have just a /profile url, how would you architect a method for him to see other profiles? Clearly the presence of a resource is something different from the ability to see it and from who is looking at it.)

So we have established that authentication should not be via parameters in the URI. Since we are using HTTP, we can either provide it in a header, or provide it outside HTTP itself.

Although it's not very common, some REST apis handle authentication at the SSL layer using client certificates. This is great from a technical standpoint, but the user experience is baffling and terrible. Although this article is from 2008, nothing has improved. This approach is also not easily scriptable from browser JS, and even outside the browser it's cumbersome to write an application that has to provide client certificates. Development on the server side is difficult, too, because most web scripting environments do not give you easy access to SSL-layer stuff at all, let alone more esoteric SSL features like client-certificates. Your application may not be able to know what certificate identity was provided with the request.

So that leaves HTTP in the header. We can either use a traditional cookie-based auth, where we "log in" at a special url and get a token back, or HTTP authentication which is natively supported by HTTP.

HTTP authentication is far superior in terms of RESTful principles because it is stateless. (This makes your third question nonsensical--there is no "logged in" or "logged out" state--you either provided the right credentials for the request or you didn't!) You don't need to ensure you first visited a special url and got a magic token you need to save (which might suddenly expire), you just issue a request with your credentials every time. If you are not authorized to access a resource, there's a specific HTTP response code (401) and header (WWW-Authenticate, Authorization) and set of well-defined behaviors. It's also extensible with different authorization methods. There's a fair amount of javascript support too, at least if you stick to XmlHTTPRequest. (Heck, jQuery 1.7.2 even has username and password options, so you don't even need to base64-encode it yourself!)

The downside is that the only HTTP auth methods that are in common use and well supported are Basic and Digest, and both of them are not very secure. They're probably fine if you only use https, but otherwise they are terrible.

Also HTTP auth is useless for normal browser use by humans: no custom login pages, no way to present "forgot password" functionality or other authentication customizations, and still browser makers have provided no easy way to "log out" (i.e., forget the credentials for the current realm)!

Cookie-based auth provides you the most control, but you need to keep server- and client-side authentication state and worry about a whole host of other security issues, such as session fixation or even what constitutes a session! Is it IP address, user agent, some combination? How long should an authenticated session be valid before we expire it? What if there's a proxy involved and the IP address changes frequently? What should we do when the magic token expires? What should we do when the user is not authorized? (You can't use an HTTP 401 response--you need to define your own method specific to your site.) In essence, you need to define your own complex session and authentication protocol or adopt someone else's. At least with HTTP auth, the only thing you need to worry about is an attacker reading your Authenticate headers, and using SSL solves that problem.

like image 54
Francis Avila Avatar answered Oct 08 '22 05:10

Francis Avila


Question 1: Why use the HTTP Authentication and headers? As as far as I can tell, they are not offering me anything unless I am using them incorrectly? Why not just pass the username and password in the data param of the JS?

HTTP Authentication here appears to be a personal choice of the developer. You could use OpenID. You could use a token. You could use HTTP Header Authentication. You could use sessions/cookies. Heck, you could even use RSA Key Authentication or SSL Client Certificates if you liked.


Question 2: As stated earlier I have based my API design on the Cake plugin above, but from what I can see they ALWAYS pass the username and password with every request and then decide to use it or not if the user is logged in. Is this correct? To me, it seems more likely that authentication and requests for data should be handled separately. Otherwise I would have to store the username and password in my JavaScript somewhere so that I can send it with each request.

You shouldn't expose the username and password of anything in plaintext, ever (technically you shouldn't even store it as such, but that's not what the question is about). Use a public token with an API, or a unique cookie ID. Personally, I use $_SESSION["id"] to see if a user is logged into anything. Let the AJAX submit and then map the ID to a token internally, if you have to.


Question 3: How do I know if a user is logged in? I could set a variable to true and then pass that as part of my JSON with every request, but can a session work with an external device?

I have a function I use for this linked in this answer. You should ideally check for a valid login using a session and setting the value of an index (such as $_SESSION["id"]) to a value that is unique to that account (usually, this is their database ID).

Edit: after reading the comments on the OP, you would have to make sure the session cookie was on any device that was logged in before making the request

Do not take a value: true field to be a login, as this is a security vulnerability.

like image 39
Amelia Avatar answered Oct 08 '22 05:10

Amelia