diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..044880fd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/vendor/
+/composer.lock
+/docs/build/
+/build/logs/
+/build/coverage/
\ No newline at end of file
diff --git a/README.md b/README.md
index d2f4bc60..a1f48c37 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,43 @@
-# PHP OAuth server
+# PHP OAuth Framework
 
-The goal of this project is to develop a standards compliant [OAuth 2](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-v2/) server that supports a number of different authentication flows, and two extensions, [JSON web tokens](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-json-web-token/) and [SAML assertions](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-saml2-bearer/).
+The goal of this project is to develop a standards compliant [OAuth 2](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-v2/) authentication server, resource server and client library with support for a major OAuth 2 providers.
 
-The library will be a [composer](http://getcomposer.org/) package and will be framework agnostic.
+## Package Installation
 
-This code will be developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which has been funded by [JISC](http://jisc.ac.uk) under the access and identity management programme.
\ No newline at end of file
+The framework is provided as a Composer package which can be installed by adding the package to your composer.json file:
+
+```javascript
+{
+	"require": {  
+		"lncd\Oauth2": "*"  
+	}
+}
+```
+
+## Package Integration
+
+Check out the [wiki](https://github.com/lncd/OAuth2/wiki)
+
+## Current Features
+
+### Authentication Server
+
+The authentication server is a flexible class that supports the standard authorization code grant.
+
+### Resource Server
+
+The resource server allows you to secure your API endpoints by checking for a valid OAuth access token in the request and ensuring the token has the correct permission to access resources.
+
+
+
+
+## Future Goals
+
+### Authentication Server
+
+* Support for [JSON web tokens](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-json-web-token/).
+* Support for [SAML assertions](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-saml2-bearer/).
+
+---
+
+This code will be developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which has been funded by [JISC](http://jisc.ac.uk) under the Access and Identity Management programme.
\ No newline at end of file
diff --git a/build.xml b/build.xml
new file mode 100644
index 00000000..8008f502
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="PHP OAuth 2.0 Server" default="build">
+
+	<target name="build" depends="prepare,lint,phploc,pdepend,phpmd-ci,phpcs-ci,phpcpd,composer,phpunit,phpdox,phpcb"/>
+	
+	<target name="build-parallel" depends="prepare,lint,tools-parallel,phpcb"/>
+	
+	<target name="minimal" depends="prepare,lint,phploc,pdepend,phpcpd,composer,phpunit,phpdox,phpcb" />
+	
+	<target name="tools-parallel" description="Run tools in parallel">
+		<parallel threadCount="2">
+			<sequential>
+				<antcall target="pdepend"/>
+				<antcall target="phpmd-ci"/>
+			</sequential>
+			<antcall target="phpcpd"/>
+			<antcall target="phpcs-ci"/>
+			<antcall target="phploc"/>
+			<antcall target="phpdox"/>
+		</parallel>
+	</target>
+	
+	<target name="clean" description="Cleanup build artifacts">
+		<delete dir="${basedir}/build/api"/>
+		<delete dir="${basedir}/build/code-browser"/>
+		<delete dir="${basedir}/build/coverage"/>
+		<delete dir="${basedir}/build/logs"/>
+		<delete dir="${basedir}/build/pdepend"/>
+	</target>
+	
+	<target name="prepare" depends="clean" description="Prepare for build">
+		<mkdir dir="${basedir}/build/api"/>
+		<mkdir dir="${basedir}/build/code-browser"/>
+		<mkdir dir="${basedir}/build/coverage"/>
+		<mkdir dir="${basedir}/build/logs"/>
+		<mkdir dir="${basedir}/build/pdepend"/>
+		<mkdir dir="${basedir}/build/phpdox"/>
+	</target>
+	
+	<target name="lint">
+		<apply executable="php" failonerror="true">
+			<arg value="-l" />
+			
+			<fileset dir="${basedir}/src">
+				<include name="**/*.php" />
+				<modified />
+			</fileset>
+		</apply>
+	</target>
+	
+	<target name="phploc" description="Measure project size using PHPLOC">
+		<exec executable="phploc">
+			<arg value="--log-csv" />
+			<arg value="${basedir}/build/logs/phploc.csv" />
+			<arg path="${basedir}/src" />
+		</exec>
+	</target>
+	
+	<target name="pdepend" description="Calculate software metrics using PHP_Depend">
+		<exec executable="pdepend">
+			<arg value="--jdepend-xml=${basedir}/build/logs/jdepend.xml" />
+			<arg value="--jdepend-chart=${basedir}/build/pdepend/dependencies.svg" />
+			<arg value="--overview-pyramid=${basedir}/build/pdepend/overview-pyramid.svg" />
+			<arg path="${basedir}/src" />
+		</exec>
+	</target>
+	
+	<target name="phpmd" description="Perform project mess detection using PHPMD and print human readable output. Intended for usage on the command line before committing.">
+		<exec executable="phpmd">
+			<arg path="${basedir}/src" />
+			<arg value="text" />
+			<arg value="${basedir}/build/phpmd.xml" />
+		</exec>
+	</target>
+	
+	<target name="phpmd-ci" description="Perform project mess detection using PHPMD creating a log file for the continuous integration server">
+		<exec executable="phpmd">
+			<arg path="${basedir}/src" />
+			<arg value="xml" />
+			<arg value="${basedir}/build/phpmd.xml" />
+			<arg value="--reportfile" />
+			<arg value="${basedir}/build/logs/pmd.xml" />
+		</exec>
+	</target>
+	
+	<target name="phpcs" description="Find coding standard violations using PHP_CodeSniffer and print human readable output. Intended for usage on the command line before committing.">
+		<exec executable="phpcs">
+			<arg value="--standard=${basedir}/build/phpcs.xml" />
+			<arg value="--extensions=php" />
+			<arg value="--ignore=third_party/CIUnit" />
+			<arg path="${basedir}/src" />
+		</exec>
+	</target>
+	
+	<target name="phpcs-ci" description="Find coding standard violations using PHP_CodeSniffer creating a log file for the continuous integration server">
+		<exec executable="phpcs" output="/dev/null">
+			<arg value="--report=checkstyle" />
+			<arg value="--report-file=${basedir}/build/logs/checkstyle.xml" />
+			<arg value="--standard=${basedir}/build/phpcs.xml" />
+			<arg value="--extensions=php" />
+			<arg value="--ignore=third_party/CIUnit" />
+			<arg path="${basedir}/src" />
+		</exec>
+	</target>
+	
+	<target name="phpcpd" description="Find duplicate code using PHPCPD">
+		<exec executable="phpcpd">
+			<arg value="--log-pmd" />
+			<arg value="${basedir}/build/logs/pmd-cpd.xml" />
+			<arg path="${basedir}/src" />
+		</exec>
+	</target>
+
+	<target name="composer" description="Install Composer requirements">
+		<exec executable="composer.phar" failonerror="true">
+			<arg value="install" />
+			<arg value="--dev" />
+		</exec>
+	</target>
+
+	<target name="phpunit" description="Run unit tests with PHPUnit">
+		<exec executable="${basedir}/vendor/bin/phpunit" failonerror="true">
+			<arg value="--configuration" />
+			<arg value="${basedir}/build/phpunit.xml" />
+		</exec>
+	</target>
+	
+	<target name="phpdox" description="Generate API documentation using phpDox">
+		<exec executable="phpdox"/>
+	</target>
+	
+	<target name="phpcb" description="Aggregate tool output with PHP_CodeBrowser">
+		<exec executable="phpcb">
+			<arg value="--log" />
+			<arg path="${basedir}/build/logs" />
+			<arg value="--source" />
+			<arg path="${basedir}/src" />
+			<arg value="--output" />
+			<arg path="${basedir}/build/code-browser" />
+		</exec>
+	</target>
+</project>
\ No newline at end of file
diff --git a/build/phpcs.xml b/build/phpcs.xml
new file mode 100644
index 00000000..a6ee80da
--- /dev/null
+++ b/build/phpcs.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<ruleset name="PHP_CodeSniffer">
+
+	<description>PHP_CodeSniffer configuration</description>
+
+	<rule ref="PSR2"/>
+
+</ruleset>
\ No newline at end of file
diff --git a/build/phpmd.xml b/build/phpmd.xml
new file mode 100644
index 00000000..11f54dc1
--- /dev/null
+++ b/build/phpmd.xml
@@ -0,0 +1,14 @@
+<ruleset name="OAuth 2.0 Server"
+	xmlns="http://pmd.sf.net/ruleset/1.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
+		http://pmd.sf.net/ruleset_xml_schema.xsd"
+	xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
+
+	<description>
+		Ruleset for OAuth 2.0 server
+	</description>
+
+	<!-- Import the entire unused code rule set -->
+	<rule ref="rulesets/unusedcode.xml" />
+</ruleset>
\ No newline at end of file
diff --git a/build/phpunit.xml b/build/phpunit.xml
new file mode 100644
index 00000000..e74535da
--- /dev/null
+++ b/build/phpunit.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit colors="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="false" stopOnFailure="false" stopOnIncomplete="false" stopOnSkipped="false">
+	<testsuites>
+		<testsuite name="Authentication Server">
+			<directory suffix="test.php">../tests/authentication</directory>
+		</testsuite>
+		<testsuite name="Resource Server">
+			<directory suffix="test.php">../tests/resource</directory>
+		</testsuite>
+	</testsuites>
+	<filter>
+		<blacklist>
+			<directory suffix=".php">PEAR_INSTALL_DIR</directory>
+			<directory suffix=".php">PHP_LIBDIR</directory>
+			<directory suffix=".php">../vendor/composer</directory>
+		</blacklist>
+	</filter>
+	<logging>
+		<log type="coverage-html" target="coverage" title="lncd/OAuth" charset="UTF-8" yui="true" highlight="true" lowUpperBound="35" highLowerBound="70"/>
+		<log type="coverage-clover" target="logs/clover.xml"/>
+		<log type="junit" target="logs/junit.xml" logIncompleteSkipped="false"/>
+	</logging>
+</phpunit>
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 00000000..fc8f52b0
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,40 @@
+{
+	"name": "lncd/Oauth2",
+	"description": "OAuth 2.0 Framework",
+	"version": "0.1",
+	"homepage": "https://github.com/lncd/OAuth2",
+	"license": "MIT",
+	"require": {
+		"php": ">=5.3.0"
+	},
+	"require-dev": {
+		"EHER/PHPUnit": "*"
+	},
+	"repositories": [
+		{
+			"type": "git",
+			"url": "https://github.com/lncd/OAuth2"
+		}
+	],
+	"keywords": [
+		"oauth",
+		"oauth2",
+		"server",
+		"authorization",
+		"authentication",
+		"resource"
+	],
+	"authors": [
+		{
+			"name": "Alex Bilbie",
+			"email": "oauth2server@alexbilbie.com",
+			"homepage": "http://www.httpster.org",
+			"role": "Developer"
+		}
+	],
+	"autoload": {
+		"psr-0": {
+			"Oauth2": "src/"
+		}
+	}
+}
\ No newline at end of file
diff --git a/license.txt b/license.txt
new file mode 100644
index 00000000..91f8b897
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,20 @@
+MIT License
+
+Copyright (C) 2012 University of Lincoln
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of 
+this software and associated documentation files (the "Software"), to deal in 
+the Software without restriction, including without limitation the rights to 
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/src/Oauth2/Authentication/Database.php b/src/Oauth2/Authentication/Database.php
new file mode 100644
index 00000000..ed526f89
--- /dev/null
+++ b/src/Oauth2/Authentication/Database.php
@@ -0,0 +1,320 @@
+<?php
+
+namespace Oauth2\Authentication;
+
+interface Database
+{
+    /**
+     * Validate a client
+     * 
+     * Database query:
+     * 
+     * <code>
+     * # Client ID + redirect URI
+     * SELECT clients.id FROM clients LEFT JOIN client_endpoints ON
+     *  client_endpoints.client_id = clients.id WHERE clients.id = $clientId AND
+     *  client_endpoints.redirect_uri = $redirectUri
+     * 
+     * # Client ID + client secret
+     * SELECT clients.id FROM clients  WHERE clients.id = $clientId AND
+     *  clients.secret = $clientSecret
+     * 
+     * # Client ID + client secret + redirect URI
+     * SELECT clients.id FROM clients LEFT JOIN client_endpoints ON
+     *  client_endpoints.client_id = clients.id WHERE clients.id = $clientId AND
+     *  clients.secret = $clientSecret AND client_endpoints.redirect_uri =
+     *  $redirectUri
+     * </code>
+     * 
+     * @param  string $clientId     The client's ID
+     * @param  string $clientSecret The client's secret (default = "null")
+     * @param  string $redirectUri  The client's redirect URI (default = "null")
+     * @return [type]               [description]
+     */
+    public function validateClient(
+        $clientId,
+        $clientSecret = null,
+        $redirectUri = null
+    );
+
+    /**
+     * Create a new OAuth session
+     * 
+     * Database query:
+     * 
+     * <code>
+     * INSERT INTO oauth_sessions (client_id, redirect_uri, owner_type,
+     *  owner_id, auth_code, access_token, stage, first_requested, last_updated)
+     *  VALUES ($clientId, $redirectUri, $type, $typeId, $authCode,
+     *  $accessToken, $stage, UNIX_TIMESTAMP(NOW()), UNIX_TIMESTAMP(NOW()))
+     * </code>
+     * 
+     * @param  string $clientId    The client ID
+     * @param  string $redirectUri The redirect URI
+     * @param  string $type        The session owner's type (default = "user")
+     * @param  string $typeId      The session owner's ID (default = "null")
+     * @param  string $authCode    The authorisation code (default = "null")
+     * @param  string $accessToken The access token (default = "null")
+     * @param  string $stage       The stage of the session (default ="request")
+     * @return [type]              [description]
+     */
+    public function newSession(
+        $clientId,
+        $redirectUri,
+        $type = 'user',
+        $typeId = null,
+        $authCode = null,
+        $accessToken = null,
+        $accessTokenExpire = null,
+        $stage = 'requested'
+    );
+
+    /**
+     * Update an OAuth session
+     * 
+     * Database query:
+     * 
+     * <code>
+     * UPDATE oauth_sessions SET auth_code = $authCode, access_token =
+     *  $accessToken, stage = $stage, last_updated = UNIX_TIMESTAMP(NOW()) WHERE
+     *  id = $sessionId
+     * </code>
+     * 
+     * @param  string $sessionId   The session ID
+     * @param  string $authCode    The authorisation code (default = "null")
+     * @param  string $accessToken The access token (default = "null")
+     * @param  string $stage       The stage of the session (default ="request")
+     * @return void
+     */
+    public function updateSession(
+        $sessionId,
+        $authCode = null,
+        $accessToken = null,
+        $accessTokenExpire = null,
+        $stage = 'requested'
+    );
+
+    /**
+     * Delete an OAuth session
+     * 
+     * <code>
+     * DELETE FROM oauth_sessions WHERE client_id = $clientId AND owner_type =
+     *  $type AND owner_id = $typeId
+     * </code>
+     * 
+     * @param  string $clientId The client ID
+     * @param  string $type     The session owner's type 
+     * @param  string $typeId   The session owner's ID
+     * @return [type]           [description]
+     */
+    public function deleteSession(
+        $clientId,
+        $type,
+        $typeId
+    );
+
+    /**
+     * Validate that an authorisation code is valid
+     * 
+     * Database query:
+     * 
+     * <code>
+     * SELECT id FROM oauth_sessions WHERE client_id = $clientID AND
+     *  redirect_uri = $redirectUri AND auth_code = $authCode
+     * </code>
+     * 
+     * Response:
+     * 
+     * <code>
+     * Array
+     * (
+     *     [id] => (int) The session ID
+     *     [client_id] => (string) The client ID
+     *     [redirect_uri] => (string) The redirect URI
+     *     [owner_type] => (string) The session owner type
+     *     [owner_id] => (string) The session owner's ID
+     *     [auth_code] => (string) The authorisation code
+     *     [stage] => (string) The session's stage
+     *     [first_requested] => (int) Unix timestamp of the time the session was
+     *      first generated
+     *     [last_updated] => (int) Unix timestamp of the time the session was
+     *      last updated
+     * )
+     * </code>
+     * 
+     * @param  string     $clientId    The client ID
+     * @param  string     $redirectUri The redirect URI
+     * @param  string     $authCode    The authorisation code
+     * @return int|bool                Returns the session ID if the auth code
+     *  is valid otherwise returns false 
+     */
+    public function validateAuthCode(
+        $clientId,
+        $redirectUri,
+        $authCode
+    );
+
+    /**
+     * Return the session ID for a given session owner and client combination
+     * 
+     * Database query:
+     * 
+     * <code>
+     * SELECT id FROM oauth_sessions WHERE client_id = $clientId
+     *  AND owner_type = $type AND owner_id = $typeId
+     * </code>
+     * 
+     * @param  string      $type     The session owner's type 
+     * @param  string      $typeId   The session owner's ID
+     * @param  string      $clientId The client ID
+     * @return string|null           Return the session ID as an integer if 
+     *  found otherwise returns false
+     */
+    public function hasSession(
+        $type,
+        $typeId,
+        $clientId
+    );
+
+    /**
+     * Return the access token for a given session
+     * 
+     * Database query:
+     * 
+     * <code>
+     * SELECT access_token FROM oauth_sessions WHERE id = $sessionId
+     * </code>
+     * 
+     * @param  int         $sessionId The OAuth session ID
+     * @return string|null            Returns the access token as a string if
+     *  found otherwise returns null
+     */
+    public function getAccessToken($sessionId);
+
+    /**
+     * Removes an authorisation code associated with a session
+     * 
+     * Database query:
+     * 
+     * <code>
+     * UPDATE oauth_sessions SET auth_code = NULL WHERE id = $sessionId
+     * </code>
+     * 
+     * @param  int    $sessionId The OAuth session ID
+     * @return void
+     */
+    public function removeAuthCode($sessionId);
+
+    /**
+     * Sets a sessions access token
+     * 
+     * Database query:
+     * 
+     * <code>
+     * UPDATE oauth_sessions SET access_token = $accessToken WHERE id = 
+     *  $sessionId
+     * </code>
+     * 
+     * @param int    $sessionId   The OAuth session ID
+     * @param string $accessToken The access token
+     * @return void
+     */
+    public function setAccessToken(
+        $sessionId,
+        $accessToken
+    );
+
+    /**
+     * Associates a session with a scope
+     * 
+     * Database query:
+     * 
+     * <code>
+     * INSERT INTO oauth_session_scopes (session_id, scope) VALUE ($sessionId, 
+     *  $scope)
+     * </code>
+     * 
+     * @param int    $sessionId The session ID
+     * @param string $scope     The scope
+     * @return void
+     */
+    public function addSessionScope(
+        $sessionId,
+        $scope
+    );
+
+    /**
+     * Return information about a scope
+     * 
+     * Database query:
+     * 
+     * <code>
+     * SELECT * FROM scopes WHERE scope = $scope
+     * </code>
+     * 
+     * Response:
+     * 
+     * <code>
+     * Array
+     * (
+     *     [id] => (int) The scope's ID
+     *     [scope] => (string) The scope itself
+     *     [name] => (string) The scope's name
+     *     [description] => (string) The scope's description
+     * )
+     * </code>
+     * 
+     * @param  string $scope The scope
+     * @return array         
+     */
+    public function getScope($scope);
+
+    /**
+     * Associate a session's scopes with an access token
+     * 
+     * Database query:
+     * 
+     * <code>
+     * UPDATE oauth_session_scopes SET access_token = $accessToken WHERE 
+     *  session_id = $sessionId
+     * </code>
+     * 
+     * @param  int    $sessionId   The session ID
+     * @param  string $accessToken The access token
+     * @return void
+     */
+    public function updateSessionScopeAccessToken(
+        $sessionId,
+        $accessToken
+    );
+
+    /**
+     * Return the scopes associated with an access token
+     * 
+     * Database query:
+     * 
+     * <code>
+     * SELECT scopes.scope, scopes.name, scopes.description FROM 
+     * oauth_session_scopes JOIN scopes ON oauth_session_scopes.scope = 
+     *  scopes.scope WHERE access_token = $accessToken
+     * </code>
+     * 
+     * Response:
+     * 
+     * <code>
+     * Array
+     * (
+     *     [0] => Array
+     *         (
+     *             [scope] => (string) The scope
+     *             [name] => (string) The scope's name
+     *             [description] => (string) The scope's description
+     *         )
+     * )
+     * </code>
+     * 
+     * @param  string $accessToken The access token
+     * @return array
+     */
+    public function accessTokenScopes($accessToken);
+}
diff --git a/src/Oauth2/Authentication/Server.php b/src/Oauth2/Authentication/Server.php
new file mode 100644
index 00000000..0c6f9476
--- /dev/null
+++ b/src/Oauth2/Authentication/Server.php
@@ -0,0 +1,517 @@
+<?php
+
+namespace Oauth2\Authentication;
+
+class OAuthServerClientException extends \Exception
+{
+
+}
+
+class OAuthServerUserException extends \Exception
+{
+
+}
+
+class OAuthServerException extends \Exception
+{
+
+}
+
+class Server
+{
+    /**
+     * Reference to the database abstractor
+     * @var object
+     */
+    private $db = null;
+
+    /**
+     * Server configuration
+     * @var array
+     */
+    private $config = array(
+        'scope_delimeter'       =>  ',',
+        'access_token_ttl'   =>  null
+    );
+
+    /**
+     * Supported response types
+     * @var array
+     */
+    private $response_types =   array(
+        'code'
+    );
+    
+    /**
+     * Supported grant types
+     * @var array
+     */
+    private $grant_types    =   array(
+        'authorization_code'
+    );
+
+    /**
+     * Exception error codes
+     * @var array
+     */
+    public $exceptionCodes = array(
+        0   =>  'invalid_request',
+        1   =>  'unauthorized_client',
+        2   =>  'access_denied',
+        3   =>  'unsupported_response_type',
+        4   =>  'invalid_scope',
+        5   =>  'server_error',
+        6   =>  'temporarily_unavailable',
+        7   =>  'unsupported_grant_type',
+        8   =>  'invalid_client',
+        9   =>  'invalid_grant'
+    );
+
+    /**
+     * Error codes.
+     * 
+     * To provide i8ln errors just overwrite the keys
+     * 
+     * @var array
+     */
+    public $errors = array(
+        'invalid_request'   =>  'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "%s" parameter.',
+        'unauthorized_client'   =>  'The client is not authorized to request an access token using this method.',
+        'access_denied' =>  'The resource owner or authorization server denied the request.',
+        'unsupported_response_type' =>  'The authorization server does not support obtaining an access token using this method.',
+        'invalid_scope' =>  'The requested scope is invalid, unknown, or malformed. Check the "%s" scope.',
+        'server_error'  =>  'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.',
+        'temporarily_unavailable'   =>  'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.',
+        'unsupported_grant_type'    =>  'The authorization grant type is not supported by the authorization server',
+        'invalid_client'    =>  'Client authentication failed',
+        'invalid_grant'     =>  'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. Check the "%s" parameter.'
+    );
+
+    /**
+     * Constructor
+     * 
+     * @access public
+     * @param  array $options Optional list of options to overwrite the defaults
+     * @return void
+     */
+    public function __construct($options = null)
+    {
+        if ($options !== null) {
+            $this->options = array_merge($this->config, $options);
+        }
+    }
+
+    /**
+     * Register a database abstrator class
+     * 
+     * @access public
+     * @param  object $db A class that implements OAuth2ServerDatabase
+     * @return void
+     */
+    public function registerDbAbstractor($db)
+    {
+        $this->db = $db;
+    }
+
+    /**
+     * Check client authorise parameters
+     * 
+     * @access public
+     * @param  array $authParams Optional array of parsed $_GET keys
+     * @return array             Authorise request parameters
+     */
+    public function checkClientAuthoriseParams($authParams = null)
+    {
+        $params = array();
+
+        // Client ID
+        if ( ! isset($authParams['client_id']) && ! isset($_GET['client_id'])) {
+
+            throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
+
+        } else {
+
+            $params['client_id'] = (isset($authParams['client_id'])) ? $authParams['client_id'] : $_GET['client_id'];
+
+        }
+
+        // Redirect URI
+        if ( ! isset($authParams['redirect_uri']) && ! isset($_GET['redirect_uri'])) {
+
+            throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'redirect_uri'), 0);
+
+        } else {
+
+            $params['redirect_uri'] = (isset($authParams['redirect_uri'])) ? $authParams['redirect_uri'] : $_GET['redirect_uri'];
+
+        }
+
+        // Validate client ID and redirect URI
+        $clientDetails = $this->dbcall('validateClient', $params['client_id'], null, $params['redirect_uri']);
+
+        if ($clientDetails === false) {
+
+            throw new OAuthServerClientException($this->errors['invalid_client'], 8);
+        }
+
+        // Response type
+        if ( ! isset($authParams['response_type']) && ! isset($_GET['response_type'])) {
+
+            throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'response_type'), 0);
+
+        } else {
+
+            $params['response_type'] = (isset($authParams['response_type'])) ? $authParams['response_type'] : $_GET['response_type'];
+
+            // Ensure response type is one that is recognised
+            if ( ! in_array($params['response_type'], $this->response_types)) {
+
+                throw new OAuthServerClientException($this->errors['unsupported_response_type'], 3);
+
+            }
+        }
+
+        // Get and validate scopes
+        if (isset($authParams['scope']) || isset($_GET['scope'])) {
+
+            $scopes = (isset($_GET['scope'])) ? $_GET['scope'] : $authParams['scope'];
+
+            $scopes = explode($this->config['scope_delimeter'], $scopes);
+
+            // Remove any junk scopes
+            for ($i = 0; $i < count($scopes); $i++) {
+                $scopes[$i] = trim($scopes[$i]);
+
+                if ($scopes[$i] === '') {
+                    unset($scopes[$i]);
+                }
+            }
+
+            if (count($scopes) === 0) {
+
+                throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'scope'), 0);
+            }
+
+            $params['scopes'] = array();
+
+            foreach ($scopes as $scope) {
+
+                $scopeDetails = $this->dbcall('getScope', $scope);
+                
+                if ($scopeDetails === false) {
+
+                    throw new OAuthServerClientException(sprintf($this->errors['invalid_scope'], $scope), 4);
+
+                }
+
+                $params['scopes'][] = $scopeDetails;
+
+            }
+        }
+
+        return $params;
+    }
+
+    /**
+     * Parse a new authorise request
+     * 
+     * @param  string $type            The session owner's type
+     * @param  string $typeId          The session owner's ID
+     * @param  array  $authoriseParams The authorise request $_GET parameters
+     * @return string                  An authorisation code
+     */
+    public function newAuthoriseRequest($type, $typeId, $authoriseParams)
+    {
+        // Remove any old sessions the user might have
+        $this->dbcall('deleteSession',
+            $authoriseParams['client_id'],
+            $type,
+            $typeId
+        );
+
+        // Create the new auth code
+        $authCode = $this->newAuthCode(
+            $authoriseParams['client_id'], 
+            'user',
+            $typeId,
+            $authoriseParams['redirect_uri'], 
+            $authoriseParams['scopes']
+        );
+
+        return $authCode;
+    }
+
+    /**
+     * Generate a unique code
+     * 
+     * Generate a unique code for an authorisation code, or token
+     * 
+     * @return string A unique code
+     */
+    private function generateCode()
+    {
+        return sha1(uniqid(microtime()));
+    }
+
+    /**
+     * Create a new authorisation code
+     * 
+     * @param  string $clientId    The client ID
+     * @param  string $type        The type of the owner of the session
+     * @param  string $typeId      The session owner's ID
+     * @param  string $redirectUri The redirect URI
+     * @param  array  $scopes      The requested scopes
+     * @param  string $accessToken The access token (default = null)
+     * @return string              An authorisation code
+     */
+    private function newAuthCode($clientId, $type, $typeId, $redirectUri, $scopes = array(), $accessToken = null)
+    {
+        $authCode = $this->generateCode();
+
+        // If an access token exists then update the existing session with the 
+        // new authorisation code otherwise create a new session
+        if ($accessToken !== null) {
+
+            $this->dbcall('updateSession',
+                $clientId,
+                $type,
+                $typeId,
+                $authCode,
+                $accessToken,
+                'request'
+            );
+        
+        } else {
+
+            // Delete any existing sessions just to be sure
+            $this->dbcall('deleteSession', $clientId, $type, $typeId);
+               
+            // Create a new session     
+            $sessionId = $this->dbcall('newSession',
+                $clientId,
+                $redirectUri,
+                $type,
+                $typeId,
+                $authCode,
+                null,
+                null,
+                'request'
+            );
+                        
+            // Add the scopes
+            foreach ($scopes as $key => $scope) {
+
+                $this->dbcall('addSessionScope', $sessionId, $scope['scope']);
+
+            }
+
+        }
+        
+        return $authCode;
+    }
+
+    /**
+     * Issue an access token
+     * 
+     * @access public
+     * 
+     * @param  array $authParams Optional array of parsed $_POST keys
+     * 
+     * @return array             Authorise request parameters
+     */
+    public function issueAccessToken($authParams = null)
+    {
+        $params = array();
+
+        if ( ! isset($authParams['grant_type']) && ! isset($_POST['grant_type'])) {
+
+            throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'grant_type'), 0);
+
+        } else {
+
+            $params['grant_type'] = (isset($authParams['grant_type'])) ? $authParams['grant_type'] : $_POST['grant_type'];
+
+            // Ensure grant type is one that is recognised
+            if ( ! in_array($params['grant_type'], $this->grant_types)) {
+
+                throw new OAuthServerClientException($this->errors['unsupported_grant_type'], 7);
+
+            }
+        }
+
+        switch ($params['grant_type'])
+        {
+            
+            case 'authorization_code': // Authorization code grant
+                return $this->completeAuthCodeGrant($authParams, $params);
+                break;  
+
+            case 'refresh_token': // Refresh token
+            case 'password': // Resource owner password credentials grant
+            case 'client_credentials': // Client credentials grant
+            default: // Unsupported
+                throw new OAuthServerException($this->errors['server_error'] . 'Tried to process an unsuppported grant type.', 5);
+                break;
+        }
+    }
+
+    /**
+     * Complete the authorisation code grant
+     * 
+     * @access private
+     * 
+     * @param  array $authParams Array of parsed $_POST keys
+     * @param  array $params     Generated parameters from issueAccessToken()
+     * 
+     * @return array             Authorise request parameters
+     */
+    private function completeAuthCodeGrant($authParams = array(), $params = array())
+    {
+        // Client ID
+        if ( ! isset($authParams['client_id']) && ! isset($_POST['client_id'])) {
+
+            throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
+
+        } else {
+
+            $params['client_id'] = (isset($authParams['client_id'])) ? $authParams['client_id'] : $_POST['client_id'];
+
+        }
+
+        // Client secret
+        if ( ! isset($authParams['client_secret']) && ! isset($_POST['client_secret'])) {
+
+            throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'client_secret'), 0);
+
+        } else {
+
+            $params['client_secret'] = (isset($authParams['client_secret'])) ? $authParams['client_secret'] : $_POST['client_secret'];
+
+        }
+
+        // Redirect URI
+        if ( ! isset($authParams['redirect_uri']) && ! isset($_POST['redirect_uri'])) {
+
+            throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'redirect_uri'), 0);
+
+        } else {
+
+            $params['redirect_uri'] = (isset($authParams['redirect_uri'])) ? $authParams['redirect_uri'] : $_POST['redirect_uri'];
+
+        }
+
+        // Validate client ID and redirect URI
+        $clientDetails = $this->dbcall('validateClient',
+            $params['client_id'],
+            $params['client_secret'], 
+            $params['redirect_uri']
+        );
+
+        if ($clientDetails === false) {
+
+            throw new OAuthServerClientException($this->errors['invalid_client'], 8);
+        }
+
+        // The authorization code
+        if ( ! isset($authParams['code']) && ! isset($_POST['code'])) {
+
+            throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'code'), 0);
+
+        } else {
+
+            $params['code'] = (isset($authParams['code'])) ? $authParams['code'] : $_POST['code'];
+
+        }
+
+        // Verify the authorization code matches the client_id and the
+        //  request_uri
+        $session = $this->dbcall('validateAuthCode',
+            $params['client_id'],
+            $params['redirect_uri'],
+            $params['code']
+        );
+
+        if ( ! $session) {
+
+            throw new OAuthServerClientException(sprintf($this->errors['invalid_grant'], 'code'), 9);
+        
+        } else {
+
+            // A session ID was returned so update it with an access token,
+            //  remove the authorisation code, change the stage to 'granted'
+
+            $accessToken = $this->generateCode();
+
+            $accessTokenExpires = ($this->config['access_token_ttl'] === null) ? null : time() + $this->config['access_token_ttl'];
+
+            $this->dbcall('updateSession',
+                $session['id'],
+                null,
+                $accessToken,
+                $accessTokenExpires,
+                'granted'
+            );
+
+            // Update the session's scopes to reference the access token
+            $this->dbcall('updateSessionScopeAccessToken',
+                $session['id'],
+                $accessToken
+            );
+
+            return array(
+                'access_token'  =>  $accessToken,
+                'token_type'    =>  'bearer',
+                'expires_in'    =>  $this->config['access_token_ttl']
+            );
+        }
+    }
+
+    /**
+     * Generates the redirect uri with appended params
+     * 
+     * @param  string $redirectUri     The redirect URI
+     * @param  array  $params          The parameters to be appended to the URL
+     * @param  string $query_delimeter The query string delimiter (default: ?)
+     * 
+     * @return string                  The updated redirect URI
+     */
+    public function redirectUri($redirectUri, $params = array(), $queryDelimeter = '?')
+    {
+      
+        if (strstr($redirectUri, $queryDelimeter)) {
+
+            $redirectUri = $redirectUri . '&' . http_build_query($params);
+
+        } else {
+
+            $redirectUri = $redirectUri . $queryDelimeter . http_build_query($params);
+
+        }
+        
+        return $redirectUri;
+
+    }
+
+    /**
+     * Call database methods from the abstractor
+     * 
+     * @return mixed The query result
+     */
+    private function dbcall()
+    {
+        if ($this->db === null) {
+            throw new OAuthServerException('No registered database abstractor');
+        }
+
+        if ( ! $this->db instanceof Database) {
+            throw new OAuthServerException('Registered database abstractor is not an instance of Oauth2\Authentication\Database');
+        }
+
+        $args = func_get_args();
+        $method = $args[0];
+        unset($args[0]);
+        $params = array_values($args);
+
+        return call_user_func_array(array($this->db, $method), $params);
+    }
+}
diff --git a/src/Oauth2/Resource/Database.php b/src/Oauth2/Resource/Database.php
new file mode 100644
index 00000000..9c5d1b44
--- /dev/null
+++ b/src/Oauth2/Resource/Database.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Oauth2\Resource;
+
+interface Database
+{
+    /**
+     * Validate an access token and return the session details.
+     * 
+     * Database query:
+     * 
+     * <code>
+     * SELECT id, owner_type, owner_id FROM oauth_sessions WHERE access_token =
+     *  $accessToken AND stage = 'granted' AND
+     *  access_token_expires > UNIX_TIMESTAMP(now())
+     * </code>
+     * 
+     * Response:
+     * 
+     * <code>
+     * Array
+     * (
+     *     [id] => (int) The session ID
+     *     [owner_type] => (string) The session owner type
+     *     [owner_id] => (string) The session owner's ID
+     * )
+     * </code>
+     * 
+     * @param  string     $accessToken The access token
+     * @return array|bool              Return an array on success or false on failure
+     */
+    public function validateAccessToken($accessToken);
+
+    /**
+     * Returns the scopes that the session is authorised with.
+     * 
+     * Database query:
+     * 
+     * <code>
+     * SELECT scope FROM oauth_session_scopes WHERE access_token =
+     *  '291dca1c74900f5f252de351e0105aa3fc91b90b'
+     * </code>
+     * 
+     * Response:
+     * 
+     * <code>
+     * Array
+     * (
+     *      [0] => (string) A scope
+     *      [1] => (string) Another scope
+     *      ...
+     * )
+     * </code>
+     * 
+     * @param  int   $sessionId The session ID
+     * @return array            A list of scopes
+     */
+    public function sessionScopes($sessionId);
+}
\ No newline at end of file
diff --git a/src/Oauth2/Resource/Server.php b/src/Oauth2/Resource/Server.php
new file mode 100644
index 00000000..0ec835a5
--- /dev/null
+++ b/src/Oauth2/Resource/Server.php
@@ -0,0 +1,225 @@
+<?php
+
+namespace Oauth2\Resource;
+
+class OAuthResourceServerException extends \Exception
+{
+
+}
+
+class Server
+{
+    /**
+     * Reference to the database abstractor
+     * @var object
+     */
+    private $_db = null;
+
+    /**
+     * The access token.
+     * @access private
+     */
+    private $_accessToken = null;
+
+    /**
+     * The scopes the access token has access to.
+     * @access private
+     */
+    private $_scopes = array();
+
+    /**
+     * The type of owner of the access token.
+     * @access private
+     */
+    private $_type = null;
+
+    /**
+     * The ID of the owner of the access token.
+     * @access private
+     */
+    private $_typeId = null;
+
+    /**
+     * Server configuration
+     * @var array
+     */
+    private $_config = array(
+        'token_key' =>  'oauth_token'
+    );
+
+    /**
+     * Error codes.
+     * 
+     * To provide i8ln errors just overwrite the keys
+     * 
+     * @var array
+     */
+    public $errors = array(
+        'missing_access_token'  =>  'An access token was not presented with the request',
+        'invalid_access_token'  =>  'The access token is not registered with the resource server'
+    );
+
+    /**
+     * Constructor
+     * 
+     * @access public
+     * @return void
+     */
+    public function __construct($options = null)
+    {
+        if ($options !== null) {
+            $this->config = array_merge($this->config, $options);
+        }
+    }
+
+    /**
+     * Magic method to test if access token represents a particular owner type
+     * @param  string $method     The method name
+     * @param  mixed  $arguements The method arguements
+     * @return bool               If method is valid, and access token is owned by the requested party then true,
+     */
+    public function __call($method, $arguements = null)
+    {
+        if (substr($method, 0, 2) === 'is') {
+
+            if ($this->_type === strtolower(substr($method, 2))) {
+                return $this->_typeId;
+            }
+            
+            return false;
+        }
+
+        trigger_error('Call to undefined function ' . $method . '()');
+    }
+
+    /**
+     * Register a database abstrator class
+     * 
+     * @access public
+     * @param  object $db A class that implements OAuth2ServerDatabase
+     * @return void
+     */
+    public function registerDbAbstractor($db)
+    {
+        $this->_db = $db;
+    }
+    
+    /**
+     * Init function
+     * 
+     * @access public
+     * @return void
+     */
+    public function init()
+    {
+        $accessToken = null;
+
+        // Try and get the access token via an access_token or oauth_token parameter
+        switch ($_SERVER['REQUEST_METHOD'])
+        {           
+            case 'POST':
+                $accessToken = isset($_POST[$this->_config['token_key']]) ? $_POST[$this->_config['token_key']] : null;
+                break;
+
+            default:
+            $accessToken = isset($_GET[$this->_config['token_key']]) ? $_GET[$this->_config['token_key']] : null;
+                break;
+        }
+
+        // Try and get an access token from the auth header
+        if (function_exists('getallheaders')) {
+
+            $headers = getallheaders();
+            
+            if (isset($headers['Authorization'])) {
+
+                $rawToken = trim(str_replace('Bearer', '', $headers['Authorization']));
+
+                if ( ! empty($rawToken)) {
+                    $accessToken = base64_decode($rawToken);
+                }
+            }
+        }
+        
+        if ($accessToken) {
+
+            $result = $this->_dbCall('validateAccessToken', $accessToken);
+
+            if ($result === false) {
+
+                throw new OAuthResourceServerException($this->errors['invalid_access_token']);
+
+            } else {
+
+                $this->_accessToken = $accessToken;
+                $this->_type = $result['owner_type'];
+                $this->_typeId = $result['owner_id'];
+
+                // Get the scopes
+                $this->_scopes = $this->_dbCall('sessionScopes', $result['id']);
+            }
+
+        } else {
+
+            throw new OAuthResourceServerException($this->errors['missing_access_token']);
+
+        }
+    }
+    
+    /**
+     * Test if the access token has a specific scope
+     * 
+     * @param mixed $scopes Scope(s) to check
+     * 
+     * @access public
+     * @return string|bool
+     */
+    public function hasScope($scopes)
+    {
+        if (is_string($scopes)) {
+
+            if (in_array($scopes, $this->_scopes)) {
+                return true;
+            }
+            
+            return false;
+
+        } elseif (is_array($scopes)) {
+
+            foreach ($scopes as $scope) {
+
+                if ( ! in_array($scope, $this->_scopes)) {
+                    return false;
+                }
+
+            }
+            
+            return true;
+        }
+        
+        return false;
+    }
+
+    /**
+     * Call database methods from the abstractor
+     * 
+     * @return mixed The query result
+     */
+    private function _dbCall()
+    {
+        if ($this->_db === null) {
+            throw new OAuthResourceServerException('No registered database abstractor');
+        }
+
+        if ( ! $this->_db instanceof Database) {
+            throw new OAuthResourceServerException('Registered database abstractor is not an instance of Oauth2\Resource\Database');
+        }
+
+        $args = func_get_args();
+        $method = $args[0];
+        unset($args[0]);
+        $params = array_values($args);
+
+        return call_user_func_array(array($this->_db, $method), $params);
+    }
+}
\ No newline at end of file
diff --git a/src/sql/database.sql b/src/sql/database.sql
new file mode 100644
index 00000000..0c218795
--- /dev/null
+++ b/src/sql/database.sql
@@ -0,0 +1,59 @@
+-- Create syntax for TABLE 'clients'
+CREATE TABLE `clients` (
+  `id` varchar(40) NOT NULL DEFAULT '',
+  `secret` varchar(40) NOT NULL DEFAULT '',
+  `name` varchar(255) NOT NULL DEFAULT '',
+  `auto_approve` tinyint(1) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+-- Create syntax for TABLE 'client_endpoints'
+CREATE TABLE `client_endpoints` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `client_id` varchar(40) NOT NULL DEFAULT '',
+  `redirect_uri` varchar(255) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  KEY `client_id` (`client_id`),
+  CONSTRAINT `client_endpoints_ibfk_1` FOREIGN KEY (`client_id`) REFERENCES `clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+-- Create syntax for TABLE 'oauth_sessions'
+CREATE TABLE `oauth_sessions` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `client_id` varchar(32) NOT NULL DEFAULT '',
+  `redirect_uri` varchar(250) NOT NULL DEFAULT '',
+  `owner_type` enum('user','client') NOT NULL DEFAULT 'user',
+  `owner_id` varchar(255) DEFAULT NULL,
+  `auth_code` varchar(40) DEFAULT '',
+  `access_token` varchar(40) DEFAULT '',
+  `access_token_expires` int(10) DEFAULT NULL,
+  `stage` enum('requested','granted') NOT NULL DEFAULT 'requested',
+  `first_requested` int(10) unsigned NOT NULL,
+  `last_updated` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `client_id` (`client_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+-- Create syntax for TABLE 'scopes'
+CREATE TABLE `scopes` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `scope` varchar(255) NOT NULL DEFAULT '',
+  `name` varchar(255) NOT NULL DEFAULT '',
+  `description` varchar(255) DEFAULT '',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `scope` (`scope`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+-- Create syntax for TABLE 'oauth_session_scopes'
+CREATE TABLE `oauth_session_scopes` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `session_id` int(11) unsigned NOT NULL,
+  `access_token` varchar(40) NOT NULL DEFAULT '',
+  `scope` varchar(255) NOT NULL DEFAULT '',
+  PRIMARY KEY (`id`),
+  KEY `session_id` (`session_id`),
+  KEY `access_token` (`access_token`),
+  KEY `scope` (`scope`),
+  CONSTRAINT `oauth_session_scopes_ibfk_3` FOREIGN KEY (`scope`) REFERENCES `scopes` (`scope`) ON DELETE CASCADE ON UPDATE CASCADE,
+  CONSTRAINT `oauth_session_scopes_ibfk_4` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
\ No newline at end of file
diff --git a/src/sql/index.html b/src/sql/index.html
new file mode 100644
index 00000000..e69de29b
diff --git a/test b/test
new file mode 100755
index 00000000..dae89d37
--- /dev/null
+++ b/test
@@ -0,0 +1 @@
+vendor/bin/phpunit --coverage-text --configuration build/phpunit.xml
diff --git a/tests/authentication/database_mock.php b/tests/authentication/database_mock.php
new file mode 100644
index 00000000..955035ed
--- /dev/null
+++ b/tests/authentication/database_mock.php
@@ -0,0 +1,191 @@
+<?php
+
+use Oauth2\Authentication\Database;
+
+class OAuthdb implements Database
+{
+	private $sessions = array();
+	private $sessions_client_type_id = array();
+	private $sessions_code = array();
+	private $session_scopes = array();
+
+	private $clients = array(0 => array(
+		'client_id'	=>	'test',
+		'client_secret'	=>	'test',
+		'redirect_uri'	=>	'http://example.com/test',
+		'name'	=>	'Test Client'
+	));
+
+	private $scopes = array('test' => array(
+		'id'	=>	1,
+		'scope'	=>	'test',
+		'name'	=>	'test',
+		'description'	=>	'test'
+	));
+
+	public function validateClient(
+        $clientId,
+        $clientSecret = null,
+        $redirectUri = null
+    )
+    {
+    	if ($clientId !== $this->clients[0]['client_id'])
+    	{
+    		return false;
+    	}
+
+    	if ($clientSecret !== null && $clientSecret !== $this->clients[0]['client_secret'])
+    	{
+    		return false;
+    	}
+
+    	if ($redirectUri !== null && $redirectUri !== $this->clients[0]['redirect_uri'])
+    	{
+    		return false;
+    	}
+
+    	return $this->clients[0];
+    }
+
+    public function newSession(
+        $clientId,
+        $redirectUri,
+        $type = 'user',
+        $typeId = null,
+        $authCode = null,
+        $accessToken = null,
+        $accessTokenExpire = null,
+        $stage = 'requested'
+    )
+    {
+    	$id = count($this->sessions);
+
+    	$this->sessions[$id] = array(
+    		'id'	=>	$id,
+    		'client_id'	=>	$clientId,
+    		'redirect_uri'	=>	$redirectUri,
+    		'owner_type'	=>	$type,
+    		'owner_id'	=>	$typeId,
+    		'auth_code'	=>	$authCode,
+    		'access_token'	=>	$accessToken,
+    		'access_token_expire'	=>	$accessTokenExpire,
+    		'stage'	=>	$stage
+    	);
+
+    	$this->sessions_client_type_id[$clientId . ':' . $type . ':' . $typeId] = $id;
+    	$this->sessions_code[$clientId . ':' . $redirectUri . ':' . $authCode] = $id;
+
+    	return true;
+    }
+
+    public function updateSession(
+        $sessionId,
+        $authCode = null,
+        $accessToken = null,
+        $accessTokenExpire = null,
+        $stage = 'requested'
+    )
+    {
+    	$this->sessions[$sessionId]['auth_code'] = $authCode;
+    	$this->sessions[$sessionId]['access_token'] = $accessToken;
+    	$this->sessions[$sessionId]['access_token_expire'] = $accessTokenExpire;
+    	$this->sessions[$sessionId]['stage'] = $stage;
+
+    	return true;
+    }
+
+    public function deleteSession(
+        $clientId,
+        $type,
+        $typeId
+    )
+    {
+    	$key = $clientId . ':' . $type . ':' . $typeId;
+    	if (isset($this->sessions_client_type_id[$key]))
+    	{
+    		unset($this->sessions[$this->sessions_client_type_id[$key]]);
+    	}
+    	return true;
+    }
+
+    public function validateAuthCode(
+        $clientId,
+        $redirectUri,
+        $authCode
+    )
+    {
+    	$key = $clientId . ':' . $redirectUri . ':' . $authCode;
+
+    	if (isset($this->sessions_code[$key]))
+    	{
+    		return $this->sessions[$this->sessions_code[$key]];
+    	}
+
+    	return false;
+    }
+
+    public function hasSession(
+        $type,
+        $typeId,
+        $clientId
+    )
+    {
+    	die('not implemented hasSession');
+    }
+
+    public function getAccessToken($sessionId)
+    {
+    	die('not implemented getAccessToken');
+    }
+
+    public function removeAuthCode($sessionId)
+    {
+    	die('not implemented removeAuthCode');
+    }
+
+    public function setAccessToken(
+        $sessionId,
+        $accessToken
+    )
+    {
+    	die('not implemented setAccessToken');
+    }
+
+    public function addSessionScope(
+        $sessionId,
+        $scope
+    )
+    {
+    	if ( ! isset($this->session_scopes[$sessionId]))
+    	{
+    		$this->session_scopes[$sessionId] = array();
+    	}
+
+    	$this->session_scopes[$sessionId][] = $scope;
+
+    	return true;
+    }
+
+    public function getScope($scope)
+    {
+    	if ( ! isset($this->scopes[$scope]))
+    	{
+    		return false;
+    	}
+
+    	return $this->scopes[$scope];
+    }
+
+    public function updateSessionScopeAccessToken(
+        $sessionId,
+        $accessToken
+    )
+    {
+    	return true;
+    }
+
+    public function accessTokenScopes($accessToken)
+    {
+    	die('not implemented accessTokenScopes');
+    }
+}
\ No newline at end of file
diff --git a/tests/authentication/server_test.php b/tests/authentication/server_test.php
new file mode 100644
index 00000000..6e79e24c
--- /dev/null
+++ b/tests/authentication/server_test.php
@@ -0,0 +1,398 @@
+<?php
+
+class Authentication_Server_test extends PHPUnit_Framework_TestCase {
+
+	function setUp()
+	{
+		$this->oauth = new Oauth2\Authentication\Server();
+
+		require_once('database_mock.php');
+		$this->oauthdb = new OAuthdb();
+		$this->assertInstanceOf('Oauth2\Authentication\Database', $this->oauthdb);
+		$this->oauth->registerDbAbstractor($this->oauthdb);
+	}
+
+	function test_generateCode()
+	{
+		$reflector = new ReflectionClass($this->oauth);
+		$method = $reflector->getMethod('generateCode');
+		$method->setAccessible(true);
+
+		$result = $method->invoke($this->oauth);
+		$result2 = $method->invoke($this->oauth);
+
+		$this->assertEquals(40, strlen($result));
+		$this->assertNotEquals($result, $result2);
+	}
+
+	function test_redirectUri()
+	{
+		$result1 = $this->oauth->redirectUri('http://example.com/foo');
+		$result2 = $this->oauth->redirectUri('http://example.com/foo', array('foo' => 'bar'));
+		$result3 = $this->oauth->redirectUri('http://example.com/foo', array('foo' => 'bar'), '#');
+
+		$this->assertEquals('http://example.com/foo?', $result1);
+		$this->assertEquals('http://example.com/foo?foo=bar', $result2);
+		$this->assertEquals('http://example.com/foo#foo=bar', $result3);
+	}
+
+	function test_checkClientAuthoriseParams_GET()
+	{
+		$_GET['client_id'] = 'test';
+		$_GET['redirect_uri'] = 'http://example.com/test';
+		$_GET['response_type'] = 'code';
+		$_GET['scope'] = 'test';
+		
+		$expect = array(
+			'client_id'	=>	'test',
+			'redirect_uri'	=>	'http://example.com/test',
+			'response_type'	=>	'code',
+			'scopes'	=>	array(
+					0 => array(
+					'id'	=>	1,
+					'scope'	=>	'test',
+					'name'	=>	'test',
+					'description'	=>	'test'
+				)
+			)
+		);
+
+		$result = $this->oauth->checkClientAuthoriseParams();
+
+		$this->assertEquals($expect, $result);
+	}
+
+	function test_checkClientAuthoriseParams_PassedParams()
+	{
+		unset($_GET['client_id']);
+		unset($_GET['redirect_uri']);
+		unset($_GET['response_type']);
+		unset($_GET['scope']);
+
+		$params = array(
+			'client_id'	=>	'test',
+			'redirect_uri'	=>	'http://example.com/test',
+			'response_type'	=>	'code',
+			'scope'	=>	'test'
+		);
+
+		$this->assertEquals(array(
+			'client_id'	=>	'test',
+			'redirect_uri'	=>	'http://example.com/test',
+			'response_type'	=>	'code',
+			'scopes'	=>	array(0 => array(
+				'id'	=>	1,
+				'scope'	=>	'test',
+				'name'	=>	'test',
+				'description'	=>	'test'
+			))
+		), $this->oauth->checkClientAuthoriseParams($params));
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    0
+	 */
+	function test_checkClientAuthoriseParams_missingClientId()
+	{
+		$this->oauth->checkClientAuthoriseParams();
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    0
+	 */
+	function test_checkClientAuthoriseParams_missingRedirectUri()
+	{
+		$_GET['client_id'] = 'test';
+
+		$this->oauth->checkClientAuthoriseParams();
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    0
+	 */
+	function test_checkClientAuthoriseParams_missingResponseType()
+	{
+		$_GET['client_id'] = 'test';
+		$_GET['redirect_uri'] = 'http://example.com/test';
+
+		$this->oauth->checkClientAuthoriseParams();
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    0
+	 */
+	function test_checkClientAuthoriseParams_missingScopes()
+	{
+		$_GET['client_id'] = 'test';
+		$_GET['redirect_uri'] = 'http://example.com/test';
+		$_GET['response_type'] = 'code';
+		$_GET['scope'] = ' ';
+
+		$this->oauth->checkClientAuthoriseParams();
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    4
+	 */
+	function test_checkClientAuthoriseParams_invalidScopes()
+	{
+		$_GET['client_id'] = 'test';
+		$_GET['redirect_uri'] = 'http://example.com/test';
+		$_GET['response_type'] = 'code';
+		$_GET['scope'] = 'blah';
+
+		$this->oauth->checkClientAuthoriseParams();
+	}
+
+	function test_newAuthoriseRequest()
+	{
+		$result = $this->oauth->newAuthoriseRequest('user', '123', array(
+			'client_id'	=>	'test',
+			'redirect_uri'	=>	'http://example.com/test',
+			'scopes'	=>	array(array(
+				'id'	=>	1,
+				'scope'	=>	'test',
+				'name'	=>	'test',
+				'description'	=>	'test'
+			))
+		));
+
+		$this->assertEquals(40, strlen($result));
+	}
+
+	function test_newAuthoriseRequest_isUnique()
+	{
+		$result1 = $this->oauth->newAuthoriseRequest('user', '123', array(
+			'client_id'	=>	'test',
+			'redirect_uri'	=>	'http://example.com/test',
+			'scopes'	=>	array(array(
+				'id'	=>	1,
+				'scope'	=>	'test',
+				'name'	=>	'test',
+				'description'	=>	'test'
+			))
+		));
+
+		$result2 = $this->oauth->newAuthoriseRequest('user', '123', array(
+			'client_id'	=>	'test',
+			'redirect_uri'	=>	'http://example.com/test',
+			'scopes'	=>	array(array(
+				'id'	=>	1,
+				'scope'	=>	'test',
+				'name'	=>	'test',
+				'description'	=>	'test'
+			))
+		));
+
+		$this->assertNotEquals($result1, $result2);
+	}
+
+	function test_issueAccessToken_POST()
+	{
+		$auth_code = $this->oauth->newAuthoriseRequest('user', '123', array(
+			'client_id'	=>	'test',
+			'redirect_uri'	=>	'http://example.com/test',
+			'scopes'	=>	array(array(
+				'id'	=>	1,
+				'scope'	=>	'test',
+				'name'	=>	'test',
+				'description'	=>	'test'
+			))
+		));
+
+		$_POST['client_id'] = 'test';
+		$_POST['client_secret'] = 'test';
+		$_POST['redirect_uri'] = 'http://example.com/test';
+		$_POST['grant_type'] = 'authorization_code';
+		$_POST['code'] = $auth_code;
+
+		$result = $this->oauth->issueAccessToken();
+
+		$this->assertCount(3, $result);
+		$this->assertArrayHasKey('access_token', $result);
+		$this->assertArrayHasKey('token_type', $result);
+		$this->assertArrayHasKey('expires_in', $result);
+	}
+
+	function test_issueAccessToken_PassedParams()
+	{
+		$auth_code = $this->oauth->newAuthoriseRequest('user', '123', array(
+			'client_id'	=>	'test',
+			'redirect_uri'	=>	'http://example.com/test',
+			'scopes'	=>	array(array(
+				'id'	=>	1,
+				'scope'	=>	'test',
+				'name'	=>	'test',
+				'description'	=>	'test'
+			))
+		));
+
+		$params['client_id'] = 'test';
+		$params['client_secret'] = 'test';
+		$params['redirect_uri'] = 'http://example.com/test';
+		$params['grant_type'] = 'authorization_code';
+		$params['code'] = $auth_code;
+
+		$result = $this->oauth->issueAccessToken($params);
+
+		$this->assertCount(3, $result);
+		$this->assertArrayHasKey('access_token', $result);
+		$this->assertArrayHasKey('token_type', $result);
+		$this->assertArrayHasKey('expires_in', $result);
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    0
+	 */
+	function test_issueAccessToken_missingGrantType()
+	{
+		$this->oauth->issueAccessToken();
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    7
+	 */
+	function test_issueAccessToken_unsupportedGrantType()
+	{
+		$params['grant_type'] = 'blah';
+
+		$this->oauth->issueAccessToken($params);
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    0
+	 */
+	function test_completeAuthCodeGrant_missingClientId()
+	{
+		$reflector = new ReflectionClass($this->oauth);
+		$method = $reflector->getMethod('completeAuthCodeGrant');
+		$method->setAccessible(true);
+
+		$method->invoke($this->oauth);
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    0
+	 */
+	function test_completeAuthCodeGrant_missingClientSecret()
+	{
+		$reflector = new ReflectionClass($this->oauth);
+		$method = $reflector->getMethod('completeAuthCodeGrant');
+		$method->setAccessible(true);
+
+		$authParams['client_id'] = 'test';
+
+		$method->invoke($this->oauth, $authParams);
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    0
+	 */
+	function test_completeAuthCodeGrant_missingRedirectUri()
+	{
+		$reflector = new ReflectionClass($this->oauth);
+		$method = $reflector->getMethod('completeAuthCodeGrant');
+		$method->setAccessible(true);
+
+		$authParams['client_id'] = 'test';
+		$authParams['client_secret'] = 'test';
+
+		$method->invoke($this->oauth, $authParams);
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    8
+	 */
+	function test_completeAuthCodeGrant_invalidClient()
+	{
+		$reflector = new ReflectionClass($this->oauth);
+		$method = $reflector->getMethod('completeAuthCodeGrant');
+		$method->setAccessible(true);
+
+		$authParams['client_id'] = 'test';
+		$authParams['client_secret'] = 'test123';
+		$authParams['redirect_uri'] = 'http://example.com/test';
+
+		$method->invoke($this->oauth, $authParams);
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    0
+	 */
+	function test_completeAuthCodeGrant_missingCode()
+	{
+		$reflector = new ReflectionClass($this->oauth);
+		$method = $reflector->getMethod('completeAuthCodeGrant');
+		$method->setAccessible(true);
+
+		$authParams['client_id'] = 'test';
+		$authParams['client_secret'] = 'test';
+		$authParams['redirect_uri'] = 'http://example.com/test';
+
+		$method->invoke($this->oauth, $authParams);
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerClientException
+	 * @expectedExceptionCode    9
+	 */
+	function test_completeAuthCodeGrant_invalidCode()
+	{
+		$reflector = new ReflectionClass($this->oauth);
+		$method = $reflector->getMethod('completeAuthCodeGrant');
+		$method->setAccessible(true);
+
+		$authParams['client_id'] = 'test';
+		$authParams['client_secret'] = 'test';
+		$authParams['redirect_uri'] = 'http://example.com/test';
+		$authParams['code'] = 'blah';
+
+		$method->invoke($this->oauth, $authParams);
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerException
+	 * @expectedExceptionMessage No registered database abstractor
+	 */
+	function test_noRegisteredDatabaseAbstractor()
+	{
+		$reflector = new ReflectionClass($this->oauth);
+		$method = $reflector->getMethod('dbcall');
+		$method->setAccessible(true);
+
+		$dbAbstractor = $reflector->getProperty('db');
+		$dbAbstractor->setAccessible(true);
+		$dbAbstractor->setValue($this->oauth, null);
+
+		$result = $method->invoke($this->oauth);
+	}
+
+	/**
+	 * @expectedException        Oauth2\Authentication\OAuthServerException
+	 * @expectedExceptionMessage Registered database abstractor is not an instance of Oauth2\Authentication\Database
+	 */
+	function test_invalidRegisteredDatabaseAbstractor()
+	{
+		$fake = new stdClass;
+		$this->oauth->registerDbAbstractor($fake);
+
+		$reflector = new ReflectionClass($this->oauth);
+		$method = $reflector->getMethod('dbcall');
+		$method->setAccessible(true);
+
+		$result = $method->invoke($this->oauth);
+	}
+
+}
\ No newline at end of file
diff --git a/tests/resource/database_mock.php b/tests/resource/database_mock.php
new file mode 100644
index 00000000..52c698d8
--- /dev/null
+++ b/tests/resource/database_mock.php
@@ -0,0 +1,31 @@
+<?php
+
+use Oauth2\Resource\Database;
+
+class ResourceDB implements Database
+{
+	private $accessTokens = array(
+		'test12345' => array(
+			'id'	=>	1,
+			'owner_type'	=>	'user',
+			'owner_id'	=>	123
+		)
+	);
+
+	private $sessionScopes = array(
+		1	=>	array(
+			'foo',
+			'bar'
+		)
+	);
+
+	public function validateAccessToken($accessToken)
+	{
+		return (isset($this->accessTokens[$accessToken])) ? $this->accessTokens[$accessToken] : false;
+	}
+
+	public function sessionScopes($sessionId)
+	{
+		return (isset($this->sessionScopes[$sessionId])) ? $this->sessionScopes[$sessionId] : array();
+	}
+}
\ No newline at end of file
diff --git a/tests/resource/server_test.php b/tests/resource/server_test.php
new file mode 100644
index 00000000..fdb35be4
--- /dev/null
+++ b/tests/resource/server_test.php
@@ -0,0 +1,121 @@
+<?php
+
+class Resource_Server_test extends PHPUnit_Framework_TestCase {
+
+	function setUp()
+	{
+		require_once('database_mock.php');
+		$this->server = new Oauth2\Resource\Server();
+		$this->db = new ResourceDB();
+
+		$this->assertInstanceOf('Oauth2\Resource\Database', $this->db);
+		$this->server->registerDbAbstractor($this->db);
+	}
+
+	function test_init_POST()
+	{
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$_POST['oauth_token'] = 'test12345';
+
+		$this->server->init();
+
+		$reflector = new ReflectionClass($this->server);
+
+		$_accessToken = $reflector->getProperty('_accessToken');
+		$_accessToken->setAccessible(true);
+
+		$_type = $reflector->getProperty('_type');
+		$_type->setAccessible(true);
+
+		$_typeId = $reflector->getProperty('_typeId');
+		$_typeId->setAccessible(true);
+
+		$_scopes = $reflector->getProperty('_scopes');
+		$_scopes->setAccessible(true);
+
+		$this->assertEquals($_accessToken->getValue($this->server), $_POST['oauth_token']);
+		$this->assertEquals($_type->getValue($this->server), 'user');
+		$this->assertEquals($_typeId->getValue($this->server), 123);
+		$this->assertEquals($_scopes->getValue($this->server), array('foo', 'bar'));
+	}
+
+	function test_init_GET()
+	{
+		$_GET['oauth_token'] = 'test12345';
+
+		$this->server->init();
+
+		$reflector = new ReflectionClass($this->server);
+
+		$_accessToken = $reflector->getProperty('_accessToken');
+		$_accessToken->setAccessible(true);
+
+		$_type = $reflector->getProperty('_type');
+		$_type->setAccessible(true);
+
+		$_typeId = $reflector->getProperty('_typeId');
+		$_typeId->setAccessible(true);
+
+		$_scopes = $reflector->getProperty('_scopes');
+		$_scopes->setAccessible(true);
+
+		$this->assertEquals($_accessToken->getValue($this->server), $_GET['oauth_token']);
+		$this->assertEquals($_type->getValue($this->server), 'user');
+		$this->assertEquals($_typeId->getValue($this->server), 123);
+		$this->assertEquals($_scopes->getValue($this->server), array('foo', 'bar'));
+	}
+
+	function test_init_header()
+	{
+		// Test with authorisation header
+		$this->markTestIncomplete('Authorisation header test has not been implemented yet.');
+	}
+
+	/**
+	 * @expectedException        \Oauth2\Resource\OAuthResourceServerException
+	 * @expectedExceptionMessage An access token was not presented with the request
+	 */
+	function test_init_missingToken()
+	{
+		$this->server->init();
+	}
+
+	/**
+	 * @expectedException        \Oauth2\Resource\OAuthResourceServerException
+	 * @expectedExceptionMessage The access token is not registered with the resource server
+	 */
+	function test_init_wrongToken()
+	{
+		$_POST['oauth_token'] = 'blah';
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+
+		$this->server->init();
+	}
+
+	function test_hasScope()
+	{
+		$_POST['oauth_token'] = 'test12345';
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+
+		$this->server->init();
+
+		$this->assertEquals(true, $this->server->hasScope('foo'));
+		$this->assertEquals(true, $this->server->hasScope('bar'));
+		$this->assertEquals(true, $this->server->hasScope(array('foo', 'bar')));
+
+		$this->assertEquals(false, $this->server->hasScope('foobar'));
+		$this->assertEquals(false, $this->server->hasScope(array('foobar')));
+	}
+
+	function test___call()
+	{
+		$_POST['oauth_token'] = 'test12345';
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+
+		$this->server->init();
+
+		$this->assertEquals(123, $this->server->isUser());
+		$this->assertEquals(false, $this->server->isMachine());
+	}
+
+}
\ No newline at end of file