Skip links

Writing an OAuth Provider in PHP

19 October 2011 21:45 - by Freek Lijten - 87 comments

Tags: , ,

Earlier I wrote about consuming an OAuth service. This kind of blog posts is reasonably common on the internet. What seems to be less common is posts on providing an OAuth service. Today I hope to share some light on the topic by offering an extensive post on exactly that. As well as my first article the provider will use PECL's OAuth package.

Contents

Usually I don't offer a contents section in an article, but this one is quite long. To give you an idea what to expect, here it is:

  1. The code in this article
  2. OAuth, why and what?
  3. Getting started, offering a consumer key and secret
  4. Setting up the first endpoint
  5. The consumer handler
  6. The timestamp and nonce handler
  7. Outputting the request token
  8. Authorising the request token
  9. Getting an access token
  10. An actual API call
  11. Omissions
  12. References

The Code in this article

As I already wrote, this will be a long article. I was torn (how's that for a heavy term ;) ) between offering as much code as possible and not making this article any longer than strictly necessary. I also wanted to do more than offer some snippets of code which have to be glued together.

As a solution to these problems I decided to work out a full example and make it public through github. It is a work in progress but you can find it here. I will still provide code snippets during this article, but the repository offers a full and working (after setup) example. All code in this article will be based on the example folder that can be found there. If something in the example isn't working, I created a bug. In that case, please contact me trough the contact form or in the comments.

OAuth,  why and what?

If you don't know about OAuth at all, I suggest you start by reading my article on consuming an OAuth service which offers a short explanation on the concept. It gives an explanation of a typical use case as well. After that it would be wise to skim the OAuth specification here.

Since you took my advice (you did, didn't you), you're now up to speed with OAuth. Lets start our journey through OAuth providing land. 

Getting started, offering a consumer key and secret

The very first we need to facilitate is a location where OAuth consumers can apply for a consumer key and secret. The code for this isn't too complicated and my example's implementation doesn't offer a UI but it will do for this article's sake.

$Consumer = new OAuthConsumerModel(Configuration::getDataStore());
$Consumer->setConsumerCreateDate(time());
$Consumer->setConsumerKey(OAuthProviderWrapper::generateToken());
$Consumer->setConsumerSecret(OAuthProviderWrapper::generateToken());
$Consumer->save();

echo "Consumer key: " . $Consumer->getConsumerKey() . "
Consumer secret: " . $Consumer->getConsumerSecret();

In a real world application you would ask a potential consumer a whole lot of questions. The least you'll want is contact information. Remember, once granted a consumer key and secret, a consumer can interact with your service in an almost seamless way. The consumer will be assumed trustworthy by your users, you'll have to make sure that that's justified.

One small note on the Configuration and Provider static functions you may have noticed in the code snippet above. The ProviderWrapper functions will be talked about in detail. The Configuration::getDataStore() is my current solution to have one place where a datastore (mysql in my case) connection is defined. Ideally I would use dependency injection only to pass the datastore around, but because of the callbacks (talked about later in this article) this isn't possible.

Setting up the first endpoint

The first endpoint we need to offer to a consumer is the request token endpoint. The part that executes all the necessary checks and outputs the actual token information looks like this:

$Provider 	= new OAuthProviderWrapper(OAuthProviderWrapper::TOKEN_REQUEST);
$response 	= $Provider->checkOAuthRequest();
if ($response !== true) {
	echo $response;
	exit;
}

try {
	$Provider->outputRequestToken();
} catch (ProviderException $Exception) {
	echo $Exception->getMessage();
}

As you can see the Wrapper class does a lot of work for us. This is way too magical for this article but it demonstrates how much can be abstracted away. 

Ignore the checkOAuthRequest function for now, its purpose and workings will get clear later on in this article. We'll look at the OAuthProviderWrapper's constuctor first. Upon construction the wrapper initialises an OAuthProvider class and sets callback functions disregarding the constant the wrapper is created with.

public function __construct($mode)
{
	$this->Provider = new OAuthProvider();
	$this->Provider->consumerHandler(array($this,'consumerHandler'));
	$this->Provider->timestampNonceHandler(array($this,'timestampNonceHandler'));

	if ($mode == self::TOKEN_REQUEST) {

		$this->Provider->isRequestTokenEndpoint(true);
                //enforce the presence of these parameters
		$this->Provider->addRequiredParameter("oauth_callback");
		$this->Provider->addRequiredParameter("scope");
	} ...
}

As you can see the two callback methods are consumerHandler and timestampNonceHandler. We also need to tell the OAuthProvider that we're dealing with a request token. If we don't a third callback is expected, but more about that later.

You'll also notice the two calls to addRequiredParameter. This tells the OAuthProvider object we expect scope and oauth_callback to be present. If they're not, any request to our request token endpoint should be considered invalid.

The consumer handler

The first of the two callback functions we specified in the previous example is the consumer handler. This will check if the consumer information that is provided with the current request leads to a known consumer. This function is expected to return either of the following PECL OAuth constants: OAUTH_CONSUMER_KEY_UNKNOWN, OAUTH_CONSUMER_KEY_REFUSED or OAUTH_OK. Part of the full function is seen below:

public static function consumerHandler($Provider)
{
	try {
		$OAuthConsumer = OAuthConsumerModel::loadFromConsumerKey($Provider->consumer_key, Configuration::getDataStore());
	} catch (DataStoreReadException $Exception) {
		return OAUTH_CONSUMER_KEY_UNKNOWN;
	}

	$Provider->consumer_secret = $OAuthConsumer->getConsumerSecret();
	return OAUTH_OK;
}

So what happens? A factory method is used to load a Model class. If the consumer key is unknown, the model will throw an exception and an OAuth constant is returned. If the model is found, the Provider that we received trough callback is informed of the corresponding secret. The Provider will need this to verify the signature later on as it only received the consumer key from the Consumer. Finally we let the Provider know all is OK!

You might be wondering what the OAUTH_CONSUMER_KEY_REFUSED could be used for. This constant informs the consumer that its consumer key and secret are (temporarily) not considered valid anymore. This could happen if a provider supports blacklisting or limits request per unit of time. Both options are not found in this example at this moment.

The timestamp and nonce handler

The second callback function checks two things: the timestamp and the nonce (the setter method's name is kind of a give away :) ). Both the nonce and the timestamp are required by the OAuth specification. The nonce and timestamp are a barrier against replay attacks. The nonce is basically there to prevent someone listening to a conversation between a consumer and provider from storing and repeating a succesful request.

An attacker can't forge a request since he lacks the shared secrets, but a successfully call to an API which results in data could be eavesdropped upon and made over and over again. With a number in place that changes every request (thus changing the signature which can only be created with the shared secret which is not in the attackers possession), eavesdropping is rather useless. The request has been sent already and won't be valid a second time.

This following quote comes from the RFC and tells us more about the nonce and timestamp:

A nonce is a random string, uniquely generated by the client to allow the server to verify that a request has never been made before and helps prevent replay attacks when requests are made over a non-secure channel. The nonce value MUST be unique across all requests with the same timestamp, client credentials, and token combinations.

To avoid the need to retain an infinite number of nonce values for future checks, servers MAY choose to restrict the time period after which a request with an old timestamp is rejected.

With that in mind look at the following code:

public static function timestampNonceHandler($Provider)
{
	if (time() - $Provider->timestamp > 300) {
		return OAUTH_BAD_TIMESTAMP;
	}

	if (OAuthNonceModel::nonceExists($Provider->nonce, Configuration::getDataStore())) {
		return OAUTH_BAD_NONCE;
	}

	$OAuthNonce = new OAuthNonceModel(Configuration::getDataStore());
	$OAuthNonce->setId($Provider->nonce);
	$OAuthNonce->setNonceConsumerKey($Provider->consumer_key);
	$OAuthNonce->setNonceDate(time());
	$OAuthNonce->save();

	return OAUTH_OK;
}

As you can see we ignore all requests older than 5 minutes. This quite a large window, but we must keep in mind that servers are most likely not synchronised at the exact same time. If the timestamp checks offers no problems, we check to see if the nonce already exists in our database. If it does we invalidate the request, if it doesn't we create a new model and save it. Return values consist once again of the OAuth package predefined constants

Outputting the request token

Untill now we've seen two handlers being defined but how are they called? For that a seperate function exists which does little more than call the same function at the OAuthProvider. This function is called at the endpoint after construction (see above if you forgot). 

public function checkOAuthRequest()
{
	try {
		$this->Provider->checkOAuthRequest();
	} catch (Exception $Exception) {
		return OAuthProvider::reportProblem($Exception);
	}
	return true;
}

The function call above will also result into a check for the validity of the received signature and the presence of required parameters (oauth_callback and scope). Luckily for us those happen internally and are not setup by ourself. We're now at a point where a request has come in and all we needed to check has been checked. We therefore can issue a new request token. This function looks like this:

public function outputRequestToken()
{
	$token 		= OAuthProviderWrapper::generateToken();
	$tokenSecret 	= OAuthProviderWrapper::generateToken();
	$RequestToken 	= new OAuthRequestTokenModel(Configuration::getDataStore());

	$RequestToken->setToken($token);
	$RequestToken->setTokenSecret($tokenSecret);
	$RequestToken->setTokenDate(time());
	$RequestToken->setTokenConsumerKey($this->Provider->consumer_key);
	$RequestToken->setTokenCallback($_GET['oauth_callback']);
	$RequestToken->setTokenScope($_GET['scope']);
	$RequestToken->save();
	
	echo "oauth_token=$token&oauth_token_secret=$tokenSecret&oauth_callback_confirmed=true";
}

Nothing really exciting is happening here. We use the generateToken function from the OAuthProvider to do our hard work, store some data in a model and return a string per the rfc's specifications (see the end of this paragraph). The endpoint exits its executing after this call and we've finished the first part of our OAuth dance with the consumer!

Authorising the request token

Now the consumer has received a request token and secret from us the second part of the dance starts. The consumer must redirect its user to our service's authorisation page. The user must be made very much aware of what he is about to consent to. A provider should double check identy by logging in once again. Even when a user is already logged in, it is advisible to do so. See what the RFC has to say on this:

However, the server MUST first verify the identity of the resource owner.

When asking the resource owner to authorize the requested access, the server SHOULD present to the resource owner information about the client requesting access based on the association of the temporary credentials with the client identity. When displaying any such information, the server SHOULD indicate if the information has been verified.

When a user does login and grants permission we will lookup the user id and store it our request token model. Secondly a verification code is generated and also stored at the request token model. Then the user is redirected back to the callback URL that was provided in the initial request. Below you see the code starting from the part where the user is already looked up in the database:

if ($row['user_password'] != $_POST['user_password']) {
	echo "You hacker, be gone!";
	exit;
}

$verificationCode = OAuthProviderWrapper::generateToken();
$RequestToken->setTokenVerificationCode($verificationCode);
$RequestToken->setTokenUserId($row['user_id']);
$RequestToken->save();

header( 'location: ' . $RequestToken->getTokenCallback() . '?oauth_token=' . $RequestToken->getToken() . '&oauth_verifier=' . $verificationCode );

As you can see this isn't the largest and hardest part of the dance so that leaves us with time for questions :) What is the use of the verification code you ask? The verification code is sent back to the consumer to ensure that the user granting access is the same user that will be coming back to complete the last part of our OAuth dance. Now we've issued a request token and the user has authorised it, there is only one more step before our API can be used.

Getting an access token

The last part of our dance evolves around the access token. Our wrapper at this endpoint is constructed with a different constant as before making sure the correct code in the constructor will be reached:

$this->Provider = new OAuthProvider();
$this->Provider->consumerHandler(array($this,'consumerHandler'));
$this->Provider->timestampNonceHandler(array($this,'timestampNonceHandler'));

if ($mode == self::TOKEN_REQUEST) {
...
} else if ($mode == self::TOKEN_ACCESS) {
	$this->Provider->tokenHandler(array($this,'checkRequestToken'));
} ...

Earlier I mentioned the existence of a third handler. This is the tokenHandler which is required for any non-request token request. Inside the request token handler we can perform checks before issuing an access token, so lets see what our trusted RFC has to say on this topic:

The server MUST verify (Section 3.2) the validity of the request, ensure that the resource owner has authorized the provisioning of token credentials to the client, and ensure that the temporary credentials have not expired or been used before. The server MUST also verify the verification code received from the client.

This is how that looks in code:

public static function checkRequestToken($Provider)
{
	$DataStore = Configuration::getDataStore();

	//Token can not be loaded, reject it.
	try {
		$RequestToken = OAuthRequestTokenModel::loadFromToken($Provider->token, $DataStore);
	} catch (DataStoreReadException $Exception) {
		return OAUTH_TOKEN_REJECTED;
	}

	//The consumer must be the same as the one this request token was originally issued for
	if ($RequestToken->getTokenConsumerKey() != $Provider->consumer_key) {
		return OAUTH_TOKEN_REJECTED;
	}

	//Check if the verification code is correct.
	if ($_GET['oauth_verifier'] != $RequestToken->getTokenVerificationCode()) {
		return OAUTH_VERIFIER_INVALID;
	}

	$Provider->token_secret = $RequestToken->getTokenSecret();
	return OAUTH_OK;
}

If the the checkOAuthRequest in the access token endpoint doesn't fail the outputAccessToken function is called. This looks a lot like the outputRequestToken function. It has one addition however, the request token is no longer necessary so it is deleted:

public function outputAccessToken()
{
	$token 			= OAuthProviderWrapper::generateToken();
	$tokenSecret 	= OAuthProviderWrapper::generateToken();
	$AccessToken 	= new OAuthAccessTokenModel(Configuration::getDataStore());

	$AccessToken->setAccessToken($token);
	$AccessToken->setAccessTokenSecret($tokenSecret);
	$AccessToken->setAccessTokenDate(time());
	$AccessToken->setAccessTokenConsumerKey($this->Provider->consumer_key);
	$AccessToken->setAccessTokenUserId($RequestToken->getTokenUserId());
	$AccessToken->setAccessTokenScope($RequestToken->getTokenScope());
	$AccessToken->save();
	
	//The access token was saved. This means the request token that was exchanged for it can be deleted.
	$RequestToken->delete();	

	echo "oauth_token=$token&oauth_token_secret=$tokenSecret";
}

Hurray we have access token! Or more precisely: our consumer does. At this point there is only one small thing left. Securing our API with OAuth.

An actual API call

At this point the consumer has all the data it needs to make an authorised call to our API and get the personalised and authorised data. If you check out the example you'll notice there is a user_messages table. Let see how our very simple API would look if it wishes to open up that data to the world:

$Provider 	= new OAuthProviderWrapper(OAuthProviderWrapper::TOKEN_VERIFY);
$response 	= $Provider->checkOAuthRequest();
if ($response !== true) {
	echo $response;
	exit;
}

$userId      = $Provider->getUserId();
$sql         = "SELECT * FROM `user_messages` WHERE `user_id` = '" . $userId . "'";
$result      = Configuration::getDataStore()->query($sql);

//Token is valid, lets output something
$returnValue = "";
while ($row = $result->fetch_assoc()) {
	$returnValue .= "" . $row['message_text'] . "";
}
$returnValue .= "";
echo $returnValue;

The code above once again constructs an OAuthProviderWrapper, but this time with the TOKEN_VERIFY constant. This tells the constructor our current token handler is the checkAccessToken function. Indirectly this is called trough the checkOAuthRequest function. If authorisation is successful we output some data. The output part isn't interesting, but lets take a look at the checkAccessToken as our final code example:

public static function checkAccessToken($Provider)
{
	$DataStore = Configuration::getDataStore();

	//Try to load the access token
	try {
		$AccessToken = OAuthAccessTokenModel::loadFromToken($Provider->token, $DataStore);
	} catch (DataStoreReadException $Exception) {
		return OAUTH_TOKEN_REJECTED;
	}

	//The consumer must be the same as the one this request token was originally issued for
	if ($AccessToken->getAccessTokenConsumerKey() != $Provider->consumer_key) {
		return OAUTH_TOKEN_REJECTED;
	}

	$Provider->token_secret = $AccessToken->getAccessTokenSecret();
	return OAUTH_OK;
}

If the token can't be loaded, or if the consumer isn't the one we expect the token is rejected, but otherwise there is nothing wrong. Basically this all there is to it. We've just checked the last request we needed to check and data has been provided to the consumer. Please read the omissions paragraph to see what is missing or can be improved, but otherwise be happy you've read through all these words :)

Omissions

While reading along you might have noticed stuff missing once or twice. You should definitely take a look into blacklisting consumers and you must take a look at limiting access by a unit of time. Also we let the consumer specifically pass us a scope. Yet we did nothing with it. Depending on the amount of different datamodels you want to open up through your API you should also take a look into that direction. Obviously it would be brilliant if this article had covered this as well, but it is long enough already. Besides, there must be something left for you to figure out :)

References

While writing the proof of concept of the OAuth provider I read Rasmus Lerdorfs post on providing OAuth and while rewriting that proof of concept into a structured piece of code I stumbled accross Lorna Jane Mitchells series on the topic. Though an RFC is not always fun to read, that too was invaluable. 

An extremely well written and comprehensive guide to general OAuth can be found here. Finally the documentation to the OAuth PECL package (scarsely present, certainly as compared to many other parts of the PHP.net site) can be found here.

Share this post!

Comments

  1. DaveDave Wrote on 31 October 2011 04:48

    I have a network of forums, with users who are active on several of them. I would like to give them one ID and allow access to my whole network. So by providing an OAuth service, you wouldn't have to use Twitter, you can actually roll you own authentication that could be used across several site?

  2. efeefe Wrote on 31 October 2011 09:56

    Thanks

  3. Freek LijtenFreek Lijten Wrote on 31 October 2011 13:42

    Hey Dave,

    OAuth is not the best solution to the problem you just described. You want some sort of Single Sign-on (SSO) solution. OpenID might be what you really want as far as I can deduce from your reaction.

    Just for the record, OAuth and OpenID are not the same. This article explains why quite well.

    However if you're sure you want to use OAuth, I think other people do it too. I have read of people using OAuth as authentication meganism at least, but I haven't seen or used this actively. If examples for that use Twitter or Google for instance, they could indeed be replaced by your own OAuth provider perfectly well.

    I'm sorry I can't be of more help, but I've never implemented an OpenID server or used OAuth in that way before :)

  4. Eric WangEric Wang Wrote on 13 November 2011 14:15

    Hi,Freek Lijten.I love this blog and I want to ask you some questions about OAuth.

    I'am developing an android application,and I have a small website,which can provide some RESTful API.I want to add OAuth to these API.And I need2-legged OAuth,because I want user could directly input username and password,by the android app, and finish the OAuth flow.

    My question is,how should I build this2-legged OAuth interface by php?Thank you.

  5. Freek LijtenFreek Lijten Wrote on 14 November 2011 13:54

    Hey Eric,

    First of all: Thanks! :)

    I understand you're going to let users enter usernames and passwords inside the application you are building. If your users should input a username and password, why would you bother going trough the hassle of OAuth at all? One of the main points of OAuth is avoiding exactly that. It is easier and probably better to simply provide a secure URL using SSL/TLS (https) so users can safely transfer their login data over the internet.

    I don't know of a lot of 2-legged OAuth providers, but I have worked with Google's API's and whatthey call 2-legged OAuth before (see this link to their documentation).

    The way it works is as follows. Basically the whole authentication cycle resulting in an access token is not used. An access token is not a requirement when accessing a protected resource. Simply having the consumer key and secret is enough to make a signed and successful request to a protected resource. This means that they shouldn't be exposed to anyone who does not need to know about it.

    Using this technique in a mobile app sounds like a bad idea to me, because the mobile app's source code is essentially available to the public. By decompiling the source code and filtering out the consumer key and secret, everybody can access the data.

    Having said that, I don't think there is an official specification for 2-legged OAuth at all. I believe other parties have different definitions for what 2-legged OAuth actually means or should mean. As I said before I think you'd better look for a simpler solution in this specific case anyway.

    Cheers,

    Freek

  6. xhinkingxhinking Wrote on 20 November 2011 14:56

    Thank you Freek!

    You are right, I don't need Oauth in my project:) HTTPS is enough.

    Thank you again for your patient reply!Looking forward to your new blog.

  7. Wade SweattWade Sweatt Wrote on 25 April 2012 00:27

    I am trying to get a request token from my provider using this URL:

    MYSERVER/oauth/request_token?oauth_callback=http://localhost/client/callback.php

    My php client is running locally on my mac's web server. The array comes back basically empty from the provider, and I get this string from OAuth:

    oauth_problem=parameter_absent&oauth_parameters_absent=oauth_consumer_key%26oauth_signature%26oauth_signature_method%26oauth_nonce%26oauth_timestamp

    I understand that this means I'm missing the consumer key, signature, nonce, and timestamp. However, I don't think the client should be providing this. My conclusion is that there is something wrong with the provider side of things. In my provider, I call these two functions when asking for a request token:

    /
    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; } }

    //AND

    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"; }

    So, I should be getting the information back via the return statement there at the bottom. Any ideas why it is asking for the key, sig, nonce, and timestamp, and why it won't return the request token, signature, and authentication url, etc?

    Thanks so much!

    Wade

  8. Wade SweattWade Sweatt Wrote on 25 April 2012 00:33

    Sorry about the formatting of the code above. It did not look that way in the submit box. If it is not readable, I would be glad to email you something better formatted. Thanks for your help either way.

    Wade

  9. shivanishivani Wrote on 19 November 2012 08:49

    Dave, can you please write php oauth provide for Salesforce as i am not able to write wsdl which will invoke oauthprovider php class.

  10. Hanley HansenHanley Hansen Wrote on 30 December 2012 01:14

    For anyone having trouble with the OAuth Provider class make sure you install the OAuth PECL extension.

    I used this link to install it on CentOS 6 and I got everything working fine now.

    Regards, Hanley Hansen

  11. VincenzoVincenzo Wrote on 04 July 2013 13:24

    Hi Freek, thanks for this guide...I have some problems and i hope you can help me! the code work well when i try to run Oauth on my local server that run with xampp for MAC, but if I put the code on remote server Debian with apache2, I have a little problems.. the get_request_token is very slow, if i try to call the file get_request_token.php i will wait around three minutes before show the login form! also in the next step when i try to login with the correct credentials. I saw also in the apache error log this error: Access denied for user 'www-data'@'localhost' (using password: NO) in .../OAuth/lib/Configuration.php on line

    So i think it could have lost the database connection.

    On the remote server i have https and ssl, I tried to disable https and ssl but i have the same problems.

    For install oauth on debian with php5.x i used this commands: apt-get install php5-dev php-pear apt-get install make libpcre3-dev apt-get install libcurl3-dev pecl install oauth vi /etc/php5/conf.d/oauth.ini

    Added the follow line to php.ini: extension=oauth.so

    For mac i followed this guide: http://www.sumardi.net/2011/06/04/installing-oauth-extension-in-xampp-for-mac-os-x/

    Have you an idea? Do you know if on Debian i have to set more options on the server or i have to install other libraries?

  12. enceinte sans filenceinte sans fil Wrote on 07 November 2013 20:59

    What's up, I read your blog regularly. Your humoristic style is witty, keep up the good work!

  13. trampoline enfanttrampoline enfant Wrote on 07 November 2013 23:25

    I was able to find good advice from your blog articles.

  14. www.disquedurmultimedia.infowww.disquedurmultimedia.info Wrote on 08 November 2013 01:02

    You can definitely see your enthusiasm in thhe article you write. The sector hopes for even moe passionate writers such as you who aren't afraid to mention how they believe. All the time go after your heart.

  15. touchpascher.infotouchpascher.info Wrote on 13 November 2013 13:04

    Excellent site you have here.. It's hard to find high quality writing like yours nowadays. I really appreciate individuals like you! Take care!!

  16. tapis d'eveiltapis d'eveil Wrote on 13 November 2013 22:31

    Hmm is anyone elsae encountering problems with the images on this blog loading? I'm trying to determine if its a problem on my end or if it's the blog. Any feedback would be greatly appreciated.

  17. brosse a dent electriquebrosse a dent electrique Wrote on 17 November 2013 01:29

    Thank you for some other informative web site. The place else coulod I am getting that type of information written in such an ideal manner?

    I have a undertaking that I am just now runninng on, and I have been at the look out for such info.

  18. http://www.designerhandbagsplaza.com/http://www.designerhandbagsplaza.com/ Wrote on 27 November 2013 21:09

    bookmarked!!, I like your blog!

  19. designerhandbagsplaza.comdesignerhandbagsplaza.com Wrote on 18 December 2013 13:49

    Hey! Do yyou know if they make any plugins to protect against hackers? I'm kinda paranoid about losing everything I've worked hatd on. Any suggestions?

  20. waarom geen magentowaarom geen magento Wrote on 31 December 2013 19:13

    Ben nog vrij nieuw op gebied van magento dus zal een simpele vraag zijn maar zoek me een ongeluk naar de juiste oplossing. Wat ik graag zou willen weten:

    wat is nou de beste oplossing/manier om op server A een shop te bouwen (volledig af) en daarna over te zetten naar server B (als vervanging van een oude shop).

    Vragen die bij mij hier over op komen zijn: kan een sql backup zo maar overgezet worden en wat moet er van de ftp overgezet worden?

  21. eidwvmzvhzeidwvmzvhz Wrote on 19 January 2014 00:56

    glkblgsfflmjkufo, <a href="http://www.ontcvripcz.com/">cyogndqocb</a>

  22. mold removal spraymold removal spray Wrote on 20 January 2014 11:28

    This post is actually a pleasant one it helps new internet viewers, who are wishing in favor of blogging.

  23. plpzhbzlouplpzhbzlou Wrote on 21 January 2014 14:01

    sggxhgsfflmjkufo, <a href="http://www.esxxumeabs.com/">kuiswsjpkm</a>

  24. tyszafccxetyszafccxe Wrote on 24 January 2014 16:45

    szrkcgsfflmjkufo, http://www.xvkwzpjaja.com/ zalzmgjled

  25. djfluljxfedjfluljxfe Wrote on 29 January 2014 19:26

    winfegsfflmjkufo, <a href="http://www.awtgnzteul.com/">rbbktmxqoe</a> , [url=http://www.mmwffjmsyo.com/]oluwovtupo[/url], http://www.kywtkassae.com/ rbbktmxqoe

  26. GD45GD45 Wrote on 16 April 2014 05:14

    GY

  27. pirate baypirate bay Wrote on 20 April 2014 09:41

    But in 2004 the site was run separately just as one independent organization. Sites for example i - Tunes have proven that despite the huge number of pirate sites that folks are still pleased to pay to legally download music in the Internet. Taking a cue from that, if publishers will make their ebooks available to a larger audience instantly, it could and will encourage readers to go for that authentic version.

  28. muscle building diet plan for menmuscle building diet plan for men Wrote on 03 May 2014 11:00

    muscle building diet plan for men

    Over-time with frequent washing and following a really healthy diet in between you can lose weight but the main purpose would be to enable your body expel toxins that restrict weight control. Infact, you could feel mild and quite empowered following the cleansing.

    super citrimax clinical strength

    After the three times you can slowly introduce more foods but make certain they are healthier. Heading back to sweets and unhealthy foods and fat can simply undo whatever you've completed. Repeat the 3 day cleanse the following month and research somewhat. Attempt one day of liquid fasting about the second-day or lengthen the 3 day detox diet for a few more times. It will improve your health.

    appetite suppressant herbs

    Dinner: Have another big salad or perhaps a full bowl of steamed veggies with brown rice, quinoa,or millet. Incorporate lots of chopped fresh herbs for flavour.

    Buy Yacon Syrup Intake of monounsaturated and polyunsaturated fat can help reduce blood cholesterol when substituted for fatty foods in the diet.

    buy yacon syrup

    Preparation may be the key

    buy yacon syrup

    Before this current finding the bananas was recommended as the most nutrient rich meals. It'd been regarded for the exceptionally strong antioxidant qualities.

    buy yacon syrup

    Lessen your intake of processed foods, refined, junk foods, sugars and food containing it, and red or greasy meat. Improve your consumption of vegetables and fruit. You are able to change the coffee and standard teas using a coffee substitute

    Buy Yacon Syrup your immune-system, your storage and not only this can benefit from having fat.

    It is an incredibly poor idea to remove fat completely from your own diet. "Good" fats are necessary. These good fats come from such things as cool, canola oil, extravirgin essential olive oil, flaxseed, nuts, walnuts and Enova Oil -water bass. Getting rid of the wrong sort and consuming the right kind of fat is what's necessary.

  29. napisa_ o tymnapisa_ o tym Wrote on 12 May 2014 10:25

    This design is steller! You definitely know how to keep a reader amused. Between your wit and your videos, I was almost moved to start my own blog (well, almost...HaHa!) Fantastic job. I really enjoyed what you had to say, and more than that, how you presented it. Too cool!

  30. military education benefitsmilitary education benefits Wrote on 27 July 2014 07:16

    Hey there! I know this is kind of off topic but I was wondering if you knew where I could locate a captcha plugin for my comment form? I'm using the same blog platform as yours and I'm having trouble finding one? Thanks a lot!

  31. faltenkorrekturfaltenkorrektur Wrote on 17 August 2014 15:29

    What's up, just wanted to mention, I enjoyed this article.

    It was helpful. Keep on posting!

  32. foods to avoid with IBSfoods to avoid with IBS Wrote on 25 August 2014 08:24

    It's very simple to find out any topic on net as compared to books, as I found this paragraph at this website.

  33. top 10 dating sitestop 10 dating sites Wrote on 05 September 2014 17:10

    If you are going for finest contents like myself, simply go to see this web page daily since it gives quality contents, thanks

  34. NYC Certificate of DispositionNYC Certificate of Disposition Wrote on 07 September 2014 00:25

    Certificate of Temperament is Mistaken in Ny Express Shield your flexibility.

    The clerk of the court will be the just one who could matter the document of personality and you also have to report a written demand togsther with the worker oof the US District Judge wherever your circumstance was seenn having a $45 charte tto access your aged histyory annd $9.00 to allow them to write a document of predisposition when they obtain the situation document and assessment the situation for your personality of the scenario as joined by the courtroom. Thee area Clerk's workplace affirms being that it is government circumstance I must visit the federal court-house. Advice that is legal should be accepted by you solely from a registered lawyer with whom you have an attorney-client relationship. May need to spend a fee that you simply should be in a position to do with a credit card. NYC Certificate of Disposition The disposition will udoubtedly be: Microfilm, Devastation or Racks. I went along to the governmnent court house and since the case was in 1997, they referred everyone for the national records. Visit thee central clerk's office in the event the defendantis name does not look about the schedule. Retain a records retrieval support togo thesre and get it for you. Receive information regarding applying for a Qualification of Good Conduct.Contact the Lawful Actions Core of Nyc with obtaining a Certificate of Good Execute orr Alleviation for assistanfe in case you have criminhal convictions.Get details about seeking a Qualificaion of Disposition.

  35. ______ ____ _____ ____________ ____ _____ ______ Wrote on 11 September 2014 06:40

    I was able to find good info from your articles.

  36. AlineAline Wrote on 14 September 2014 20:35

    This is the best fotografi matrimonio napoli in this world. Even though you might be the seller of the goods, and shipping them to the customer, the customer mindshare is Amazons. La discrezione gli consente di realizzare ritratti molto naturali e spontanei catturando i momenti pi emozionanti del matrimonio.

  37. Phyllis MendozaPhyllis Mendoza Wrote on 19 September 2014 09:05

    Write more, thats all I have to say. Literally, it seems as though you relied on the video to make your point. You obviously know what youre talking about, why throw away your intelligence on just posting videos to your site when you could be giving us something informative to read?

  38. advancedserviceshvac.comadvancedserviceshvac.com Wrote on 21 September 2014 08:07

    I’m not that much of a internet reader to be honest but your blogs really nice, keep it up! I'll go ahead and bookmark your website to come back in the future. All the best

  39. JacquelynJacquelyn Wrote on 23 September 2014 12:04

    Helpful information. Lucky me I discovered your site by accident, and I am stunned why this accident did not happened in advance! I bookmarked it.

  40. MableMable Wrote on 24 September 2014 22:24

    I all the time used to read paragraph in news papers but now as I am a user oof internet so from now I am using net for content, thanks to web.

  41. MauriceMaurice Wrote on 27 September 2014 10:40

    I think that everything said was very reasonable. But, think about this, what if you typed a catchier post title? I mean, I don't wish to tel you how to runn your website, but suppose you added something that grabbed a person's attention? I mean Writing an OAuth Provider in PHP - Freek Lijten is a littyle vanilla. You could look at Yahoo's front page and watch how they create post headlines to get people to open the links. You mibht add a video or a related pic oor two too grab readrers excited about everything've got to say. In my opinion, it would make yoyr posts a little livelier.

  42. JermaineJermaine Wrote on 28 September 2014 01:33

    Grea blog right here! Also your website quite a bbit up very fast! What host arre you thee usage of?

    Can I am getting your associate hyperlink on your host? I want my website loaxed up as quickly as yours lol

  43. CrystleCrystle Wrote on 29 September 2014 15:20

    Greetings! Very helpful advice within this post! It is the littlpe hanges that produce the most significant changes.

    Many thanks for sharing!

  44. Visit Wikipedia for more United Kingdom InformationVisit Wikipedia for more United Kingdom Information Wrote on 01 October 2014 07:01

    Howdy, i read yoir boog from time to time and i own a similar one and i was just curious iif you get a llot of spam remarks? If so how do yoou stop it, anyy plugin or anything you can advise? I get so mufh lately it's driving me insane so any support is ery much appreciated.

  45. acoustic treatmentacoustic treatment Wrote on 01 October 2014 14:02

    If you are going for finest contents like myself, only visit this site every day because it gives quality contents, thanks

  46. Brustverkleinerung KostenBrustverkleinerung Kosten Wrote on 04 October 2014 20:30

    Thanks for sharing your thoughts about Bruststraffung Forum. Regards

  47. JacquesJacques Wrote on 05 October 2014 00:42

    Highly energetic blog, I liked that a lot. Wiill there be a part 2?

  48. omega 3 fatty acidsomega 3 fatty acids Wrote on 06 October 2014 03:32

    Hurrah, that's what I was seeking for, what a material! present here at this webpage, thanks admin of this site.

  49. AdrieneAdriene Wrote on 08 October 2014 10:52

    you are truly a excellent webmaster. The web site loadinng velocity is incredible. It seems that you arre doing any unique trick. Moreover, The contents are masterpiece. you've done a excellent activity on this topic!

  50. dreamhostdreamhost Wrote on 10 October 2014 12:51

    Hi, everything is going sound here and ofcourse every one is sharing information, that's truly good, keep up writing.

  51. get paid for taking surveysget paid for taking surveys Wrote on 11 October 2014 01:37

    Howdy! Would you mind if I share your blog with my twitter group? There's a lot of folks that I think would really enjoy your content. Please let me know. Thank you

  52. MatthiasMatthias Wrote on 16 October 2014 06:22

    Howdy! Someone in my Mysace group shared this website with uss so I came to givve it a look. I'm definitely loving thhe information. I'm book-marking and will be tweeting this to myy followers! Wonderful blog and fantastic style and design.

  53. dilocandilocan Wrote on 20 October 2014 11:57

    More specifically, Aneros provides a wide variety of toys for prostate pleasure.

  54. assisted living seniorassisted living senior Wrote on 21 October 2014 07:43

    When I initially commented I seem to have clicked the -Notify me when new comments are added- checkbox and now each time a comment is added I receive 4 emails with the same comment. Is there a means you are able to remove me from that service? Thanks!

  55. Http://Adamvolag.Wallinside.ComHttp://Adamvolag.Wallinside.Com Wrote on 21 October 2014 11:19

    In fact no matter if someone doesn't know then its up to other visitors that they will help, so here it takes place.

  56. windows for the homewindows for the home Wrote on 23 October 2014 04:23

    Attractive section of content. I just stumbled upon your website and in accession capital to assert that I get in fact enjoyed account your blog posts. Anyway I will be subscribing to your feeds and even I achievement you access consistently quickly.

  57. wifi hackerwifi hacker Wrote on 23 October 2014 07:43

    Howdy! Someone in my Facebook group shared this website with us so I came to take a look. I'm definitely loving the information. I'm bookmarking and will be tweeting this to my followers! Exceptional blog and outstanding design and style.

  58. how to lose weighthow to lose weight Wrote on 23 October 2014 20:23

    Hi there, after reading this awesome post i am too happy to share my knowledge here with mates.

  59. Brustvergrößerung FotosBrustvergrößerung Fotos Wrote on 25 October 2014 16:36

    Nice blog right here! Also your web site a lot up fast!

    What host are you the usage of? Can I get your associate link on your host? I wish my website loaded up as fast as yours lol

  60. google adwords account suspendedgoogle adwords account suspended Wrote on 26 October 2014 04:51

    Magnificent site. A lot of helpful info here. I am sending it to a few pals ans additionally sharing in delicious. And naturally, thank you for your effort!

  61. Escort bangkokEscort bangkok Wrote on 29 October 2014 15:18

    I really love your site.. Very nice colors & theme. Did you make this site yourself? Please reply back as I'm attempting to create my very own blog and would love to find out where you got this from or exactly what the theme is named. Many thanks!

  62. Check out these Android Apps for the PCCheck out these Android Apps for the PC Wrote on 03 November 2014 21:55

    I need to to thank you for this good read!! I certainly enjoyed every little biit of it. Ihave you book-marked to check out new things yoou post…

  63. cute and funny friendship quotes for myspacecute and funny friendship quotes for myspace Wrote on 04 November 2014 18:23

    Try something new, be spontaneous, and stay away from hum-drum.

    Zero-Flight a very popular my little pony racing game.

    There is a wonderful deal of international flavour in the blend of cultures in France over the decades, and for that reason, the gastronomic experience in Paris is unlike any other.

    The pendants come in either white or yellow gold, though the chain is not provided. This flirtatious text may sound violent but it is funny so give this one a try.

  64. ccgi.crabtree.force9.co.ukccgi.crabtree.force9.co.uk Wrote on 05 November 2014 22:23

    My spouse and I absolutely love your blog and find a lot of your post's to be exactly I'm looking for. can you offer guest writers to write content to suit your needs?

    I wouldn't mind publishing a post or elaborating on a number of the subjects you write with regards to here. Again, awesome blog!

  65. how to get rid of herpeshow to get rid of herpes Wrote on 07 November 2014 21:15

    Article Source: you spend another dime on your COLD SORES, grab Denny's great free reports about cold sores and discover a few incredible COLD SORE cures and treatments. Now that you understand the importance of oxygen, understand that about 8 out of 10 of you has some form of the Herpes virus.

    Most often the patient will be taken off the particular medication or will be given anti-estrogen treatment.

  66. http://fbgamescheats.net/dragon-city-cheats-and-hacks/http://fbgamescheats.net/dragon-city-cheats-and-hacks/ Wrote on 10 November 2014 09:40

    You have to breed them as well as need to feed them.

  67. joulurunotjoulurunot Wrote on 10 November 2014 19:32

    Mielenkiintoinen ammattimainen hengittää vuorineuvos pielinen yhteydenotto työmatka pelastuslaitos kaurismäki, sula etappi osaaja suhdanne öcalan laskeminen valtio romani suistua epäselvyys kaikesta huolimatta

  68. overwatch beta leakoverwatch beta leak Wrote on 11 November 2014 05:36

    Highly energetic post, I enjoyed that a lot. Will there be a part 2?

  69. Long Sleeve Bodycon DressLong Sleeve Bodycon Dress Wrote on 11 November 2014 22:45

    The effect of French fashion in Europe are very important when it comes to the fashion lifestyle there. This urban style looks great with a pair of wedge trainers or retro low tops. A great touch is making sure your belt matches your shoes.

  70. jquery google apijquery google api Wrote on 12 November 2014 20:21

    I know this web site provides quality dependent content and other data, is there any other web site which presents these stuff in quality?

  71. tax lien auctionstax lien auctions Wrote on 13 November 2014 06:22

    I couldn't refrain from commenting. Perfectly written!

  72. google ads plus advertisinggoogle ads plus advertising Wrote on 16 November 2014 16:59

    What i don't understood is actually how you're not really a lot more well-favored than you may be now. You are very intelligent. You know therefore significantly with regards to this subject, made me personally believe it from a lot of varied angles. Its like women and men don't seem to be fascinated unless it's something to accomplish with Girl gaga! Your personal stuffs outstanding. Always maintain it up!

  73. Sims 3 CheatsSims 3 Cheats Wrote on 16 November 2014 20:03

    Most of these games developers have come up with game merchandise that allow the gamers to enjoy none game items like T-shirts, watches, caps just to mention but a few. You require to be really mindful about which guide or website you use to support you with Farmville though, and I will describe why. Xbox 360: B, A, LB, B, B, LB, B, RB, RT, LT, LB, LB.

  74. rencontre transexuelrencontre transexuel Wrote on 19 November 2014 12:33

    I hardly comment, however i did a few searching and wound up here Writing an OAuth Provider in PHP - Freek Lijten. And I actually do have 2 questions for you if it's allright. Is it simply me or does it seem like some of these comments appear like written by brain dead visitors? :-P And, if you are posting on other places, I would like to follow anything fresh you have to post. Could you list of all of your community pages like your twitter feed, Facebook page or linkedin profile?

  75. get paid for surveyget paid for survey Wrote on 21 November 2014 23:48

    Hello would you mind sharing which blog platform you're working with? I'm looking to start my own blog in the near future but I'm having a hard time making a decision between BlogEngine/Wordpress/B2evolution and Drupal. The reason I ask is because your design seems different then most blogs and I'm looking for something completely unique. P.S My apologies for getting off-topicbut I had to ask!

  76. paid surveys reviewspaid surveys reviews Wrote on 23 November 2014 16:46

    I get pleasure from, cause I found exactly what I was taking a look for. You have ended my four day lengthy hunt! God Bless you man. Have a nice day. Bye

  77. selling fifa 14 coins pcselling fifa 14 coins pc Wrote on 24 November 2014 01:55

    What i don't realize is in fact how you're not actually much more neatly-favored than you may be now. You're so intelligent. You realize thus significantly with regards to this topic, produced me for my part believe it from so many numerous angles. Its like men and women aren't involved unless it's something to do with Woman gaga! Your individual stuffs nice.

    All the time maintain it up!

  78. videovideo Wrote on 24 November 2014 18:21

    Today, I went to the beach front with my kids. I found a sea shell and gave it to my 4 year old daughter and said "You can hear the ocean if you put this to your ear." She placed the shell to her ear and screamed. There was a hermit crab inside and it pinched her ear. She never wants to go back! LoL I know this is entirely off topic but I had to tell someone!

  79. gledajte domace snimke i dosta lijepih slikagledajte domace snimke i dosta lijepih slika Wrote on 25 November 2014 20:02

    Hi all, here every one is sharing such know-how, therefore it's nice to read this web site, and I used to visit this webpage daily.

  80. google keyword toolgoogle keyword tool Wrote on 26 November 2014 22:53

    It is not my first time to pay a visit this site, i am visiting this web page dailly and take good facts from here all the time.

  81. hollywood movies on top news 2015hollywood movies on top news 2015 Wrote on 30 November 2014 05:26

    It is now possible to watch Netflix in Trinidad and Tobago, as Netflix has released a server that includes this region. Also, the glut of Law & Order spinoffs and ER-tribute medical dramas that followed (and maybe it had to do with my aging and my perceptions as well) made all of those genres lose their luster, at least in my eyes. There is no doubt that everybody just loves and appreciates a good movie.

  82. JacelynJacelyn Wrote on 30 November 2014 12:47

    It's truly a great and useful piece of information. I'm glad that you simply shared this helpful information with us.

    Please stay us informed like this. Thanks for sharing.

  83. nike hyperdunknike hyperdunk Wrote on 02 December 2014 07:26

    Your1Click provided me with many available job postings and resume submission offers free of charge

  84. essayessay Wrote on 04 December 2014 11:43

    I want to to thank you for this very good read!! I certainly enjoyed every little bit of it. I have you bookmarked to look at new things you post…

  85. lool.tvlool.tv Wrote on 06 December 2014 00:04

    Everything yyou posted was very logical. However, think on this, what if you were to create a killer headline? I amn't saying your content isn't solid., however suppose you added a post title that makes people need more?

    I mean Writing an OAuth Provider in PHP - Freek Lijten is kinda plain. You might look at Yahoo's home page and make note of how they create article headlines to get viewers to open the links. You migbht try adding a video or a related pic or two to get readers interested about what you have got to say. Just my opinion, it would bring your posts more interesting.

  86. hystersisters weight loss after hysterectomyhystersisters weight loss after hysterectomy Wrote on 12 December 2014 03:09

    loss in weight, decrease of inches, or lower extra fat percentage, this could indicate a need to re-evaluate your plan and efforts. Including snacks or perhaps only one piece of food, because after the week when you take a look at food diary, you might be shocked at the amount food you consumed. According with a study inside the American Journal of Clinical Nutrition 85: 1465-1477, 2007, various diets produce short- term fat loss, but long-term perseverance is harder.

  87. google adwords accountgoogle adwords account Wrote on 16 December 2014 17:11

    A person essentially assist to make critically articles I would state. This is the first time I frequented your website page and thus far? I amazed with the research you made to make this particular post incredible. Magnificent process!

Leave a comment!

Italic and bold

*This is italic*, and _so is this_.
**This is bold**, and __so is this__.

Links

This is a link to [Procurios](http://www.procurios.nl).

Lists

A bulleted list can be made with:
- Minus-signs,
+ Add-signs,
* Or an asterisk.

A numbered list can be made with:
1. List item number 1.
2. List item number 2.

Quote

The text below creates a quote:
> This is the first line.
> This is the second line.

Code

A text block with code can be created. Prefix a line with four spaces and a code-block will be made.