If you’ve ever integrated with another API that requires security (such as Twitter), you’ve probably consumed an OAuth service. In this article, I’ll explore what it takes to create your own three-legged OAuth server allowing you, for example, to create your own secure API which you can release publicly. When dealing with OAuth, you will typically see it implemented as a two-legged or three-legged OAuth server. The major difference between them is that two-legged authentication doesn’t involve another user. For example, if you want to access a specific user’s Twitter information, you would consume the three-legged server because an access token must be generated for the user in your application, versus just Twitter providing you a token. We’ll focus on the three-legged variety since it’s more practical for real-world use. We’ll use oauth-php to perform a lot of the heavy lifting for us. The library is hosted on Google Code and is not listed in Packagist, but it can still be installed using Composer. For details, check out the composer.json file in code that accompanies this article available on GitHub.
Understanding the Flow
When using a three-legged OAuth server, the typical flow that a developer would take to implement and consume the service is as follows:- The consumer requests a token from the server
- The consumer then directs the user to a login page, passing the token with them
- The user logs in and is redirected back to the consumer with an access token
- The consumer takes the access token and requests the OAuth token to use with future secure requests
- The OAuth token is retrieved, and the developer can now make secure requests by passing the token for validation
Setting up the Database
With the oauth-php library in an accessible location, a new database needs to be created and initialized. I’ll be using the schema script found inlibrary/store/mysql/mysql.sql
.
If you browse through the tables, you’ll see that the oauth_server_registry
table contains a field called osr_usa_id_ref
. This is populated during the registration process by the OAuth server. It assumes you have an already existing users table that it will be related to. If you do, that’s perfect! But if not, then here is some basic SQL to create a standard user table:
CREATE TABLE users (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL DEFAULT '',
password VARCHAR(255) NOT NULL DEFAULT '',
email VARCHAR(255) NOT NULL DEFAULT '',
created DATE NOT NULL DEFAULT '0000-00-00',
PRIMARY KEY (id)
);
Creating the OAuth Server
Let’s start writing the OAuth server. The following is common to the rest of our code, so I’ve placed it in the separate fileinclude/common.php
:
<?php
require_once '../vendor/autoload.php';
session_start();
// Add a header indicating this is an OAuth server
header('X-XRDS-Location: http://' . $_SERVER['SERVER_NAME'] .
'/services.xrds.php');
// Connect to database
$db = new PDO('mysql:host=localhost;dbname=oauth', 'dbuser', 'dbpassword');
// Create a new instance of OAuthStore and OAuthServer
$store = OAuthStore::instance('PDO', array('conn' => $db));
$server = new OAuthServer();
The file adds an additional HTTP header to each request to inform clients that this is an OAuth server. Notice that it references services.xrds.php
; this file is provided with the example that comes with the oauth-php library. You should copy it from example/server/www/services.xrds.php
to the root public directory of the web server.
The next few lines of code establish a connection to the database (the connection information should be updated accordingly to your own set up) and creates new instances of OAuthStore
and OAuthServer
objects provided by the library.
The set up for the OAuth server is now complete and the server is ready to be fully implemented. In the remaining examples, the includes/common.php
file must be included each time to instantiate the server.
Allowing Registration
Before developers can consume your OAuth server, they must register themselves with it. To allow this, we need to create a basic registration form. The following fields are required because they are passed to the library:requester_name
and requester_email
. The remaining fields are optional: application_uri
and callback_uri
.
<form method="post" action="register.php">
<fieldset>
<legend>Register</legend>
<div>
<label for="requester_name">Name</label>
<input type="text" id="requester_name" name="requester_name">
</div>
<div>
<label for="requester_email">Email</label>
<input type="text" id="requester_email" name="requester_email">
</div>
<div>
<label for="application_uri">URI</label>
<input type="text" id="application_uri" name="application_uri">
</div>
<div>
<label for="callback_uri">Callback URI</label>
<input type="text" id="callback_uri" name="callback_uri">
</div>
</fieldset>
<input type="submit" value="Register">
</form>
As I mentioned earlier, the library assumes you have existing users who want to consume your server. In the following code, I create a new user in the users
table, then retrieve the ID, and then pass it to the updateConsumer()
method creating (or updating) the consumer key and secret for this user. When you integrate this into your application, this piece should be modified and placed under your existing login process where you already know who the user is who is registering for access.
<?php
$stmt = $db->prepare('INSERT INTO users (name, email, created) ' .
'VALUES (:name, :email, NOW())');
$stmt->execute(array(
'name' => $_POST['requester_name'],
'email' => $_POST['requester_email']
));
$id = $db->lastInsertId();
$key = $store->updateConsumer($_POST, $id, true);
$c = $store->getConsumer($key, $id);
?>
<p><strong>Save these values!</strong></p>
<p>Consumer key: <strong><?=$c['consumer_key']; ?></strong></p>
<p>Consumer secret: <strong><?=$c['consumer_secret']; ?></strong></p>
On completion of the registration, the user’s new consumer key and consumer secret key are outputted. These values should be saved by the user for future use.
Now that a user is registered, they can begin making requests for an access token!
Generating a Request Token
Once a user has registered, they should perform an OAuth request to yourrequest_token.php
file. This file (once again because of the library) is extremely simple:
<?php
require_once 'include/oauth.php';
$server->requestToken();
The requestToken()
method takes care of validating that the user has provided a valid consumer key and signature. If the request is valid, a new request token is returned.
Exchanging the Request Token for an Access Token
The user should be redirected to your login page once a request token has been generated. This page should expect the following URL parameters:oauth_token
and oauth_callback
.
The login page should retrieve the user from the users table. Once retrieved, the user ID is passed (along with the oauth_token
) to the authorizeVerify()
method provided by the library. Assuming the user has authorized the application, the ID of the logged in user is then associated with the consumer’s key allowing them secure access to this user’s data.
The necessary logic of a basic login.php
might look like the following:
<?php
// check if the login information is valid and get the user's ID
$sql = 'SELECT id FROM users WHERE email = :email';
$stmt = $db->prepare($sql);
$result = $stmt->exec(array(
'email' => $_POST['requester_email']
));
$row = $result->fetch(PDO::FETCH_ASSOC);
if (!$row) {
// incorrect login
}
$id = $row['id'];
$result->closeCursor();
// Check if there is a valid request token in the current request.
// This returns an array with the consumer key, consumer secret,
// token, token secret, and token type.
$rs = $server->authorizeVerify();
// See if the user clicked the 'allow' submit button (or whatever
// you choose)
$authorized = array_key_exists('allow', $_POST);
// Set the request token to be authorized or not authorized
// When there was a oauth_callback then this will redirect to
// the consumer
$server->authorizeFinish($authorized, $id);
After the user logs in, they will be redirected back to the consuming developer’s website (via the oauth_callback
parameter) with a valid token. This token and verify key can then be used in the exchange for a valid access token.
A basic access_token.php
file looks like this:
<?php
require_once 'include/oauth.php';
$server->accessToken();
This file is as simple as the previously created request_token.php
. The work is all done inside the accessToken()
method provided by the oauth-php library. Upon a successful request, a valid oauth_token
and oauth_token_secret
are outputted that should be stored and used with future requests to your API.
Validating a Request
At this point, the OAuth server is up and running. But we still need to verify that a request contains a valid OAuth signature. I’ve created a basic test file that does just that:<?php
require_once 'includes/oauth.php';
if (OAuthRequestVerifier::requestIsSigned()) {
try {
$req = new OAuthRequestVerifier();
$id = $req->verify();
// If we have a user ID, then login as that user (for
// this request)
if ($id) {
echo 'Hello ' . $id;
}
} catch (OAuthException $e) {
// The request was signed, but failed verification
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: OAuth realm=""');
header('Content-Type: text/plain; charset=utf8');
echo $e->getMessage();
exit();
}
}
In the example, if the request is verified, I simply echo the user id of the user who logged in. I would suggest creating a re-usable method that contains this code for any API calls that require security.
Testing the OAuth Server
Finally, it’s time to test the OAuth server. Below is a simple test file that performs the above steps to require a user to login and performs a secure request:<?php
define('OAUTH_HOST', 'http://' . $_SERVER['SERVER_NAME']);
$id = 1;
// Init the OAuthStore
$options = array(
'consumer_key' => '<MYCONSUMERKEY>',
'consumer_secret' => '<MYCONSUMERSECRET>',
'server_uri' => OAUTH_HOST,
'request_token_uri' => OAUTH_HOST . '/request_token.php',
'authorize_uri' => OAUTH_HOST . '/login.php',
'access_token_uri' => OAUTH_HOST . '/access_token.php'
);
OAuthStore::instance('Session', $options);
if (empty($_GET['oauth_token'])) {
// get a request token
$tokenResultParams = OauthRequester::requestRequestToken($options['consumer_key'], $id);
header('Location: ' . $options['authorize_uri'] .
'?oauth_token=' . $tokenResultParams['token'] .
'&oauth_callback=' . urlencode('http://' .
$_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF']));
}
else {
// get an access token
$oauthToken = $_GET['oauth_token'];
$tokenResultParams = $_GET;
OAuthRequester::requestAccessToken($options['consumer_key'],
$tokenResultParams['oauth_token'], $id, 'POST', $_GET);
$request = new OAuthRequester(OAUTH_HOST . '/test_request.php',
'GET', $tokenResultParams);
$result = $request->doRequest(0);
if ($result['code'] == 200) {
var_dump($result['body']);
}
else {
echo 'Error';
}
}
OAuth requires timestamps and signatures to be appended to each request, and once again this library will perform this for us.
The first part of the above code is configuration information that should be updated accordingly to match your needs. The user ID, consumer key, and consumer secret key are all generated during the registration process on the server.
As described during the introduction to a three-legged OAuth server, the following process is performed in the above test file:
- Ask for a request token (via the
request_token.php
file) with the consumer key - Upon receiving the token, redirect the user to the login page passing the token and callback URL via URL parameters
- Once the user is logged in, they are redirected back to the above test page. The test page takes the token and asks for an access token (via the
access_token.php
file) - Upon success, the necessary OAuth information is returned and the test file performs a secure request to
test_request.php
. - If all goes well a basic “Hello 1” will be displayed.
Summary
At this point, you should know how to create a basic OAuth server. Using thetest_request.php
file as example, you can begin creating more features that are secured with Oauth! If you’d like to play around with some code, full source code for this article is available on GitHub.
Image via Fotolia
And if you enjoyed reading this post, you’ll love Learnable; the place to learn fresh skills and techniques from the masters. Members get instant access to all of SitePoint’s ebooks and interactive online courses, like Jump Start PHP.
Comments on this article are closed. Have a question about PHP? Why not ask it on our forums?
Frequently Asked Questions (FAQs) about Creating a PHP OAuth Server
What is the role of the authorization server in OAuth?
The authorization server plays a crucial role in the OAuth protocol. It is responsible for interacting with the resource owner (the user) to obtain their consent and authenticate their identity. Once the user’s identity is confirmed and their consent obtained, the authorization server issues an access token to the client. This token is what the client uses to access the protected resources.
How can I secure my PHP OAuth server?
Securing your PHP OAuth server is paramount to protect sensitive user data. You can achieve this by using secure communication channels such as HTTPS, validating all inputs to prevent SQL injection attacks, and regularly updating your server to patch any security vulnerabilities. Additionally, you should limit the scope of access tokens and implement token expiration to minimize the potential damage in case of a token leak.
What is the difference between OAuth1.0 and OAuth2.0?
OAuth1.0 and OAuth2.0 are both protocols for authorization, but they have some key differences. OAuth1.0 requires clients to sign each request with a secret key, making it more complex to implement. On the other hand, OAuth2.0 uses bearer tokens, which are easier to use but potentially less secure if not handled correctly. OAuth2.0 also provides more flexibility with different grant types to suit different use cases.
How can I implement refresh tokens in PHP OAuth server?
Refresh tokens are used to obtain new access tokens without requiring the user to re-authenticate. To implement this, you need to issue a refresh token alongside the access token. When the access token expires, the client can send the refresh token to the authorization server to get a new access token. Remember to implement proper security measures to protect these tokens.
What are the common errors I might encounter when setting up a PHP OAuth server?
Some common errors include invalid client credentials, redirect URI mismatch, and invalid grant types. These errors usually occur due to misconfiguration of the OAuth server or client. Make sure to double-check your configuration and ensure that the client credentials and redirect URIs match what’s registered on the server.
How can I handle multiple scopes in PHP OAuth server?
Scopes define the level of access that the client has to the user’s resources. To handle multiple scopes, you can include a space-separated list of scopes in the access token request. The server should then validate these scopes and include them in the access token response.
Can I use OAuth with mobile applications?
Yes, OAuth is suitable for mobile applications. However, it requires careful implementation to ensure security. For instance, you should use the authorization code grant type with PKCE (Proof Key for Code Exchange) to prevent interception of the authorization code.
How can I revoke access tokens in PHP OAuth server?
To revoke access tokens, you need to implement a token revocation endpoint on your server. This endpoint should accept a token and mark it as revoked in your database. Once a token is revoked, any subsequent requests using that token should be denied.
What is the role of the client in OAuth?
The client in OAuth is the application that wants to access the user’s protected resources. It interacts with the authorization server to obtain an access token, which it then uses to access the resources on the resource server.
How can I test my PHP OAuth server?
You can test your PHP OAuth server using tools like Postman or curl. These tools allow you to send HTTP requests to your server and inspect the responses. Make sure to test all possible scenarios, including successful and unsuccessful authorization requests, token refreshes, and token revocations.
Jamie Munro is the author of Rapid Application Development with CakePHP, 20 Recipes for Programming MVC 3, and most recently 20 Recipes for Programming PhoneGap - all books are available in print and electronic format.