When you enter your email and password into the Facebook login page, you get into your account. Then, wherever you go in the site, you always see your photo at the top right corner of the page. Facebook remembers you and doesn't ask for the password again and again. This works thanks to HTTP cookies and is called cookie-based authentication. Even though this mechanism often causes some security problems, it is very popular and simple. Here is how Takes makes it possible in a few lines of code.
First, let's see how it works. Moreover, let's see how I believe it should work.
Step one: The user enters an email and password and clicks "submit". The server receives a POST request with this information inside:
The server matches the provided information with its records and decides what to do. If the information is invalid, it returns the same login page, asking you to enter it all again. If the information is valid, the server returns something like this:
Since the response status code is 303, the browser goes to the page specified in the
Location header and opens the front page of the site. This is what it sends to the server:
The server gets my email from the
Cookie header and understands that it's me again! No need to ask for the password once more. The server trusts the information from the cookie. That's it. That's what cookie-based authentication is all about.
Wait ... What About Security?
Right, what about security? If the server trusts any browser request with a user email in the
Cookie header, anyone would be able to send my email from another place and get access to my account.
The first step to prevent this is to encrypt the email with a secret encryption key, known only to the server. Nobody except the server itself will be able to encrypt it the same way the server needs to decrypt it. The response would look like this, using an example of encryption by XOR cipher with
bamboo as a secret key:
This is not the best encryption mechanism, though; for proper encryption, it's better to use something stronger like DES.
This all sounds good, but what if someone hijacks the traffic between the server and the browser and gets a hold of a properly encrypted email cookie? In this case, the thief would be able to use the same cookie for authentication even without knowing its content. The server would trust the information and let the person into my account. This type of attack is called man-in-the-middle (MITM). To prevent this from happening, we should use HTTPS and inform the browser that the cookie is sensitive and should never be returned to the server without SSL encryption. That's done by an extra flag in the
Set-Cookie header, called
How It's Done in Takes
Here is how this cookie-based authentication mechanism is designed in the Takes framework. The entire framework consists of takes, which receive requests and produce responses (this article explains the framework in more detail). When the request comes in, we should find the authentication cookie in the
Cookie header and translate it to the user credentials. When the response goes out, we should add the
Set-Cookie header to it with the encrypted user credentials. That's it. Just these two steps.
Let's say we have an account page that is supposed to show the current user's balance:
Right after the
request comes in, we should retrieve the identity of the user, encoded inside an authenticating cookie. To make this mechanism reusable, we have the
TkAuth decorator, which wraps an existing take, decodes an incoming cookie, and adds a new
TkAuth header to the request with the user's identification information:
TkAuth receives a request with an authenticating cookie inside, it asks
pass to decode the cookie and return either a valid
Then, when the response goes back to the browser,
pass to encode the identity back into a string and adds
Set-Cookie to the response.
RqAuth decorator uses the header, added by
PsCookie, in order to authenticate the user and create an
How Is It Composable?
This mechanism is indeed very extensible and "composable". Let's say we want to skip authentication during integration testing. Here is how:
Pass and attempts to authenticate the user by asking all encapsulated passes, one by one. The first one in the chain is
PsFake. Using a single boolean argument in its constructor, it makes a decision whether to return a fake identity or return nothing. With just a single boolean trigger, we can switch off the entire authentication mechanism in the app.
Let's say you want to authenticate users through Facebook OAuth. Here is how:
When a user clicks on the login link on your site, the browser goes to
facebook.com, where his or her identity is verified. Then, Facebook returns a
302 redirection response with a
Location header set to the URL we provide in the login link. The link must include something like this:
?PsByFlag=PsFacebook. This will tell
PsByFlag that this request authenticates a user.
PsByFlag will iterate through all encapsulated "pairs" and try to find the right one.
PsFacebook will be the first and the right one. It will connect to the Facebook API using the provided credentials and will retrieve all possible information about the user.
Here is how we can implement a logout mechanism:
Now, we can add
?PsByFlag=PsLogout to any link on the site and it will log the current user out.