Compare commits

...

138 Commits
0.1 ... 0.3.2

Author SHA1 Message Date
Alex Bilbie
a5b83c00bd Version bump 2012-11-20 15:29:43 +00:00
Alex Bilbie
129c9a7b7a Version bump 2012-11-20 15:28:48 +00:00
Alex Bilbie
958eab33a7 Lots of small documentation updates and clarifications 2012-11-20 15:27:33 +00:00
Alex Bilbie
fead044830 Spacing fixes and fixed sessionScopes() example query 2012-11-20 15:27:15 +00:00
Alex Bilbie
21f48c0491 Spacing updates 2012-11-20 15:26:04 +00:00
Alex Bilbie
ac990b609a Fixed client_id column length 2012-11-16 16:40:01 +00:00
Alex Bilbie
e5bbd41570 Version bump
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-14 17:33:46 +01:00
Alex Bilbie
99f54d1a02 Missing provider class
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-14 17:33:22 +01:00
Alex Bilbie
0897e6226c Merge branch 'release/0.3' 2012-10-14 17:20:58 +01:00
Alex Bilbie
3ee9175a5d Version bump
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-14 17:20:48 +01:00
Alex Bilbie
cca5ae6229 Merge branch 'feature/clients' into develop 2012-10-14 17:18:52 +01:00
Alex Bilbie
b1082ecb41 Lots of updates so it all works now
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-14 17:18:27 +01:00
Alex Bilbie
af83b1e80e Added urlUserInfo() method
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-12 14:42:36 +01:00
Alex Bilbie
92ace5c6e5 Fixed PHP error with missing bracket
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-12 13:09:56 +01:00
Alex Bilbie
5ed4a8a2c4 Don't base64 decode the bearer token
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-10 09:32:50 +01:00
Alex Bilbie
4fdfb63128 Removed old test bash file
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-05 15:27:34 +01:00
Alex Bilbie
ddfb355280 Code beautification 2012-10-05 15:24:59 +01:00
Alex Bilbie
ac3743027b Code beautification 2012-10-05 15:22:29 +01:00
Alex Bilbie
ed5c33008d Merge branch 'refs/heads/master' into develop 2012-10-05 15:07:38 +01:00
Alex Bilbie
0ce0459cfe Merge branch 'refs/heads/master' into feature/clients 2012-10-05 15:07:21 +01:00
Alex Bilbie
a76fbcd23d Epic rewriting to use Guzzle
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-05 15:05:27 +01:00
Alex Bilbie
bef7a212e3 Fixes and cleanup
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-05 15:03:15 +01:00
Alex Bilbie
ec191cffe1 Cleanup
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-05 15:02:56 +01:00
Alex Bilbie
5c13c3ee54 Namespace fixes
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-05 15:01:09 +01:00
Alex Bilbie
52bd18a2b4 Fixed namespaces in providers
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-05 15:00:51 +01:00
Alex Bilbie
74338bca6c Changed email + require Guzzle
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-10-05 14:59:43 +01:00
Alex Bilbie
6d1702a15d Merge branch 'develop' into feature/clients 2012-09-19 23:29:40 +01:00
Alex Bilbie
500640c56c phpunit/phpunit is now officially composer compatible
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-09-19 22:04:18 +01:00
Alex Bilbie
7d50b8e812 Moved SQL files
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-09-19 19:59:00 +01:00
Alex Bilbie
43ce218cbb Merge branch 'release/0.2.3' into develop 2012-09-19 19:54:30 +01:00
Alex Bilbie
5b870a84f8 Merge branch 'release/0.2.3' 2012-09-19 19:54:20 +01:00
Alex Bilbie
7e009a2d02 Version number bump + email change
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-09-19 19:54:11 +01:00
Alex Bilbie
602bc85121 Merge branch 'hotfix/auth-exceptions' into develop 2012-09-19 19:53:16 +01:00
Alex Bilbie
e567866a01 Merge branch 'hotfix/auth-exceptions' 2012-09-19 19:53:05 +01:00
Alex Bilbie
3c2720ece4 Renamed exceptions
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-09-19 19:52:52 +01:00
Alex Bilbie
1440a5e46c Added a .gitattributes file
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-09-19 19:48:32 +01:00
Alex Bilbie
0db10063ac Merge branch 'refs/heads/master' into develop 2012-09-19 19:34:17 +01:00
Alex Bilbie
9844613b2d Fixed test checks
Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-09-19 19:34:05 +01:00
Alex Bilbie
ed2cabc5b6 Merge branch 'master' of github.com:lncd/OAuth2
Conflicts:
	composer.json

Signed-off-by: Alex Bilbie <alex@alexbilbie.com>
2012-09-19 19:32:31 +01:00
Alex Bilbie
926034c824 Pulled latest changes 2012-09-19 19:30:35 +01:00
Alex Bilbie
e965bfdeb5 Merge branch 'release/0.2.2' into develop 2012-09-19 19:29:27 +01:00
Alex Bilbie
085f849855 Merge branch 'release/0.2.2' 2012-09-19 19:29:18 +01:00
Alex Bilbie
6ea2f6480d version number bump 2012-09-19 19:29:14 +01:00
Alex Bilbie
f75fef179a Merge branch 'hotfix/resource-fixes' into develop 2012-09-19 19:27:58 +01:00
Alex Bilbie
a6cfd40b0f Merge branch 'hotfix/resource-fixes' 2012-09-19 19:27:32 +01:00
Alex Bilbie
3f3b987fb1 Check responses from the database are valid 2012-09-19 19:27:04 +01:00
Alex Bilbie
bdbf9072f6 Update README.md 2012-09-07 12:59:41 +02:00
Alex Bilbie
c993a0cdc0 Use namespace, use PSR, get rid of Pyro settings 2012-09-04 14:50:20 +01:00
Alex Bilbie
9dfa485bcb PSR fixes 2012-09-04 14:49:56 +01:00
Alex Bilbie
c760667448 PSR style class braces 2012-09-04 12:43:18 +01:00
Alex Bilbie
0298f6fb7d Include IDP.php 2012-09-04 12:42:33 +01:00
Alex Bilbie
17b7bf7d1d Test if provider config exists 2012-09-04 12:40:32 +01:00
Alex Bilbie
ed6301a497 Renamed provider.php to IDP.php 2012-09-04 12:40:02 +01:00
Alex Bilbie
c423ef810f Require instead of include 2012-09-04 12:33:17 +01:00
Alex Bilbie
485a3772b6 Changed function to __construct 2012-09-04 12:32:59 +01:00
Alex Bilbie
6f03eec814 Class rename 2012-09-04 12:32:44 +01:00
Alex Bilbie
0a5455642f PSR style if/else block 2012-09-04 12:31:04 +01:00
Alex Bilbie
8fdcb02740 Exception class rename 2012-09-04 12:30:49 +01:00
Alex Bilbie
153bb962c5 PSR variable rename 2012-09-04 12:30:38 +01:00
Alex Bilbie
3ee5f22a55 Class rename 2012-09-04 12:30:15 +01:00
Alex Bilbie
c09663f2b8 Class rename 2012-09-04 12:30:06 +01:00
Alex Bilbie
6a41ae5254 PSR style if block 2012-09-04 12:29:52 +01:00
Alex Bilbie
dd5fc7dc24 PSR variable rename 2012-09-04 12:29:10 +01:00
Alex Bilbie
11415bea0b PSR style if/else block 2012-09-04 12:28:28 +01:00
Alex Bilbie
eb5010a3f8 PSR variable rename 2012-09-04 12:28:06 +01:00
Alex Bilbie
da329b6b37 Merge branch 'refs/heads/develop' into feature/clients
Conflicts:
	src/Oauth2/Authentication/Server.php
2012-09-04 12:09:13 +01:00
Alex Bilbie
248b09641d Merge branch 'hotfix/0.2.1' into develop 2012-08-28 12:31:36 +01:00
Alex Bilbie
0af98224dd Merge branch 'hotfix/0.2.1' 2012-08-28 12:31:07 +01:00
Alex Bilbie
88185320a8 Version bump 2012-08-28 12:30:58 +01:00
Alex Bilbie
a1e5fdddda Bug fix 2012-08-28 12:30:51 +01:00
Alex Bilbie
58d0a988a9 Merge branch 'release/0.2' into develop 2012-08-27 16:05:50 +01:00
Alex Bilbie
d9c4962380 Merge branch 'release/0.2' 2012-08-27 16:05:30 +01:00
Alex Bilbie
db7eddb4ea Updated version number 2012-08-27 16:05:20 +01:00
Alex Bilbie
4d4db99c06 Underscored private variables and methods and updated references 2012-08-27 16:02:54 +01:00
Alex Bilbie
b485f15e83 Fixed reference to renamed methods and properties 2012-08-27 16:02:35 +01:00
Alex Bilbie
c92dc8f759 Merge branch 'release/0.1' into develop 2012-08-27 15:52:21 +01:00
Alex Bilbie
d7ae0f316e Merge branch 'release/0.1' 2012-08-27 15:52:20 +01:00
Alex Bilbie
c05880471c Updated composer.json 2012-08-27 15:44:06 +01:00
Alex Bilbie
8724a1efb0 Updated composer.json with new version 2012-08-27 15:43:26 +01:00
Alex Bilbie
c89fe5bdf8 Updated README 2012-08-27 15:43:17 +01:00
Alex Bilbie
8f20659f1d Check for correct exception messages 2012-08-27 15:25:59 +01:00
Alex Bilbie
7341d5ddc8 Moaaaare tests! 2012-08-27 15:25:41 +01:00
Alex Bilbie
7a4aece507 Stylistic fix 2012-08-27 15:25:28 +01:00
Alex Bilbie
b7d73accdc Removed old die statement 2012-08-27 15:25:14 +01:00
Alex Bilbie
5da9088410 Assert that mock database objects are proper instances of their interfaces 2012-08-27 14:43:12 +01:00
Alex Bilbie
337b2e0a92 Marked test_init_header as incomplete 2012-08-27 14:32:06 +01:00
Alex Bilbie
3642b8432e PHPCS fixes 2012-08-27 14:25:24 +01:00
Alex Bilbie
e191566260 Fixed errors with handling database calls 2012-08-27 14:25:18 +01:00
Alex Bilbie
95931abd6b Spelling fix 2012-08-27 14:24:43 +01:00
Alex Bilbie
3ab511f2f7 Spacing fix 2012-08-27 14:24:32 +01:00
Alex Bilbie
3e7b471e75 Lots of beautiful tests 2012-08-27 14:23:50 +01:00
Alex Bilbie
00562858f9 Excluded Composer autoload 2012-08-27 14:23:26 +01:00
Alex Bilbie
9e01157324 Updated .gitignore 2012-08-24 12:26:13 +01:00
Alex Bilbie
465c1c6ab2 Wrapped getallheaders() method in function_exists (function isn't available on command line) 2012-08-24 12:25:31 +01:00
Alex Bilbie
87ec05dba6 Corrected namespace 2012-08-24 12:24:55 +01:00
Alex Bilbie
b987c71820 Renamed test classes 2012-08-24 12:23:16 +01:00
Alex Bilbie
829735aeeb Update .gitignore 2012-08-24 12:23:04 +01:00
Alex Bilbie
e8962f543d Merge branch 'refs/heads/feature/resource' into develop 2012-08-24 12:21:30 +01:00
Alex Bilbie
2c3e842702 Updated .gitignore 2012-08-24 12:21:11 +01:00
Alex Bilbie
f53f6ca609 Added .gitignore 2012-08-24 12:19:31 +01:00
Alex Bilbie
81a7322933 Started resource server unit tests
TODO: authentication header test
2012-08-23 12:22:39 +01:00
Alex Bilbie
66ee8df5b1 Added database mock for resource tests 2012-08-23 12:22:16 +01:00
Alex Bilbie
78424ce100 Added resource server test suite 2012-08-23 12:21:59 +01:00
Alex Bilbie
326e96cc17 Bug fix in dbcall 2012-08-20 15:49:57 +01:00
Alex Bilbie
6fdb6177bc Lots of fixes 2012-08-20 15:09:33 +01:00
Alex Bilbie
ed3238b862 Fixed constance letter casing 2012-08-20 14:19:33 +01:00
Alex Bilbie
9933bde7d8 Merge branch 'develop' of github.com:lncd/OAuth2 into develop 2012-08-15 11:08:43 +01:00
Alex Bilbie
519d20f0a5 Changed indent to spaces 2012-08-14 16:34:43 +01:00
Alex Bilbie
e859f435a1 Added docblocks for the database interface 2012-08-14 16:28:40 +01:00
Alex Bilbie
77ce18df56 Added the resource server database interface 2012-08-14 15:46:58 +01:00
Alex Bilbie
8720de48de Initial update with some PSR-* changes 2012-08-14 15:44:25 +01:00
Alex Bilbie
a9b6cd3e12 Mini fixes 2012-08-14 15:43:11 +01:00
Alex Bilbie
44666402dd Renamed variable 2012-08-14 14:20:14 +01:00
Alex Bilbie
4c82648a9a First commit of Phil's code with some namespace and class name tweaks 2012-08-13 16:36:45 +01:00
Alex Bilbie
176c678c23 Don't remove composer.json and vendor dir 2012-08-06 16:13:27 +01:00
Alex Bilbie
d743412cf7 Trying to get phpunit to log somewhere sensible 2012-08-06 16:10:22 +01:00
Alex Bilbie
1a925788e6 Fix for phpunit log paths 2012-08-06 16:06:03 +01:00
Alex Bilbie
095ea72b62 Added composer install 2012-08-06 15:57:57 +01:00
Alex Bilbie
9aa8f86f3a Remove the composer.lock file and the vendor folder 2012-08-06 15:53:10 +01:00
Alex Bilbie
7c7dfec4c4 Execute the local phpunit 2012-08-06 15:48:33 +01:00
Alex Bilbie
30ef11c1d7 ALL OF THE TESTS! 2012-08-06 15:32:35 +01:00
Alex Bilbie
a9e816f336 Updated name of project and build location 2012-08-06 15:32:25 +01:00
Alex Bilbie
7705805568 build sh tool location fix 2012-08-06 15:32:11 +01:00
Alex Bilbie
95068c5176 Lots of fixes following errors found from unit tests 2012-08-06 14:42:58 +01:00
Alex Bilbie
983c1faf0a Updated build.xml to run PHPUnit 2012-08-06 14:42:18 +01:00
Alex Bilbie
7ea3a045cd Moved the phpunit.xml config file to the build folder 2012-08-06 14:42:02 +01:00
Alex Bilbie
2e315fc257 New tests 2012-08-06 10:59:36 +01:00
Alex Bilbie
a5f019ad19 Call database methods via the dbcall() method 2012-08-06 10:59:20 +01:00
Alex Bilbie
c17cd7b1cc Fixed variable reference bug 2012-08-04 09:12:12 +01:00
Alex Bilbie
bfab30061e Spacing fix 2012-08-04 09:11:39 +01:00
Alex Bilbie
b67a804e19 Removed old test/index.php 2012-08-03 16:13:23 +01:00
Alex Bilbie
0748ba379b test executable 2012-08-01 17:15:48 +01:00
Alex Bilbie
1409df6eb4 Started adding tests for auth server 2012-08-01 17:15:29 +01:00
Alex Bilbie
d0bb79bd7c Added phpunit.xml 2012-08-01 17:15:16 +01:00
Alex Bilbie
9935cc5d21 JSON error fix 2012-08-01 15:44:03 +01:00
Alex Bilbie
93772d0f70 Require PHPunit in dev composer mode 2012-08-01 15:41:41 +01:00
Alex Bilbie
5ef85e53af Removed old test file 2012-08-01 15:41:24 +01:00
Alex Bilbie
5c463a69b8 Little fixes 2012-08-01 14:50:31 +01:00
35 changed files with 2493 additions and 221 deletions

4
.gitattributes vendored Normal file
View File

@@ -0,0 +1,4 @@
tests/ export-ignore
phpunit.xml export-ignore
build.xml export-ignore
test export-ignore

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
/vendor/
/composer.lock
/docs/build/
/build/logs/
/build/coverage/
test

View File

@@ -1,7 +1,47 @@
# 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.
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/).
### Client support
* Merge in https://github.com/philsturgeon/codeigniter-oauth2
---
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.

View File

@@ -1,10 +1,11 @@
<?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,phpdox,phpcb"/>
<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,phpdox,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">
@@ -109,6 +110,20 @@
<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"/>

23
build/phpunit.xml Normal file
View File

@@ -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>

View File

@@ -1,11 +1,15 @@
{
"name": "lncd/Oauth2",
"description": "OAuth 2.0 server - UNDER DEVELOPMENT - NOT READY FOR PRIMETIME",
"version": "0.0.1",
"name": "lncd/oauth2",
"description": "OAuth 2.0 Framework",
"version": "0.3.2",
"homepage": "https://github.com/lncd/OAuth2",
"license": "MIT",
"require": {
"php": ">=5.3.0"
"php": ">=5.3.0",
"guzzle/guzzle": "*"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"repositories": [
{
@@ -16,12 +20,15 @@
"keywords": [
"oauth",
"oauth2",
"server"
"server",
"authorization",
"authentication",
"resource"
],
"authors": [
{
"name": "Alex Bilbie",
"email": "oauth2server@alexbilbie.com",
"email": "hello@alexbilbie.com",
"homepage": "http://www.httpster.org",
"role": "Developer"
}
@@ -30,5 +37,8 @@
"psr-0": {
"Oauth2": "src/"
}
},
"suggest": {
"lncd/oauth2-facebook": "Adds support for Facebook as an IDP"
}
}

View File

@@ -20,7 +20,7 @@ CREATE TABLE `client_endpoints` (
-- 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 '',
`client_id` varchar(40) 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,

View File

@@ -6,30 +6,42 @@ 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
*
* Response:
*
* <code>
* Array
* (
* [client_id] => (string) The client ID
* [client secret] => (string) The client secret
* [redirect_uri] => (string) The redirect URI used in this request
* [name] => (string) The name of the client
* )
* </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]
* @param string $redirectUri The client's redirect URI (default = "null")
* @return bool|array Returns false if the validation fails, array on success
*/
public function validateClient(
$clientId,
@@ -39,16 +51,16 @@ interface Database
/**
* 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")
@@ -56,7 +68,7 @@ interface Database
* @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]
* @return int The session ID
*/
public function newSession(
$clientId,
@@ -71,20 +83,20 @@ interface Database
/**
* 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
* @return void
*/
public function updateSession(
$sessionId,
@@ -96,16 +108,16 @@ interface Database
/**
* 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 $type The session owner's type
* @param string $typeId The session owner's ID
* @return [type] [description]
* @return void
*/
public function deleteSession(
$clientId,
@@ -115,16 +127,16 @@ interface Database
/**
* 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
* (
@@ -141,12 +153,12 @@ interface Database
* 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
* @return int|bool Returns the session ID if the auth code
* is valid otherwise returns false
*/
public function validateAuthCode(
$clientId,
@@ -156,18 +168,18 @@ interface Database
/**
* 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 $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
* @return string|null Return the session ID as an integer if
* found otherwise returns false
*/
public function hasSession(
@@ -178,13 +190,13 @@ interface Database
/**
* 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
@@ -193,13 +205,13 @@ interface Database
/**
* 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
*/
@@ -207,14 +219,14 @@ interface Database
/**
* Sets a sessions access token
*
*
* Database query:
*
*
* <code>
* UPDATE oauth_sessions SET access_token = $accessToken WHERE id =
* 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
@@ -226,14 +238,14 @@ interface Database
/**
* Associates a session with a scope
*
*
* Database query:
*
*
* <code>
* INSERT INTO oauth_session_scopes (session_id, scope) VALUE ($sessionId,
* 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
@@ -245,15 +257,15 @@ interface Database
/**
* Return information about a scope
*
*
* Database query:
*
*
* <code>
* SELECT * FROM scopes WHERE scope = $scope
* </code>
*
*
* Response:
*
*
* <code>
* Array
* (
@@ -263,22 +275,22 @@ interface Database
* [description] => (string) The scope's description
* )
* </code>
*
*
* @param string $scope The scope
* @return array
* @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
* 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
@@ -290,17 +302,17 @@ interface Database
/**
* 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 =
* 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
* (
@@ -312,7 +324,7 @@ interface Database
* )
* )
* </code>
*
*
* @param string $accessToken The access token
* @return array
*/

View File

@@ -2,17 +2,17 @@
namespace Oauth2\Authentication;
class OAuthServerClientException extends \Exception
class ClientException extends \Exception
{
}
class OAuthServerUserException extends \Exception
class UserException extends \Exception
{
}
class OAuthServerException extends \Exception
class ServerException extends \Exception
{
}
@@ -23,13 +23,13 @@ class Server
* Reference to the database abstractor
* @var object
*/
private $db;
private $_db = null;
/**
* Server configuration
* @var array
*/
private $config = array(
private $_config = array(
'scope_delimeter' => ',',
'access_token_ttl' => null
);
@@ -38,7 +38,7 @@ class Server
* Supported response types
* @var array
*/
private $response_types = array(
private $_responseTypes = array(
'code'
);
@@ -46,7 +46,7 @@ class Server
* Supported grant types
* @var array
*/
private $grant_types = array(
private $_grantTypes = array(
'authorization_code'
);
@@ -97,7 +97,7 @@ class Server
public function __construct($options = null)
{
if ($options !== null) {
$this->options = array_merge($this->config, $options);
$this->options = array_merge($this->_config, $options);
}
}
@@ -110,7 +110,7 @@ class Server
*/
public function registerDbAbstractor($db)
{
$this->db = $db;
$this->_db = $db;
}
/**
@@ -127,47 +127,57 @@ class Server
// Client ID
if ( ! isset($authParams['client_id']) && ! isset($_GET['client_id'])) {
throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
} else {
$params['client_id'] = (isset($authParams['client_id'])) ? $authParams['client_id'] : $_GET['client_id'];
$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);
throw new ClientException(sprintf($this->errors['invalid_request'], 'redirect_uri'), 0);
} else {
$params['redirect_uri'] = (isset($authParams['redirect_uri'])) ? $authParams['redirect_uri'] : $_GET['redirect_uri'];
$params['redirect_uri'] = (isset($authParams['redirect_uri'])) ?
$authParams['redirect_uri'] :
$_GET['redirect_uri'];
}
// Validate client ID and redirect URI
$clientDetails = $this->db->validateClient($params['client_id'], null, $params['redirect_uri']);
$clientDetails = $this->_dbCall(
'validateClient',
$params['client_id'],
null,
$params['redirect_uri']
);
if ($clientDetails === false) {
throw new OAuthServerClientException(
$this->errors['invalid_client'], 8);
throw new ClientException($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);
throw new ClientException(sprintf($this->errors['invalid_request'], 'response_type'), 0);
} else {
$params['response_type'] = (isset($authParams['response_type'])) ? $authParams['response_type'] : $_GET['response_type'];
$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)) {
if ( ! in_array($params['response_type'], $this->_responseTypes)) {
throw new OAuthServerClientException($this->errors['unsupported_response_type'], 3);
throw new ClientException($this->errors['unsupported_response_type'], 3);
}
}
@@ -175,15 +185,15 @@ class Server
// Get and validate scopes
if (isset($authParams['scope']) || isset($_GET['scope'])) {
$scopes = $_GET['scope'];
if (isset($authParams['client_id'])) {
$authParams['scope'];
}
$scopes = (isset($_GET['scope'])) ?
$_GET['scope'] :
$authParams['scope'];
$scopes = explode($this->config['scope_delimeter'], $scopes);
$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] === '') {
@@ -193,18 +203,21 @@ class Server
if (count($scopes) === 0) {
throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'scope'), 0);
throw new ClientException(sprintf($this->errors['invalid_request'], 'scope'), 0);
}
$params['scopes'] = array();
foreach ($scopes as $scope) {
$scopeDetails = $this->db->getScope($scope);
$scopeDetails = $this->_dbCall(
'getScope',
$scope
);
if ($scopeDetails === false) {
throw new OAuthServerClientException(sprintf($this->errors['invalid_scope'], $scope), 4);
throw new ClientException(sprintf($this->errors['invalid_scope'], $scope), 4);
}
@@ -227,7 +240,8 @@ class Server
public function newAuthoriseRequest($type, $typeId, $authoriseParams)
{
// Remove any old sessions the user might have
$this->db->deleteSession(
$this->_dbCall(
'deleteSession',
$authoriseParams['client_id'],
$type,
$typeId
@@ -276,7 +290,8 @@ class Server
// new authorisation code otherwise create a new session
if ($accessToken !== null) {
$this->db->updateSession(
$this->_dbCall(
'updateSession',
$clientId,
$type,
$typeId,
@@ -288,10 +303,11 @@ class Server
} else {
// Delete any existing sessions just to be sure
$this->db->deleteSession($clientId, $type, $typeId);
$this->_dbCall('deleteSession', $clientId, $type, $typeId);
// Create a new session
$sessionId = $this->db->newSession(
$sessionId = $this->_dbCall(
'newSession',
$clientId,
$redirectUri,
$type,
@@ -305,7 +321,11 @@ class Server
// Add the scopes
foreach ($scopes as $key => $scope) {
$this->db->addSessionScope($sessionId, $scope['scope']);
$this->_dbCall(
'addSessionScope',
$sessionId,
$scope['scope']
);
}
@@ -327,19 +347,20 @@ class Server
{
$params = array();
// Grant type (must be 'authorization_code')
if ( ! isset($authParams['grant_type']) && ! isset($_POST['grant_type'])) {
throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'grant_type'), 0);
throw new ClientException(sprintf($this->errors['invalid_request'], 'grant_type'), 0);
} else {
$params['grant_type'] = (isset($authParams['grant_type'])) ? $authParams['grant_type'] : $_POST['grant_type'];
$params['grant_type'] = (isset($authParams['grant_type'])) ?
$authParams['grant_type'] :
$_POST['grant_type'];
// Ensure response type is one that is recognised
if ( ! in_array($params['grant_type'], $this->grant_types)) {
// Ensure grant type is one that is recognised
if ( ! in_array($params['grant_type'], $this->_grantTypes)) {
throw new OAuthServerClientException($this->errors['unsupported_grant_type'], 7);
throw new ClientException($this->errors['unsupported_grant_type'], 7);
}
}
@@ -349,13 +370,13 @@ class Server
case 'authorization_code': // Authorization code grant
return $this->completeAuthCodeGrant($authParams, $params);
break;
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);
throw new ServerException($this->errors['server_error'] . 'Tried to process an unsuppported grant type.', 5);
break;
}
}
@@ -375,38 +396,45 @@ class Server
// Client ID
if ( ! isset($authParams['client_id']) && ! isset($_POST['client_id'])) {
throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_id'), 0);
} else {
$params['client_id'] = (isset($authParams['client_id'])) ? $authParams['client_id'] : $_POST['client_id'];
$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);
throw new ClientException(sprintf($this->errors['invalid_request'], 'client_secret'), 0);
} else {
$params['client_secret'] = (isset($authParams['client_secret'])) ? $authParams['client_secret'] : $_POST['client_secret'];
$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);
throw new ClientException(sprintf($this->errors['invalid_request'], 'redirect_uri'), 0);
} else {
$params['redirect_uri'] = (isset($authParams['redirect_uri'])) ? $authParams['redirect_uri'] : $_POST['redirect_uri'];
$params['redirect_uri'] = (isset($authParams['redirect_uri'])) ?
$authParams['redirect_uri'] :
$_POST['redirect_uri'];
}
// Validate client ID and redirect URI
$clientDetails = $this->db->validateClient(
$clientDetails = $this->_dbCall(
'validateClient',
$params['client_id'],
$params['client_secret'],
$params['redirect_uri']
@@ -414,24 +442,26 @@ class Server
if ($clientDetails === false) {
throw new OAuthServerClientException($this->errors['invalid_client'], 8);
throw new ClientException($this->errors['invalid_client'], 8);
}
// The authorization code
if ( ! isset($authParams['code']) &&
! isset($_POST['code'])) {
if ( ! isset($authParams['code']) && ! isset($_POST['code'])) {
throw new OAuthServerClientException(sprintf($this->errors['invalid_request'], 'code'), 0);
throw new ClientException(sprintf($this->errors['invalid_request'], 'code'), 0);
} else {
$params['code'] = (isset($authParams['code'])) ? $authParams['code'] : $_POST['code'];
$params['code'] = (isset($authParams['code'])) ?
$authParams['code'] :
$_POST['code'];
}
// Verify the authorization code matches the client_id and the
// request_uri
$session = $this->db->validateAuthCode(
$session = $this->_dbCall(
'validateAuthCode',
$params['client_id'],
$params['redirect_uri'],
$params['code']
@@ -439,7 +469,7 @@ class Server
if ( ! $session) {
throw new OAuthServerClientException(sprintf($this->errors['invalid_grant'], 'code'), 9);
throw new ClientException(sprintf($this->errors['invalid_grant'], 'code'), 9);
} else {
@@ -448,9 +478,12 @@ class Server
$accessToken = $this->generateCode();
$accessTokenExpires = ($this->config['access_token_ttl'] === null) ? null : time() + $this->config['access_token_ttl'];
$accessTokenExpires = ($this->_config['access_token_ttl'] === null) ?
null :
time() + $this->_config['access_token_ttl'];
$this->db->updateSession(
$this->_dbCall(
'updateSession',
$session['id'],
null,
$accessToken,
@@ -459,7 +492,8 @@ class Server
);
// Update the session's scopes to reference the access token
$this->db->updateSessionScopeAccessToken(
$this->_dbCall(
'updateSessionScopeAccessToken',
$session['id'],
$accessToken
);
@@ -467,7 +501,7 @@ class Server
return array(
'access_token' => $accessToken,
'token_type' => 'bearer',
'expires_in' => $this->config['access_token_ttl']
'expires_in' => $this->_config['access_token_ttl']
);
}
}
@@ -497,4 +531,27 @@ class Server
return $redirectUri;
}
/**
* Call database methods from the abstractor
*
* @return mixed The query result
*/
private function _dbCall()
{
if ($this->_db === null) {
throw new ServerException('No registered database abstractor');
}
if ( ! $this->_db instanceof Database) {
throw new ServerException('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);
}
}

230
src/Oauth2/Client/IDP.php Normal file
View File

@@ -0,0 +1,230 @@
<?php
namespace OAuth2\Client;
use Guzzle\Service\Client as GuzzleClient;
class IDPException extends \Exception
{
protected $result;
public function __construct($result)
{
$this->result = $result;
$code = isset($result['code']) ? $result['code'] : 0;
if (isset($result['error'])) {
// OAuth 2.0 Draft 10 style
$message = $result['error'];
} elseif (isset($result['message'])) {
// cURL style
$message = $result['message'];
} else {
$message = 'Unknown Error.';
}
parent::__construct($message['message'], $message['code']);
}
public function getType()
{
if (isset($this->result['error'])) {
$message = $this->result['error'];
if (is_string($message)) {
// OAuth 2.0 Draft 10 style
return $message;
}
}
return 'Exception';
}
/**
* To make debugging easier.
*
* @returns
* The string representation of the error.
*/
public function __toString()
{
$str = $this->getType() . ': ';
if ($this->code != 0) {
$str .= $this->code . ': ';
}
return $str . $this->message;
}
}
abstract class IDP {
public $clientId = '';
public $clientSecret = '';
public $redirectUri = '';
public $name;
public $uidKey = 'uid';
public $scopes = array();
public $method = 'post';
public $scopeSeperator = ',';
public $responseType = 'json';
public function __construct($options)
{
foreach ($options as $option => $value) {
if (isset($this->{$option})) {
$this->{$option} = $value;
}
}
}
abstract public function urlAuthorize();
abstract public function urlAccessToken();
abstract public function urlUserDetails(\Oauth2\Client\Token\Access $token);
abstract public function userDetails($response, \Oauth2\Client\Token\Access $token);
public function authorize($options = array())
{
$state = md5(uniqid(rand(), TRUE));
setcookie($this->name.'_authorize_state', $state);
$params = array(
'client_id' => $this->clientId,
'redirect_uri' => $this->redirectUri,
'state' => $state,
'scope' => is_array($this->scope) ? implode($this->scopeSeperator, $this->scope) : $this->scope,
'response_type' => isset($options['response_type']) ? $options['response_type'] : 'code',
'approval_prompt' => 'force' // - google force-recheck
);
header('Location: ' . $this->urlAuthorize().'?'.http_build_query($params));
exit;
}
public function getAccessToken($code = NULL, $options = array())
{
if ($code === NULL) {
throw new \BadMethodCallException('Missing authorization code');
}
$params = array(
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'grant_type' => isset($options['grantType']) ? $options['grantType'] : 'authorization_code',
);
switch ($params['grant_type']) {
case 'authorization_code':
$params['code'] = $code;
$params['redirect_uri'] = isset($options['redirectUri']) ? $options['redirectUri'] : $this->redirectUri;
break;
case 'refresh_token':
$params['refresh_token'] = $code;
break;
}
try {
switch ($this->method) {
case 'get':
$client = new GuzzleClient($this->urlAccessToken() . '?' . http_build_query($params));
$request = $client->send();
$response = $request->getBody();
break;
case 'post':
$client = new GuzzleClient($this->urlAccessToken());
$request = $client->post(null, null, $params)->send();
$response = $request->getBody();
break;
}
}
catch (\Guzzle\Http\Exception\BadResponseException $e)
{
$raw_response = explode("\n", $e->getResponse());
$response = end($raw_response);
}
switch ($this->responseType) {
case 'json':
$result = json_decode($response, true);
break;
case 'string':
parse_str($response, $result);
break;
}
if (isset($result['error']) && ! empty($result['error'])) {
throw new \Oauth2\Client\IDPException($result);
}
switch ($params['grant_type']) {
case 'authorization_code':
return \Oauth2\Client\Token::factory('access', $result);
break;
case 'refresh_token':
return \Oauth2\Client\Token::factory('refresh', $result);
break;
}
}
public function getUserDetails(\Oauth2\Client\Token\Access $token)
{
$url = $this->urlUserDetails($token);
try {
$client = new GuzzleClient($url);
$request = $client->get()->send();
$response = $request->getBody();
return $this->userDetails(json_decode($response), $token);
}
catch (\Guzzle\Http\Exception\BadResponseException $e)
{
$raw_response = explode("\n", $e->getResponse());
throw new \Oauth2\Client\IDPException(end($raw_response));
}
}
}

19
src/Oauth2/Client/Provider.php Executable file
View File

@@ -0,0 +1,19 @@
<?php
namespace OAuth2\Client;
class Provider
{
private function __constuct() {}
public static function factory($name, array $options = null)
{
if ( ! class_exists($name)) {
throw new OAuth2\Client\Exception('There is no identity provider called: '.$name);
}
return new $name($options);
}
}

View File

@@ -0,0 +1,42 @@
<?php
class Blooie extends Oauth2\Client\IDP
{
public $scope = array('user.profile', 'user.picture');
public $method = 'POST';
public function urlAuthorize()
{
return 'https://bloo.ie/oauth';
}
public function urlAccessToken()
{
return 'https://bloo.ie/oauth/access_token';
}
public function getUserInfo(Oauth2\Token\Access $token)
{
$url = 'https://graph.facebook.com/me?'.http_build_query(array(
'access_token' => $token->access_token,
));
$user = json_decode(file_get_contents($url));
return array(
'uid' => $user->id,
'nickname' => $user->username,
'name' => $user->name,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'email' => isset($user->email) ? $user->email : null,
'location' => isset($user->hometown->name) ? $user->hometown->name : null,
'description' => isset($user->bio) ? $user->bio : null,
'image' => 'https://graph.facebook.com/me/picture?type=normal&access_token='.$token->access_token,
'urls' => array(
'Facebook' => $user->link,
),
);
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Facebook OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Phil Sturgeon
* @copyright (c) 2012 HappyNinjas Ltd
* @license http://philsturgeon.co.uk/code/dbad-license
*/
class Facebook extends Oauth2\Client\IDP
{
protected $scope = array('offline_access', 'email', 'read_stream');
public function urlAuthorize()
{
return 'https://www.facebook.com/dialog/oauth';
}
public function urlAccessToken()
{
return 'https://graph.facebook.com/oauth/access_token';
}
public function getUserInfo(Oauth2\Token\Access $token)
{
$url = 'https://graph.facebook.com/me?'.http_build_query(array(
'access_token' => $token->access_token,
));
$user = json_decode(file_get_contents($url));
return array(
'uid' => $user->id,
'nickname' => isset($user->username) ? $user->username : null,
'name' => $user->name,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'email' => isset($user->email) ? $user->email : null,
'location' => isset($user->hometown->name) ? $user->hometown->name : null,
'description' => isset($user->bio) ? $user->bio : null,
'image' => 'https://graph.facebook.com/me/picture?type=normal&access_token='.$token->access_token,
'urls' => array(
'Facebook' => $user->link,
),
);
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Foursquare OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Phil Sturgeon
* @copyright (c) 2012 HappyNinjas Ltd
* @license http://philsturgeon.co.uk/code/dbad-license
*/
class Foursquare extends Oauth2\Client\IDP
{
public $method = 'POST';
public function urlAuthorize()
{
return 'https://foursquare.com/oauth2/authenticate';
}
public function urlAccessToken()
{
return 'https://foursquare.com/oauth2/access_token';
}
public function getUserInfo(Oauth2\Token\Access $token)
{
$url = 'https://api.foursquare.com/v2/users/self?'.http_build_query(array(
'oauth_token' => $token->access_token,
));
$response = json_decode(file_get_contents($url));
$user = $response->response->user;
// Create a response from the request
return array(
'uid' => $user->id,
'name' => sprintf('%s %s', $user->firstName, $user->lastName),
'email' => $user->contact->email,
'image' => $user->photo,
'location' => $user->homeCity,
);
}
}

View File

@@ -0,0 +1,43 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* GitHub OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Phil Sturgeon
* @copyright (c) 2012 HappyNinjas Ltd
* @license http://philsturgeon.co.uk/code/dbad-license
*/
class OAuth2_Provider_Github extends Oauth2\Client\IDP
{
public function urlAuthorize()
{
return 'https://github.com/login/oauth/authorize';
}
public function urlAccessToken()
{
return 'https://github.com/login/oauth/access_token';
}
public function getUserInfo(Oauth\Token\Access $token)
{
$url = 'https://api.github.com/user?'.http_build_query(array(
'access_token' => $token->access_token,
));
$user = json_decode(file_get_contents($url));
return array(
'uid' => $user->id,
'nickname' => $user->login,
'name' => $user->name,
'email' => $user->email,
'urls' => array(
'GitHub' => 'http://github.com/'.$user->login,
'Blog' => $user->blog,
),
);
}
}

View File

@@ -0,0 +1,84 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Google OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Phil Sturgeon
* @copyright (c) 2012 HappyNinjas Ltd
* @license http://philsturgeon.co.uk/code/dbad-license
*/
class OAuth2_Provider_Google extends OAuth2_Provider
{
/**
* @var string the method to use when requesting tokens
*/
public $method = 'POST';
/**
* @var string scope separator, most use "," but some like Google are spaces
*/
public $scope_seperator = ' ';
public function url_authorize()
{
return 'https://accounts.google.com/o/oauth2/auth';
}
public function url_access_token()
{
return 'https://accounts.google.com/o/oauth2/token';
}
public function __construct(array $options = array())
{
// Now make sure we have the default scope to get user data
empty($options['scope']) and $options['scope'] = array(
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email'
);
// Array it if its string
$options['scope'] = (array) $options['scope'];
parent::__construct($options);
}
/*
* Get access to the API
*
* @param string The access code
* @return object Success or failure along with the response details
*/
public function access($code, $options = array())
{
if ($code === null)
{
throw new OAuth2_Exception(array('message' => 'Expected Authorization Code from '.ucfirst($this->name).' is missing'));
}
return parent::access($code, $options);
}
public function get_user_info(OAuth2_Token_Access $token)
{
$url = 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json&'.http_build_query(array(
'access_token' => $token->access_token,
));
$user = json_decode(file_get_contents($url), true);
return array(
'uid' => $user['id'],
'nickname' => url_title($user['name'], '_', true),
'name' => $user['name'],
'first_name' => $user['given_name'],
'last_name' => $user['family_name'],
'email' => $user['email'],
'location' => null,
'image' => (isset($user['picture'])) ? $user['picture'] : null,
'description' => null,
'urls' => array(),
);
}
}

View File

@@ -0,0 +1,48 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Instagram OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Phil Sturgeon
* @copyright (c) 2012 HappyNinjas Ltd
* @license http://philsturgeon.co.uk/code/dbad-license
*/
class OAuth2_Provider_Instagram extends OAuth2_Provider
{
/**
* @var string scope separator, most use "," but some like Google are spaces
*/
public $scope_seperator = '+';
/**
* @var string the method to use when requesting tokens
*/
public $method = 'POST';
public function url_authorize()
{
return 'https://api.instagram.com/oauth/authorize';
}
public function url_access_token()
{
return 'https://api.instagram.com/oauth/access_token';
}
public function get_user_info(OAuth2_Token_Access $token)
{
$user = $token->user;
return array(
'uid' => $user->id,
'nickname' => $user->username,
'name' => $user->full_name,
'image' => $user->profile_picture,
'urls' => array(
'website' => $user->website,
),
);
}
}

View File

@@ -0,0 +1,36 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Mailchimp OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Phil Sturgeon
* @copyright (c) 2012 HappyNinjas Ltd
* @license http://philsturgeon.co.uk/code/dbad-license
*/
class OAuth2_Provider_Mailchimp extends OAuth2_Provider
{
/**
* @var string the method to use when requesting tokens
*/
protected $method = 'POST';
public function url_authorize()
{
return 'https://login.mailchimp.com/oauth2/authorize';
}
public function url_access_token()
{
return 'https://login.mailchimp.com/oauth2/token';
}
public function get_user_info(OAuth2_Token_Access $token)
{
// Create a response from the request
return array(
'uid' => $token->access_token,
);
}
}

View File

@@ -0,0 +1,73 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Mailru OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Lavr Lyndin
*/
class OAuth2_Provider_Mailru extends OAuth2_Provider
{
public $method = 'POST';
public function url_authorize()
{
return 'https://connect.mail.ru/oauth/authorize';
}
public function url_access_token()
{
return 'https://connect.mail.ru/oauth/token';
}
protected function sign_server_server(array $request_params, $secret_key)
{
ksort($request_params);
$params = '';
foreach ($request_params as $key => $value) {
$params .= "$key=$value";
}
return md5($params . $secret_key);
}
public function get_user_info(OAuth2_Token_Access $token)
{
$request_params = array(
'app_id' => $this->client_id,
'method' => 'users.getInfo',
'uids' => $token->uid,
'access_token' => $token->access_token,
'secure' => 1
);
$sig = $this->sign_server_server($request_params,$this->client_secret);
$url = 'http://www.appsmail.ru/platform/api?'.http_build_query($request_params).'&sig='.$sig;
$user = json_decode(file_get_contents($url));
return array(
'uid' => $user[0]->uid,
'nickname' => $user[0]->nick,
'name' => $user[0]->first_name.' '.$user[0]->last_name,
'first_name' => $user[0]->first_name,
'last_name' => $user[0]->last_name,
'email' => isset($user[0]->email) ? $user[0]->email : null,
'image' => isset($user[0]->pic_big) ? $user[0]->pic_big : null,
);
}
public function authorize($options = array())
{
$state = md5(uniqid(rand(), TRUE));
get_instance()->session->set_userdata('state', $state);
$params = array(
'client_id' => $this->client_id,
'redirect_uri' => isset($options['redirect_uri']) ? $options['redirect_uri'] : $this->redirect_uri,
'response_type' => 'code',
);
redirect($this->url_authorize().'?'.http_build_query($params));
}
}

View File

@@ -0,0 +1,59 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* PayPal OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Phil Sturgeon
* @copyright (c) 2012 HappyNinjas Ltd
* @license http://philsturgeon.co.uk/code/dbad-license
*/
class OAuth2_Provider_Paypal extends OAuth2_Provider
{
/**
* @var string default scope (useful if a scope is required for user info)
*/
protected $scope = array('https://identity.x.com/xidentity/resources/profile/me');
/**
* @var string the method to use when requesting tokens
*/
protected $method = 'POST';
public function url_authorize()
{
return 'https://identity.x.com/xidentity/resources/authorize';
}
public function url_access_token()
{
return 'https://identity.x.com/xidentity/oauthtokenservice';
}
public function get_user_info(OAuth2_Token_Access $token)
{
$url = 'https://identity.x.com/xidentity/resources/profile/me?' . http_build_query(array(
'oauth_token' => $token->access_token
));
$user = json_decode(file_get_contents($url));
$user = $user->identity;
return array(
'uid' => $user['userId'],
'nickname' => url_title($user['fullName'], '_', true),
'name' => $user['fullName'],
'first_name' => $user['firstName'],
'last_name' => $user['lastName'],
'email' => $user['emails'][0],
'location' => $user->addresses[0],
'image' => null,
'description' => null,
'urls' => array(
'PayPal' => null
)
);
}
}

View File

@@ -0,0 +1,51 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Soundcloud OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Phil Sturgeon
* @copyright (c) 2012 HappyNinjas Ltd
* @license http://philsturgeon.co.uk/code/dbad-license
*/
class OAuth2_Provider_Soundcloud extends OAuth2_Provider
{
/**
* @var string the method to use when requesting tokens
*/
protected $method = 'POST';
public function url_authorize()
{
return 'https://soundcloud.com/connect';
}
public function url_access_token()
{
return 'https://api.soundcloud.com/oauth2/token';
}
public function get_user_info(OAuth2_Token_Access $token)
{
$url = 'https://api.soundcloud.com/me.json?'.http_build_query(array(
'oauth_token' => $token->access_token,
));
$user = json_decode(file_get_contents($url));
// Create a response from the request
return array(
'uid' => $user->id,
'nickname' => $user->username,
'name' => $user->full_name,
'location' => $user->country.' ,'.$user->country,
'description' => $user->description,
'image' => $user->avatar_url,
'urls' => array(
'MySpace' => $user->myspace_name,
'Website' => $user->website,
),
);
}
}

View File

@@ -0,0 +1,54 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Vkontakte OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Lavr Lyndin
*/
class OAuth2_Provider_Vkontakte extends OAuth2_Provider
{
protected $method = 'POST';
public $uid_key = 'user_id';
public function url_authorize()
{
return 'http://oauth.vk.com/authorize';
}
public function url_access_token()
{
return 'https://oauth.vk.com/access_token';
}
public function get_user_info(OAuth2_Token_Access $token)
{
$scope = array('nickname', 'screen_name','photo_big');
$url = 'https://api.vk.com/method/users.get?'.http_build_query(array(
'uids' => $token->uid,
'fields' => implode(",",$scope),
'access_token' => $token->access_token,
));
$user = json_decode(file_get_contents($url))->response;
if(sizeof($user)==0)
return null;
else
$user = $user[0];
return array(
'uid' => $user->uid,
'nickname' => isset($user->nickname) ? $user->nickname : null,
'name' => isset($user->name) ? $user->name : null,
'first_name' => isset($user->first_name) ? $user->first_name : null,
'last_name' => isset($user->last_name) ? $user->last_name : null,
'email' => null,
'location' => null,
'description' => null,
'image' => isset($user->photo_big) ? $user->photo_big : null,
'urls' => array(),
);
}
}

View File

@@ -0,0 +1,60 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Windows Live OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Phil Sturgeon
* @copyright (c) 2012 HappyNinjas Ltd
* @license http://philsturgeon.co.uk/code/dbad-license
*/
class OAuth2_Provider_Windowslive extends OAuth2_Provider
{
protected $scope = array('wl.basic', 'wl.emails');
/**
* @var string the method to use when requesting tokens
*/
protected $method = 'POST';
// authorise url
public function url_authorize()
{
return 'https://oauth.live.com/authorize';
}
// access token url
public function url_access_token()
{
return 'https://oauth.live.com/token';
}
// get basic user information
/********************************
** this can be extended through the
** use of scopes, check out the document at
** http://msdn.microsoft.com/en-gb/library/hh243648.aspx#user
*********************************/
public function get_user_info(OAuth2_Token_Access $token)
{
// define the get user information token
$url = 'https://apis.live.net/v5.0/me?'.http_build_query(array(
'access_token' => $token->access_token,
));
// perform network request
$user = json_decode(file_get_contents($url));
// create a response from the request and return it
return array(
'uid' => $user->id,
'name' => $user->name,
'nickname' => url_title($user->name, '_', true),
// 'location' => $user[''], # scope wl.postal_addresses is required
# but won't be implemented by default
'locale' => $user->locale,
'urls' => array('Windows Live' => $user->link),
);
}
}

View File

@@ -0,0 +1,115 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Yandex OAuth2 Provider
*
* @package CodeIgniter/OAuth2
* @category Provider
* @author Lavr Lyndin
*/
class OAuth2_Provider_Yandex extends OAuth2_Provider
{
public $method = 'POST';
public function url_authorize()
{
return 'https://oauth.yandex.ru/authorize';
}
public function url_access_token()
{
return 'https://oauth.yandex.ru/token';
}
public function get_user_info(OAuth2_Token_Access $token)
{
$opts = array(
'http' => array(
'method' => 'GET',
'header' => 'Authorization: OAuth '.$token->access_token
)
);
$_default_opts = stream_context_get_params(stream_context_get_default());
$opts = array_merge_recursive($_default_opts['options'], $opts);
$context = stream_context_create($opts);
$url = 'http://api-yaru.yandex.ru/me/?format=json';
$user = json_decode(file_get_contents($url,false,$context));
preg_match("/\d+$/",$user->id,$uid);
return array(
'uid' => $uid[0],
'nickname' => isset($user->name) ? $user->name : null,
'name' => isset($user->name) ? $user->name : null,
'first_name' => isset($user->first_name) ? $user->first_name : null,
'last_name' => isset($user->last_name) ? $user->last_name : null,
'email' => isset($user->email) ? $user->email : null,
'location' => isset($user->hometown->name) ? $user->hometown->name : null,
'description' => isset($user->bio) ? $user->bio : null,
'image' => $user->links->userpic,
);
}
public function access($code, $options = array())
{
$params = array(
'client_id' => $this->client_id,
'client_secret' => $this->client_secret,
'grant_type' => isset($options['grant_type']) ? $options['grant_type'] : 'authorization_code',
);
switch ($params['grant_type'])
{
case 'authorization_code':
$params['code'] = $code;
$params['redirect_uri'] = isset($options['redirect_uri']) ? $options['redirect_uri'] : $this->redirect_uri;
break;
case 'refresh_token':
$params['refresh_token'] = $code;
break;
}
$response = null;
$url = $this->url_access_token();
$curl = curl_init($url);
$headers[] = 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8;';
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
// curl_setopt($curl, CURLOPT_USERAGENT, 'yamolib-php');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($curl, CURLOPT_TIMEOUT, 80);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
// curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
// curl_setopt($curl, CURLOPT_CAINFO, dirname(__FILE__) . '/../data/ca-certificate.crt');
$response = curl_exec($curl);
curl_close($curl);
$return = json_decode($response, true);
if ( ! empty($return['error']))
{
throw new OAuth2_Exception($return);
}
switch ($params['grant_type'])
{
case 'authorization_code':
return OAuth2_Token::factory('access', $return);
break;
case 'refresh_token':
return OAuth2_Token::factory('refresh', $return);
break;
}
}
}

46
src/Oauth2/Client/Token.php Executable file
View File

@@ -0,0 +1,46 @@
<?php
namespace Oauth2\Client;
abstract class Token
{
/**
* Create a new token object.
*
* @param string token type
* @param array token options
* @return Token
*/
public static function factory($name = 'access', array $options = null)
{
include_once 'Token/'.ucfirst(strtolower($name)).'.php';
$class = 'Oauth2\Client\Token\\'.ucfirst($name);
return new $class($options);
}
/**
* Return the value of any protected class variable.
*
* @param string variable name
* @return mixed
*/
public function __get($key)
{
return $this->$key;
}
/**
* Return a boolean if the property is set
*
* @param string variable name
* @return bool
*/
public function __isset($key)
{
return isset($this->$key);
}
} // End Token

View File

@@ -0,0 +1,79 @@
<?php
namespace Oauth2\Client\Token;
/**
* OAuth2 Token
*
* @package OAuth2
* @category Token
* @author Phil Sturgeon
* @copyright (c) 2011 HappyNinjas Ltd
*/
class Access extends \Oauth2\Client\Token
{
/**
* @var string accessToken
*/
protected $accessToken;
/**
* @var int expires
*/
protected $expires;
/**
* @var string refreshToken
*/
protected $refreshToken;
/**
* @var string uid
*/
protected $uid;
/**
* Sets the token, expiry, etc values.
*
* @param array token options
* @return void
*/
public function __construct(array $options = null)
{
if ( ! isset($options['access_token'])) {
throw new \BadMethodCallException('Required option not passed: access_token'.PHP_EOL.print_r($options, true));
}
$this->accessToken = $options['access_token'];
// Some providers (not many) give the uid here, so lets take it
isset($options['uid']) and $this->uid = $options['uid'];
//Vkontakte uses user_id instead of uid
isset($options['user_id']) and $this->uid = $options['user_id'];
//Mailru uses x_mailru_vid instead of uid
isset($options['x_mailru_vid']) and $this->uid = $options['x_mailru_vid'];
// We need to know when the token expires, add num. seconds to current time
isset($options['expires_in']) and $this->expires = time() + ((int) $options['expires_in']);
// Facebook is just being a spec ignoring jerk
isset($options['expires']) and $this->expires = time() + ((int) $options['expires']);
// Grab a refresh token so we can update access tokens when they expires
isset($options['refresh_token']) and $this->refreshToken = $options['refresh_token'];
}
/**
* Returns the token key.
*
* @return string
*/
public function __toString()
{
return (string) $this->accessToken;
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* OAuth2 Token
*
* @package OAuth2
* @category Token
* @author Phil Sturgeon
* @copyright (c) 2011 HappyNinjas Ltd
*/
class Authorize extends \Oauth2\Client\Token
{
/**
* @var string code
*/
protected $code;
/**
* @var string redirect_uri
*/
protected $redirectUri;
/**
* Sets the token, expiry, etc values.
*
* @param array token options
* @return void
*/
public function __construct(array $options)
{
if ( ! isset($options['code'])) {
throw new Exception('Required option not passed: code');
} elseif ( ! isset($options['redirect_uri'])) {
throw new Exception('Required option not passed: redirect_uri');
}
$this->code = $options['code'];
$this->redirectUri = $options['redirect_uri'];
}
/**
* Returns the token key.
*
* @return string
*/
public function __toString()
{
return (string) $this->code;
}
}

View File

@@ -4,4 +4,56 @@ 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 session_id =
* $sessionId
* </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);
}

View File

@@ -2,7 +2,252 @@
namespace Oauth2\Resource;
class ServerException extends \Exception
{
}
class ClientException 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',
'missing_access_token_details' => 'The registered database abstractor did not return a valid access token details response',
'invalid_access_token_scopes' => 'The registered database abstractor did not return a valid access token scopes response',
);
/**
* 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;
$_SERVER['REQUEST_METHOD'] = isset($_SERVER['REQUEST_METHOD']) ?
$_SERVER['REQUEST_METHOD'] :
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 = $rawToken;
}
}
}
if ($accessToken) {
$result = $this->_dbCall('validateAccessToken', $accessToken);
if ($result === false) {
throw new ClientException($this->errors['invalid_access_token']);
} else {
if ( ! array_key_exists('id', $result) ||
! array_key_exists('owner_id', $result) ||
! array_key_exists('owner_type', $result)) {
throw new ServerException($this->errors['missing_access_token_details']);
}
$this->_accessToken = $accessToken;
$this->_type = $result['owner_type'];
$this->_typeId = $result['owner_id'];
// Get the scopes
$scopes = $this->_dbCall('sessionScopes', $result['id']);
if ( ! is_array($scopes))
{
throw new ServerException($this->errors['invalid_access_token_scopes']);
}
$this->_scopes = $scopes;
}
} else {
throw new ClientException($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 ServerException('No registered database abstractor');
}
if ( ! $this->_db instanceof Database) {
throw new ServerException('The 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);
}
}

View File

@@ -1,84 +0,0 @@
<?php
include_once('../src/oauth2server/Dbinterface.php');
include_once('../src/oauth2server/Server.php');
use \oauth2server;
class db extends oauth2server\ServerDB {
public function validateClient(
string $clientId,
string $clientSecret = null,
string $redirectUri = null
){}
public function newSession(
string $clientId,
string $redirectUri,
$type = 'user',
string $typeId = null,
string $authCode = null,
string $accessToken = null,
$stage = 'request'
){}
public function updateSession(
string $clientId,
$type = 'user',
string $typeId = null,
string $authCode = null,
string $accessToken = null,
string $stage
){}
public function deleteSession(
string $clientId,
string $type,
string $typeId
){}
public function validateAuthCode(
string $clientId,
string $redirectUri,
string $authCode
){}
/**
* Has access token
*
* Check if an access token exists for a user (or an application)
*
* @access public
* @return bool|string Return FALSE is a token doesn't exist or return the
* access token as a string
*/
public function hasAccessToken(
string $typeId,
string $clientId
){}
public function getAccessToken(int $sessionId){}
public function removeAuthCode(int $sessionId){}
public function setAccessToken(
int $sessionId,
string $accessToken
){}
public function addSessionScope(
int $sessionId,
string $scope
){}
public function getScope(string $scope){}
public function updateSessionScopeAccessToken(
int $sesstionId,
string $accessToken
){}
public function accessTokenScopes(string $accessToken){}
}
$server = new oauth2server\Server();

View File

@@ -0,0 +1,154 @@
<?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 $id;
}
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');
}
}

View File

@@ -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\ClientException
* @expectedExceptionCode 0
*/
function test_checkClientAuthoriseParams_missingClientId()
{
$this->oauth->checkClientAuthoriseParams();
}
/**
* @expectedException Oauth2\Authentication\ClientException
* @expectedExceptionCode 0
*/
function test_checkClientAuthoriseParams_missingRedirectUri()
{
$_GET['client_id'] = 'test';
$this->oauth->checkClientAuthoriseParams();
}
/**
* @expectedException Oauth2\Authentication\ClientException
* @expectedExceptionCode 0
*/
function test_checkClientAuthoriseParams_missingResponseType()
{
$_GET['client_id'] = 'test';
$_GET['redirect_uri'] = 'http://example.com/test';
$this->oauth->checkClientAuthoriseParams();
}
/**
* @expectedException Oauth2\Authentication\ClientException
* @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\ClientException
* @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\ClientException
* @expectedExceptionCode 0
*/
function test_issueAccessToken_missingGrantType()
{
$this->oauth->issueAccessToken();
}
/**
* @expectedException Oauth2\Authentication\ClientException
* @expectedExceptionCode 7
*/
function test_issueAccessToken_unsupportedGrantType()
{
$params['grant_type'] = 'blah';
$this->oauth->issueAccessToken($params);
}
/**
* @expectedException Oauth2\Authentication\ClientException
* @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\ClientException
* @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\ClientException
* @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\ClientException
* @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\ClientException
* @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\ClientException
* @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\ServerException
* @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\ServerException
* @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);
}
}

View File

@@ -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();
}
}

View File

@@ -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\ClientException
* @expectedExceptionMessage An access token was not presented with the request
*/
function test_init_missingToken()
{
$this->server->init();
}
/**
* @expectedException \Oauth2\Resource\ClientException
* @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());
}
}