OAuth 2.0 & PHP
OAuth 2.0 & PHP

First, we looked at a simple web page that displays the current time. Looking at the source code, we see JavaScript get the current time, format it for display, create a text node, and insert the node into the DOM in the appropriate place. But this website has a problem: because the JavaScript runs inside your web browser, it is showing you the time that your computer thinks it is. Much more useful would be a page that shows the time that the server thinks it is.

OAuth 2.0 & PHP

For that, we need to add code to the server. To modify files on the server we use MacFuse and SSHFS, from osxfuse.com. An ssh key has been installed on the server, and our .ssh/config has been configured, and ssh-add was used before the presentation started so that we don’t need to type the passphrase now. On our MacBook, we have created an empty directory: ~/mnt/timedemo. We can type “sshfs timedemo:/ mnt/timedemo” … and now we can access the remote server’s files as though they were directly on the Mac. The presenter prefers to use “atom” as his text editor, even though it is “hackable”. If we rename the /var/www/html/index.html file to index.php, and then add a very little bit of PHP code in the middle of the JavaScript – now the website shows the time according to the server’s clock.

This example shows the power of PHP: it is very easy to get started writing code that runs on the web server, and even though PHP is a terrible language, people choose it because it’s so convenient. The convenience makes it particularly useful for small tasks, but unfortunately, as we will see later, some people use PHP for large projects.

<!DOCTYPE html> <html><body>

<center>

<h2>You opened this page at:</h2>

<h1><div id="time"></div></h1>

</center>

<script>

 var d = new Date(<?php echo time()*1000;?>);

 var text = d.toLocaleTimeString() + " on " + d.toLocaleDateString();

 var textnode = document.createTextNode(text);

 var timenode = document.getElementById("time");

 timenode.appendChild(textnode);

</script>

</body></html>

OAuth 2.0 & PHP

Without the PHP, Date() is called with no arguments and returns the current time according to the web browser, i.e. according to the laptop’s system clock. With the PHP, Date() is called with "milliseconds since 1970 according to the server’s clock”. The PHP Hypertext Processor will pass through the HTML unmodified until it detects the <?php … ?> tag, which it will replace with the output of the PHP program.


The timedemo website is protected with HTTP Basic Authentication. A username and password are required to access it. Let’s review how HTTP Basic Authentication works. We looked at a diagram from dailysecurity.net. But suppose we don’t want the timedemo website to know which users are allowed to access it, or their passwords. In fact, we want the authentication checking to be completely separate, on a different server. Let’s discuss how OAuth2 authentication works. We looked at a diagram, from websequencediagrams.com.


A customer has requested that Quoin develop an authentication service. We decided to use OAuth2. The authorization server will be in Java using the Spring framework. And the websites requiring authentication will be written (by customer employees) in PHP using the CodeIgniter framework.


Let’s glance over the CodeIgniter framework to see how support for OAuth2 can be added to a PHP application that was developed using CodeIgniter. And let’s make a sample website, using PHP and CodeIgniter, which requires OAuth2 authentication. We can add a small hook to the main Controller which will, for every request, call an “authenticate_if_needed” function. We can separate our work into the “phpauthdemo” module which is the example app, and the “authentication” module which can be reused in the customer’s app. And since the Java-based authentication server is not ready yet, we will implement our own authentication server in PHP, as the “authserverstub” module. Writing both sides of the protocol does make it easier to understand what’s going on, and to discover potential issues.


The authserverstub can take some shortcuts, such as accepting any username provided that the password “opensesame” is entered, and using transparent reusable strings for the authentication codes, instead of opaque values which can only be used once. OAuth2 allows us to use tokens in whatever format we like. We decided to use JSON Web Tokens. We decided to sign the tokens so that they can be verified for acceptance without needing to ask the authorization server if they’re good. For signing and verification we use the Halite PHP library, which is on top of libsodium. The keys are stored in JSON Web Key format. The phpauthdemo and authserverstub are running on CentOS virtual machines on the presenter’s MacBook. When a page of the phpauthdemo is accessed that requires authentication, the user is redirected to the login screen on the other virtual machine, and then redirected back after a successful login.


Note how the authentication library validates the tokens for every HTTP request, even if the user is accessing a page that does not require authentication, because it is useful for the app to know, even on unauthenticated pages, if there is an authenticated user. The app has a “status” endpoint so that JavaScript can inquire who is authenticated, and for how much longer. The app has a “poke” button to initiate an AJAX request. This demonstrates that when you are performing an AJAX request, it is not appropriate to be redirected to the login page, so a 403 error will be returned instead if your access token has expired. Access tokens may expire after a short time, e.g. 5 minutes. Refresh tokens last longer, e.g. 12 hours, and are used to get new access tokens.


The PHP app is the “client” in OAuth2, and there is no separate “resource server” in this case. The resource server is merely the protected portion of the PHP app website. PHP stores a single cookie in the web browser, which is the key to a dictionary of “session variables” stored on the webserver. The tokens are stored in the PHP session that the web browser has with the client website, and are not accessible by javascript. There was some discussion among presentation attendees regarding whether this is adequately secure, and whether or not it is necessary for the browser to have a cookie from the authorization server as well as the client website.

Conclusion

To support OAuth2 authentication in your web app, you need:

  • Authentication server
  • Account with credentials
  • Agreement with the authentication server on some details

At a minimum, your web app must:

  • Store access tokens for each user
  • Redirect to the login page if the user doesn't have an access token
  • Receive authentication codes and trade them for access tokens
  • Present the access token to the protected resource

Ideally, your web app should also:

  • Store refresh tokens
  • Use refresh tokens to get new access tokens
  • validate access tokens

For single-page apps, detect and replace expired access tokens