Today I’m going to talk about a subject that doesn’t have a lot of coverage on the web.

There is a lot of tutorials about how to use OAuth authentication for twitter services or google & such but there is not a lot of information on how to actually make your own oauth provider.

Now if you are reading this post you probably allready know why Oauth is good and what his purpose is but you can check out some info here.

On this post I’m going to use an basic example case and we will go from nothing to actually handling api calls.

You can download the full source code of a working provider from here. It includes the provider described here as well as a basic client to help you test it out…

Prerequisites

For this entire post I’m going to use the official php oauth library. But you first have to install it from pecl.

At the time i’m writing this article the library at version 1.0 as a bug in the provider class so unless there is now a version 1.1 you’ll have to compile from svn

Dont worry it’s very easy.

sudo apt-get install subversion make php-pear php5-dev libpcre3-dev
mkdir oauthsvn
cd oauthsvn
svn export http://svn.php.net/repository/pecl/oauth/trunk/
cd trunk
phpize
./configure
make
sudo make install
sudo nano /etc/php5/apache2/php.ini # add extension=oauth.so at the end of the file
sudo /etc/init.d/apache2 restart

Now if version 1.1 is available you can just do

sudo apt-get install php-pear php5-dev libpcre3-dev
sudo pecl install oauth
sudo nano /etc/php5/apache2/php.ini # add extension=oauth.so at the end of the file
sudo /etc/init.d/apache2 restart

Our example

Summary

To start I’m going to try to sumarize what our provider should be able to do

  • Generate a request token
  • Authenticate a user
  • Generate a access token
  • Handle a basic API call

Now I’m going to explain in plain (bad) english how it works

The client gets a consumer_key & consumer_secret from you ( this is done only once )

When the client wants to get login credidential for a user, they ask the provider for a request token and they specify a callback url

the provider handles the request and if everything is in order returns a token & a token_secret

The client can then redirect the user to the provider login form with the token (oauth/login?oauth_token=xxxx)

If the user authenticates on our server we generate a verifier (associated with the request token) and redirect the user to the callback url specified
in the request token request (callbackurl?oauth_token=xxxx&oauth_verifier=yyyy)

Now the the client ask for an access token with the oauth_token, oauth_secret & verifier

If everything is correct, the provider generates an access token associated with a user & a consumer.

Please note that this tutorial covers only the provider part !

First of all Let’s explain our DB schema.

You can find a sql file here

user table

Collumn Type
id int , auto_increment
login varchar(50)

consumer table

Collumn Type
id int , auto_increment
consumer_key varchar(250)
consumer_secret varchar(250)
active tinyint(1)

consumer_nonce table

Collumn Type
id int , auto_increment
consumer_id int references consumer.id
timestamp bigint
nonce varchar(250)

token table

Collumn Type
id int , auto_increment
type int references token_type.id
consumer_id int references consumer.id
token varchar(250)
token_secret varchar(250)
callback_url varchar(250)
verifier varchar(250)

token_type table

this table is just for reference

1 – Request
2 – Access

Collumn Type
id int , auto_increment
type varchar(50)

Our Objects

Since this is not a tutorial about how to get access data & such I wont give you all the classes but I’ll show you some interface that we will use during this entire tutorial.
It will give you a full overview of what we are doing.

In the source code that I provide you will get actual classes that are not abstract…

Basicly for this provider to work we need 3 objects.

First we need consumers

A consumer is basicly a client that will connect to your provider.

Every consumer needs a consumer_key and a consumer_secret to authenticate to your website.

In this tutorial I added a the field “active” that will give you the power to unable a consumer without actually deleting him.

IConsumer interface

<?php

	interface IConsumer{

		/* return an instance of a IConsumer or return null on not found */
		public static function findByKey($key);

		/* Create in the DB a consumer with a given key & secret */
		public static function create($key,$secret);

		/* Returns if the consumer is active */
		public function isActive();

		/* Returns the consumer key */
		public function getKey();

		/* Returns the consumer secret key */
		public function getSecretKey();

		/* check if nonce exist for a specified consumer */
		public function hasNonce($nonce,$timestamp);

		/* Add a nonce to the nonce cache */
		public function addNonce($nonce);

		public function getId();

	}

?>

The second object that we need is token

Tokens are unique identifiers issued by the server, there are two kinds of tokens
Request token that are temporary tokens used to initialize the authentification process
and Access token that are token that identify an authentificated user.

If tokens are not clear for you you should probably check out the doc at the oauth website.

A token have the following attributes

id – identifier
type – request or access
token
token_secret

When It’s a request token we store the callback info and the verifier

And once the authentification is done we store the user associated with that access token

IToken interface

<?php

	interface IToken{

		/* create a request token */
		public static function createRequestToken(IConsumer $consumer,$token,$tokensecret,$callback);

		/* returns an IToken instance if you can find a token in the db that matches $token otherwhise return false */
		public static function findByToken($token);

		/* returns true if this is a request token otherwise return false */
		public function isRequest();

		/* returns true if this is a access token otherwise return false */
		public function isAccess();

		/* return callback url */
		public function getCallback();

		/* return verifier */
		public function getVerifier();

		/* return type (1 for request - 2 for access) */
		public function getType();

		/* returns the token_secret */
		public function getSecret();

		/* returns the user associated with the access token */
		public function getUser();

		/* sets the verifier in the db*/
		public function setVerifier($verifier);

		/* sets the user in the db*/
		public function setUser(IUser $user);

	}

?>

And finally we need a User that is authenticated

IUser interface

<?php

	interface IUser{

		public function getId();

	}

?>

The provider

First of all Here is the code and I’ll go thru it just after

<?php

	class Provider{

		private $oauth;
		private $consumer;
		private $oauth_error;
		private $user;
		private $authentification_url = "http://localhost/OAuthProviderExample/oauth/login.php";

		public static function createConsumer(){
			$key = sha1(OAuthProvider::generateToken(20,true));
			$secret = sha1(OAuthProvider::generateToken(20,true));
			return Consumer::create($key,$secret);
		}

		public function __construct(){

			/* create our instance */
			$this->oauth = new OAuthProvider();

			/* setup check functions */
			$this->oauth->consumerHandler(array($this,'checkConsumer'));
			$this->oauth->timestampNonceHandler(array($this,'checkNonce'));
			$this->oauth->tokenHandler(array($this,'checkToken'));

		}

		/**
		 * This function check the handlers that we added in the constructor
		 * and then checks for a valid signature
		 */
		public function checkRequest(){
			/* now that everything is setup we run the checks */
			try{
				$this->oauth->checkOAuthRequest();
			} catch(OAuthException $E){
				echo OAuthProvider::reportProblem($E);
				$this->oauth_error = true;
			}
		}

		/**
		 * This function is called when you are requesting a request token
		 * Basicly it disabled the tokenHandler check and force the oauth_callback parameter
		 */
		public function setRequestTokenQuery(){
			$this->oauth->isRequestTokenEndpoint(true);
			$this->oauth->addRequiredParameter("oauth_callback");
		}

		/**
		 * This function generates a Request token
		 * and save it in the db
		 * then returns the oauth_token, oauth_token_secret & the authentification url
		 * Please note that the authentification_url is not part of the oauth protocol but I added it to show you how to add extra parameters
		 */
		public function generateRequestToken(){

			if($this->oauth_error){
				return false;
			}

			$token = sha1(OAuthProvider::generateToken(20,true));
			$token_secret = sha1(OAuthProvider::generateToken(20,true));

			$callback = $this->oauth->callback;

			Token::createRequestToken($this->consumer, $token, $token_secret, $callback);

			return "authentification_url=".$this->authentification_url."&oauth_token=".$token."&oauth_token_secret=".$token_secret."&oauth_callback_confirmed=true";

		}

		/**
		 * This function generates a Access token saves it in the DB and return it
		 * In that process it also removes the request token used to get that access token
		 */
		public function generateAccesstoken(){

			if($this->oauth_error){
				return false;
			}

			$access_token = sha1(OAuthProvider::generateToken(20,true));
			$secret = sha1(OAuthProvider::generateToken(20,true));

			$token = Token::findByToken($this->oauth->token);

			$token->changeToAccessToken($access_token,$secret);
			return "oauth_token=".$access_token."&oauth_token_secret=".$secret;
		}

		/**
		 * This function generates a verifier and returns it
		 */
		public function generateVerifier(){
			$verifier = sha1(OAuthProvider::generateToken(20,true));
			return $verifier;
		}

		/* handlers */

		/**
		 * This function checks if the consumer exist in the DB and that it is active
		 * You can modify it at your will but you __HAVE TO__ set $provider->consumer_secret to the right value or the signature will fail
		 * It's called by OAuthCheckRequest()
		 * @param $provider
		 */
		public function checkConsumer($provider){
			$return = OAUTH_CONSUMER_KEY_UNKNOWN;

			$aConsumer = Consumer::findByKey($provider->consumer_key);

			if(is_object($aConsumer)){
				if(!$aConsumer->isActive()){
					$return = OAUTH_CONSUMER_KEY_REFUSED;
				} else {
					$this->consumer = $aConsumer;
					$provider->consumer_secret = $this->consumer->getSecretKey();
					$return = OAUTH_OK;
				}
			}

			return $return;
		}

		/**
		 * This function checks the token of the client
		 * Fails if token not found, or verifier not correct
		 * Once again you __HAVE TO__ set the $provider->token_secret to the right value or the signature will fail
		 * It's called by OAuthCheckRequest() unless the client is getting a request token
		 * @param unknown_type $provider
		 */
		public function checkToken($provider){
			$token = Token::findByToken($provider->token);

			if(is_null($token)){ // token not found
				return OAUTH_TOKEN_REJECTED;
			} elseif($token->getType() == 1 && $token->getVerifier() != $provider->verifier){ // bad verifier for request token
				return OAUTH_VERIFIER_INVALID;
			} else {
				if($token->getType() == 2){
					/* if this is an access token we register the user to the provider for use in our api */
					$this->user = $token->getUser();
				}
				$provider->token_secret = $token->getSecret();
				return OAUTH_OK;
			}

		}

		/**
		 * This function check both the timestamp & the nonce
		 * The timestamp has to be less than 5 minutes ago (this is not oauth protocol so feel free to change that)
		 * And the nonce has to be unknown for this consumer
		 * Once everything is OK it saves the nonce in the db
		 * It's called by OAuthCheckRequest()
		 * @param $provider
		 */
		public function checkNonce($provider){
			if($this->oauth->timestamp < time() - 5*60){
				return OAUTH_BAD_TIMESTAMP;
			} elseif($this->consumer->hasNonce($provider->nonce,$this->oauth->timestamp)) {
				return OAUTH_BAD_NONCE;
			} else {
				$this->consumer->addNonce($this->oauth->nonce);
				return OAUTH_OK;
			}
		}

		public function getUser(){
			if(is_object($this->user)){
				return $this->user;
			} else {
				throw new Exception("User not authentificated");
			}
		}

	}
?>

First of all you can see that we register three handlers in the constructor.

Those methods are very important and your provider wont work without them.

The checkConsumer method makes sure your consumer exist and that he is active.

The checkNonce makes sure that the timestamp of the oauth request is in a acceptable timeframe and that the nonce used has not been used before

And finally the checkToken checks if the token exist and if it’s an access token request verify that the verifier is correct.

To launch the handlers you have to call the checkRequest method, I did not add the checkRequest in the constructor because If the client is asking

for a request token we need to call setRequestTokenQuery() so that the provider wont launch the token handler.

Everything else is pretty straight forward and should be explained via the method comments.

Oauth controller setup

To make things nice we will handle all Oauth request through a front controller.

So first of all we need to create a directory called oauth

Then create a .htaccess file in this directory to redirect all request to our controller.

Options +FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule	^(.*)$	index.php [QSA,L]

For this example I use those three urls as end point

oauth/request_token

oauth/access_token

oauth/create_consumer

Finally I use oauth/api/user to make an api call

now let’s have a look at our controller

oauth/index.php

<?php
	function __autoload($name){
		require("../class/".$name.".class.php");
	}

	$provider = new Provider();

	if(strstr($_SERVER['REQUEST_URI'],"request_token")){
		$provider->setRequestTokenQuery();
		$provider->checkRequest();
		echo $provider->generateRequestToken();
	} else if(strstr($_SERVER['REQUEST_URI'],"access_token")){
		$provider->checkRequest();
		echo $provider->generateAccessToken();
	} else if(strstr($_SERVER['REQUEST_URI'],"create_consumer")){
		$consumer = Provider::createConsumer();
		?>
		<h1>New consumer</h1>
		<strong>Key : </strong> <?php echo $consumer->getKey()?><br />
		<strong>Secret : </strong> <?php echo $consumer->getSecretKey()?>
		<?
	} else if(strstr($_SERVER['REQUEST_URI'],"api/user")){
		/* this is a basic api call that will return the id of an authenticated user */
		$provider->checkRequest();
		try {
			echo $provider->getUser()->getId();
		} catch(Exception $E){
			echo $E;
		}
	}

?>

And our login form

oauth/login.php

<?

function __autoload($name){
		require("../class/".$name.".class.php");
}

if(isset($_REQUEST['oauth_token'])){
	$request_token = Token::findByToken($_REQUEST['oauth_token']);
	if(is_object($request_token)&&$request_token->isRequest()){
		if(!isset($_POST['login'])){
		?>
			<form method=post>
				<label>Login : </label><input type="text" name="login" /><br />
				<input type="submit" value="Authenticate to this website" />
			</form>
		<?
		} else {
			$user = User::exist($_POST['login']);
			if(is_object($user)){
				$request_token->setVerifier(Provider::generateVerifier($_REQUEST['oauth_token']));
				$request_token->setUser($user);
				/* this is where we redirect to the callback url */
				header("location: ".$request_token->getCallback()."?&oauth_token=".$_REQUEST['oauth_token']."&verifier_token=".$request_token->getVerifier());
			} else {
				echo "User not found !";
			}
		}
	} else {
		echo "The specified token does not exist";
	}
} else {
	echo "Please specify a oauth_token";
}
?>

This basicly check if the user exist and so generates the verifier & redirect to the callback url

Conclusion

If you prefer to have a working example and go from there please get the source code.

I included a client to help you guys get a good understanding on how it works.

If you need any aditional info or you want to say Hello Leave a comment !

Have fun !

Share

Related Posts: