summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrent Shaffer <bshafs@gmail.com>2013-05-28 01:28:48 +0400
committerBrent Shaffer <bshafs@gmail.com>2013-05-28 01:28:48 +0400
commitf58f5adf9ff7f7dbb0e1938bce1e726d94837cc0 (patch)
tree4175cff0eeb992de6cf93a70367a94b0f595b426
parentd5bb9c8bdbe8ef86c65d6479f81db859f707b865 (diff)
parente82dfa379bf021f2b3c00ce7f89a596ae4b0e5d4 (diff)
downloadoauth2-server-php-0.8.tar.xz
merges develop into trunk for v0.8 tagv0.8
-rw-r--r--.travis.yml1
-rw-r--r--README.md335
-rw-r--r--src/OAuth2/ClientAssertionType/HttpBasic.php48
-rw-r--r--src/OAuth2/ClientAssertionTypeInterface.php9
-rw-r--r--src/OAuth2/CompatibilityInterface.php16
-rw-r--r--src/OAuth2/Controller/AuthorizeController.php76
-rw-r--r--src/OAuth2/Controller/AuthorizeControllerInterface.php10
-rw-r--r--src/OAuth2/Controller/ResourceController.php57
-rw-r--r--src/OAuth2/Controller/ResourceControllerInterface.php10
-rw-r--r--src/OAuth2/Controller/TokenController.php130
-rw-r--r--src/OAuth2/Controller/TokenControllerInterface.php13
-rw-r--r--src/OAuth2/Encryption/JWT.php14
-rw-r--r--src/OAuth2/GrantType/AuthorizationCode.php75
-rw-r--r--src/OAuth2/GrantType/ClientCredentials.php29
-rw-r--r--src/OAuth2/GrantType/JWTBearer.php288
-rw-r--r--src/OAuth2/GrantType/RefreshToken.php69
-rw-r--r--src/OAuth2/GrantType/UserCredentials.php56
-rw-r--r--src/OAuth2/GrantTypeInterface.php15
-rw-r--r--src/OAuth2/Request.php17
-rw-r--r--src/OAuth2/Response.php90
-rw-r--r--src/OAuth2/Response/AuthenticationError.php17
-rw-r--r--src/OAuth2/Response/Error.php48
-rw-r--r--src/OAuth2/Response/ProviderInterface.php6
-rw-r--r--src/OAuth2/Response/Redirect.php44
-rw-r--r--src/OAuth2/ResponseInterface.php20
-rw-r--r--src/OAuth2/ResponseType/AccessToken.php17
-rw-r--r--src/OAuth2/ResponseType/AccessTokenInterface.php19
-rw-r--r--src/OAuth2/ResponseType/AuthorizationCode.php21
-rw-r--r--src/OAuth2/ResponseType/AuthorizationCodeInterface.php20
-rw-r--r--src/OAuth2/ScopeInterface.php6
-rw-r--r--src/OAuth2/Server.php342
-rw-r--r--src/OAuth2/Storage/AccessTokenInterface.php2
-rw-r--r--src/OAuth2/Storage/AuthorizationCodeInterface.php2
-rw-r--r--src/OAuth2/Storage/ClientCredentialsInterface.php2
-rw-r--r--src/OAuth2/Storage/ClientInterface.php7
-rw-r--r--src/OAuth2/Storage/JWTBearerInterface.php3
-rw-r--r--src/OAuth2/Storage/Memory.php31
-rw-r--r--src/OAuth2/Storage/Mongo.php7
-rw-r--r--src/OAuth2/Storage/Pdo.php20
-rw-r--r--src/OAuth2/Storage/Redis.php195
-rw-r--r--src/OAuth2/Storage/RefreshTokenInterface.php3
-rw-r--r--src/OAuth2/Storage/ScopeInterface.php2
-rw-r--r--src/OAuth2/Storage/UserCredentialsInterface.php4
-rw-r--r--src/OAuth2/TokenType/Bearer.php20
-rw-r--r--src/OAuth2/TokenType/MAC.php2
-rw-r--r--src/OAuth2/TokenTypeInterface.php11
-rw-r--r--test/OAuth2/Controller/Authorize/BasicValidationTest.php (renamed from test/OAuth2/Server/Authorize/BasicValidationTest.php)42
-rw-r--r--test/OAuth2/Controller/Authorize/CodeTest.php (renamed from test/OAuth2/Server/Authorize/CodeTest.php)10
-rw-r--r--test/OAuth2/Controller/Authorize/ImplicitTest.php (renamed from test/OAuth2/Server/Authorize/ImplicitTest.php)12
-rw-r--r--test/OAuth2/Controller/Resource/BasicValidationTest.php (renamed from test/OAuth2/Server/Access/BasicValidationTest.php)33
-rw-r--r--test/OAuth2/Controller/Token/BasicValidationTest.php (renamed from test/OAuth2/Server/Grant/BasicValidationTest.php)75
-rw-r--r--test/OAuth2/GrantType/AuthorizationCodeTest.php156
-rw-r--r--test/OAuth2/GrantType/ClientCredentialsTest.php44
-rw-r--r--test/OAuth2/GrantType/JWTBearerTest.php188
-rw-r--r--test/OAuth2/GrantType/RefreshTokenTest.php98
-rw-r--r--test/OAuth2/GrantType/UserCredentialsTest.php69
-rw-r--r--test/OAuth2/RequestTest.php8
-rw-r--r--test/OAuth2/ScopeTest.php (renamed from test/OAuth2/Util/ScopeTest.php)0
-rw-r--r--test/OAuth2/ServerTest.php66
-rw-r--r--test/OAuth2/StorageTest.php7
-rw-r--r--test/config/storage.json45
-rw-r--r--test/lib/OAuth2/Storage/Bootstrap.php5
-rw-r--r--test/lib/OAuth2/Storage/MockRedisClient.php63
63 files changed, 2018 insertions, 1132 deletions
diff --git a/.travis.yml b/.travis.yml
index 517b34c..ad41773 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,7 @@ php:
- 5.2
- 5.3
- 5.4
+ - 5.5
env:
- SKIP_MONGO_TESTS=1
diff --git a/README.md b/README.md
index ebd8ca4..a7385ce 100644
--- a/README.md
+++ b/README.md
@@ -3,12 +3,10 @@ oauth2-server-php
[![Build Status](https://secure.travis-ci.org/bshaffer/oauth2-server-php.png)](http://travis-ci.org/bshaffer/oauth2-server-php)
-A library for implementing an OAuth2 Server in php
+An OAuth2.0 Server in PHP! [View the Full Working Demo](http://brentertainment.com/oauth2) ([code](https://github.com/bshaffer/oauth2-server-demo))
-[View the Full Working Demo!](http://brentertainment.com/oauth2) ([code](https://github.com/bshaffer/oauth2-server-demo))
-
-Autoloading
------------
+Installation
+------------
This library follows the zend [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) standards. A number of
autoloaders exist which can autoload this library for that reason, but if you are not using one, you can register the `OAuth2_Autoloader`:
@@ -18,12 +16,12 @@ require_once('/path/to/oauth2-server-php/src/OAuth2/Autoloader.php');
OAuth2_Autoloader::register();
```
-If you use a package library like [Composer](http://getcomposer.php), add the following to `composer.json`
+Using [Composer](http://getcomposer.php)? Add the following to `composer.json`:
```
{
"require": {
- "bshaffer/oauth2-server-php": "v0.7",
+ "bshaffer/oauth2-server-php": "dev-develop",
...
},
...
@@ -32,127 +30,217 @@ If you use a package library like [Composer](http://getcomposer.php), add the fo
And then run `composer.phar install`
-> Checkout out the tag `v0.7` will ensure your application doesn't break from backwards-compatibility issues, but also this means you
-> will not receive the latest changes. To ride the bleeding edge of development, use `dev-develop` instead.
+> It is highly recommended you check out the [`v0.8`](https://github.com/bshaffer/oauth2-server-php/tree/v0.8) tag to
+> ensure your application doesn't break from backwards-compatibility issues, but also this means you
+> will not receive the latest changes.
+
+Learning OAuth2.0
+-----------------
+
+If you are new to OAuth2, take a little time first to look at the [Oauth2 Demo Application](http://brentertainment.com/oauth2) and the [source code](https://github.com/bshaffer/oauth2-server-demo), and read up on [OAuth2 Flows](http://drupal.org/node/1958718). For everything else, consult the [OAuth2.0 Specification](http://tools.ietf.org/html/rfc6749)
Get Started
-----------
-Before getting started, take a look at the [Oauth2 Demo Application](http://brentertainment.com/oauth2) and the [source code](https://github.com/bshaffer/oauth2-server-demo) for a concrete example of this library in action.
-
-The quickest way to get started is to use the following code, plugging in your database information
-to the constructor of `OAuth2_Storage_Pdo`:
+Here is an example of a bare-bones OAuth2 Server implementation:
```php
$storage = new OAuth2_Storage_Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password));
$server = new OAuth2_Server($storage);
-$server->addGrantType(new OAuth2_GrantType_UserCredentials($storage)); // or some other grant type. This is the simplest
-$server->handleTokenRequest(OAuth2_Request::createFromGlobals())->send();
+$server->addGrantType(new OAuth2_GrantType_AuthorizationCode($storage)); // or any grant type you like!
+$server->handleTokenRequest(OAuth2_Request::createFromGlobals(), new OAuth2_Response())->send();
```
-Let's break this down line by line. The first line is how the OAuth2 data is stored.
-There are several built in storage types, for your convenience. To use PDO Storage,
-instantiate the `OAuth2_Storage_Pdo` class and provide the database connection arguments:
+This library requires you to define a `Storage` object, containing instrutions on how to interact with objects in your storage
+layer such as [OAuth Clients](https://github.com/bshaffer/oauth2-server-php/blob/develop/src/OAuth2/Storage/ClientInterface.php) and
+[Authorization Codes](https://github.com/bshaffer/oauth2-server-php/blob/develop/src/OAuth2/Storage/AuthorizationCodeInterface.php).
+Built-in storage classes include [PDO](https://github.com/bshaffer/oauth2-server-php/blob/develop/src/OAuth2/Storage/Pdo.php),
+[Redis](https://github.com/bshaffer/oauth2-server-php/blob/develop/src/OAuth2/Storage/Redis.php), and
+[Mongo](https://github.com/bshaffer/oauth2-server-php/blob/develop/src/OAuth2/Storage/Mongo.php). The interfaces allow (and encourage)
+the use of your own Storage objects to fit your application's implementation.
+
+Once you've created a storage object, pass it to the server object and define which Grant Types your server is to support. See
+the list of supported [Grant Types](#grant-types) below.
+
+The final step, once the Server object is set up, is to handle the incoming request. Consult the [Server Methods](#server-methods), or
+follow the [Step-by-Step Walkthrough](#step-by-step-walkthrough) to familiarize yourself with the types of requests involved in
+OAuth2.0 workflows.
+
+Step-by-Step Walkthrough
+------------------------
+
+The following instructions provide a detailed walkthrough to help you get an OAuth2 server
+up and running. To see the codebase of an existing OAuth2 server implementing this library,
+check out the [OAuth2 Demo](https://github.com/bshaffer/oauth2-server-demo).
+
+### Define your Schema
+
+The quickest way to get started is to use the following schema to create the default database:
+
+```sql
+CREATE TABLE oauth_clients (client_id TEXT, client_secret TEXT, redirect_uri TEXT);
+CREATE TABLE oauth_access_tokens (access_token TEXT, client_id TEXT, user_id TEXT, expires TIMESTAMP, scope TEXT);
+CREATE TABLE oauth_authorization_codes (authorization_code TEXT, client_id TEXT, user_id TEXT, redirect_uri TEXT, expires TIMESTAMP, scope TEXT);
+CREATE TABLE oauth_users (username TEXT, password TEXT, first_name TEXT, last_name TEXT);
+CREATE TABLE oauth_refresh_tokens (refresh_token TEXT, client_id TEXT, user_id TEXT, expires TIMESTAMP, scope TEXT);
+```
+
+### Create a Token Controller
+
+The first thing you will do is create the **Token Controller**. This is the URI which returns an OAuth2.0 Token to the client.
+Here is an example of a token controller in the file `token.php`:
```php
+// error reporting (this is a demo, after all!)
+ini_set('display_errors',1);error_reporting(E_ALL);
+
+// Autoloading (composer is preferred, but for this example let's just do this)
+require_once('path/to/oauth2-server-php/src/OAuth2/Autoloader.php');
+OAuth2_Autoloader::register();
+
+// $dsn is the Data Source Name for your database, for exmaple "mysql:dbname=my_oauth2_db;host=localhost"
$storage = new OAuth2_Storage_Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password));
+
+// Pass a storage object or array of storage objects to the OAuth2 server class
$server = new OAuth2_Server($storage);
-```
-> Note: `$dsn` is the Data Source Name for your database. For example, if you are using MySQL your dsn will
-> look something like `mysql:dbname=my_database;host=localhost`. If you are using sqlite, your dsn will look
-> something like `sqlite://path/to/my/file.sqlite`.
+// Add the "Client Credentials" grant type (it is the simplest of the grant types)
+$server->addGrantType(new OAuth2_GrantType_ClientCredentials($storage));
-The next step is to add a grant type. This example uses the "User Credentials" grant type, which grants a token based on
-explicit user credentials passed to the request. The [OAuth2 spec](http://tools.ietf.org/html/rfc6749) has no
-restrictions on the number of grant types your server can support and therefore you can add more than one grant type to
-your $server ... read about this [below](https://github.com/bshaffer/oauth2-server-php#grant-types). Each grant type
-also requires storage, so pass the existing storage to the constructor:
+// Handle a request for an OAuth2.0 Access Token and send the response to the client
+$server->handleTokenRequest(OAuth2_Request::createFromGlobals(), new OAuth2_Response())->send();
+```
-```php
-$server->addGrantType(new OAuth2_GrantType_UserCredentials($storage));
+Congratulatons! You have created a **Token Controller**! Do you want to see it in action? Run the following SQL to
+create an OAuth Client:
+
+```sql
+INSERT INTO oauth_clients (client_id, client_secret, redirect_uri) VALUES ("testclient", "testpass", "http://fake/");
```
-Call the `grantAccessToken` method to validate the request for the user credentials grant type. This will return the token
-if successful. Access the server's response object to send the successful response back, or the error response if applicable:
+Now run the following from the command line:
-```php
-$server->handleTokenRequest(OAuth2_Request::createFromGlobals())->send();
+```bash
+curl -u testclient:testpass http://localhost/token.php -d 'grant_type=client_credentials'
```
-This creates the `OAuth2_Request` object from PHP global variables (most common, you can override this if need be) and sends it to the server
-for assessment. The response by default is in json format, and includes the access token if successful, and error codes if not.
+> Note: http://localhost/token.php assumes you have the file `token.php` on your local machine, and you have
+> set up the "localhost" webhost to point to it. This may vary for your application.
-> If you are just getting started, use the SQL below to create the database tables for the default MySQL setup
-> ```sql
-> CREATE TABLE oauth_clients (client_id TEXT, client_secret TEXT, redirect_uri TEXT);
-> CREATE TABLE oauth_access_tokens (access_token TEXT, client_id TEXT, user_id TEXT, expires TIMESTAMP, scope TEXT);
-> CREATE TABLE oauth_authorization_codes (authorization_code TEXT, client_id TEXT, user_id TEXT, redirect_uri TEXT, expires TIMESTAMP, scope TEXT);
-> CREATE TABLE oauth_refresh_tokens (refresh_token TEXT, client_id TEXT, user_id TEXT, expires TIMESTAMP, scope TEXT);
-> ```
+If everything works, you should receive a response like this:
-Server Methods
---------------
+```json
+{"access_token":"03807cb390319329bdf6c777d4dfae9c0d3b3c35","expires_in":3600,"token_type":"bearer","scope":null}
+```
-> ...an end-user (resource owner) can grant a printing
-> service (client) access to her protected photos stored at a photo
-> sharing service (resource server), without sharing her username and
-> password with the printing service. Instead, she authenticates
-> directly with a server trusted by the photo sharing service
-> (authorization server), which issues the printing service delegation-
-> specific credentials (access token).
->
-> ~ OAuth2 ([draft #31](http://tools.ietf.org/html/rfc6749#section-1))
+### Create a Resource Controller
-Most OAuth2 APIs will have endpoints for `Authorize Requests`, `Token Requests`, and `Resource Requests`. The `OAuth2_Server` object has methods to handle each of these requests.
+Now that you are creating tokens, you'll want to validate them in your APIs. Here is an
+example of a resource controller in the file `resource.php`:
-### Authorize Requests
+```php
+// error reporting again
+ini_set('display_errors',1);error_reporting(E_ALL);
-An endpoint requiring the user to authenticate, which redirects back to the client with an `authorization code`.
+// Autoloading again
+require_once('path/to/oauth2-server-php/src/OAuth2/Autoloader.php');
+OAuth2_Autoloader::register();
-**methods**:
+// create your storage again
+$storage = new OAuth2_Storage_Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password));
-`handleAuthorizeRequest`
- * Receives a request object for an authorize request, returns a response object with the appropriate response
+// create your server again
+$server = new OAuth2_Server($storage);
-`validateAuthorizeRequest`
- * Receives a request object, returns false if the incoming request is not a valid Authorize Request. If the request
-is valid, returns an array of retrieved client details together with input.
-Applications should call this before displaying a login or authorization form to the user
+// Handle a request for an OAuth2.0 Access Token and send the response to the client
+if (!$server->verifyResourceRequest(OAuth2_Request::createFromGlobals(), new OAuth2_Response())) {
+ $server->getResponse()->send();
+ die;
+}
+echo json_encode(array('success' => true, 'message' => 'You accessed my APIs!'));
+```
-### Token Requests
+Now run the following from the command line:
-An endpoint which the client uses to exchange the `authorization code` for an `access token`.
+```bash
+curl http://localhost/resource.php -d 'access_token=YOUR_TOKEN'
+```
-**methods**:
+> Note: Use the value returned in "access_token" from the previous step in place of YOUR_TOKEN
-`grantAccessToken`
+If all goes well, you should receive a response like this:
- * Receives a request object for a token request, returns a token if the request is valid.
+```json
+{"success":true,"message":"You accessed my APIs!"}
+```
-`handleTokenRequest`
+### Create an Authorize Controller
- * Receives a request object for a token request, returns a response object for the appropriate response.
+Authorize Controllers are the "killer feature" of OAuth2, and allow for your users to authorize
+third party applications. Here is an example of an authorize controller in `authorize.php`:
-`getClientCredentials`
+```php
+// error reporting again
+ini_set('display_errors',1);error_reporting(E_ALL);
- * parses the client credentials from the request and determines if they are valid
+// Autoloading again
+require_once('path/to/oauth2-server-php/src/OAuth2/Autoloader.php');
+OAuth2_Autoloader::register();
-### Resource Requests
+// create your storage again
+$storage = new OAuth2_Storage_Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password));
-Any API method requiring oauth2 authentication. The server will validate the incomming request, and then allow
-the application to serve back the protected resource.
+// create your server again
+$server = new OAuth2_Server($storage);
-**methods**:
+// Add the "Authorization Code" grant type (this is required for authorization flows)
+$server->addGrantType(new OAuth2_GrantType_AuthorizationCode($storage));
-`verifyResourceRequest`
+$request = OAuth2_Request::createFromGlobals();
+$response = new OAuth2_Response();
- * Receives a request object for a resource request, finds the token if it exists, and returns a Boolean for whether
-the incomming request is valid
+// validate the authorize request
+if (!$server->validateAuthorizeRequest($request, $response)) {
+ $response->send();
+ die;
+}
+// display an authorization form
+if (empty($_POST)) {
+ exit('
+<form method="post">
+ <label>Do You Authorize TestClient?</label><br />
+ <input type="submit" name="authorized" value="yes">
+ <input type="submit" name="authorized" value="no">
+</form>');
+}
+// print the authorization code if the user has authorized your client
+$is_authorized = ($_POST['authorized'] === 'yes');
+$server->handleAuthorizeRequest($request, $response, $is_authorized);
+if ($is_authorized) {
+ // this is only here so that you get to see your code in the cURL request. Otherwise, we'd redirect back to the client
+ $code = substr($response->getHttpHeader('Location'), strpos($response->getHttpHeader('Location'), 'code=')+5);
+ exit("SUCCESS! Authorization Code: $code");
+}
+$response->send();
+```
-`getAccessTokenData`
+Now paste the following URL in your browser
- * Takes a token string as an argument and returns the token data if applicable, or null if the token is invalid
+```
+http://localhost/authorize.php?response_type=code&client_id=testclient
+```
+
+You will be prompted with an authorization form, and receive an authorization code upon clicking "yes"
+
+> Note: The Authorization Code can now be used to receive an access token from your previously
+> created `token.php` endpoint. Just add the following grant type to `token.php`:
+> ```php
+> $server->addGrantType(new OAuth2_GrantType_AuthorizationCode($storage));
+> ```
+> And call this endpoint using the returned authorization code:
+> ```bash
+> curl -u testclient:testpass http://localhost/token.php -d 'grant_type=authorization_code&redirect_uri=http://fake/&code=YOUR_CODE'
+> ```
Grant Types
-----------
@@ -184,6 +272,10 @@ Supported grant types are as follows:
The client can submit refresh token and recieve a new access token e.g. it may be necessary to do this if the access_token had expired.
+ 7. [Extension Grant](http://tools.ietf.org/html/rfc6749#section-4.5)
+
+ Create your own grant type by implementing the `OAuth2_GrantTypeInterface` and adding it to the OAuth2 Server object.
+
When submitting a request for an access_token using either the 'Authorization Code' or 'Resource Owner Password
Credential' grant, a refresh_token is provided. However, When using the refresh_token from above to request a new
access_token, a new refresh_token is not provided. The spec does not strictly require a refresh_token be granted but it
@@ -193,8 +285,7 @@ As a result, the option always_issue_new_refresh_token was added (defaults to FA
[OAuth2_GrantType_RefreshToken](src/OAuth2/GrantType/RefreshToken.php) class. So, by default a new refresh token is not
issued, but you can easily configure this to do so by setting `'always_issue_new_refresh_token' => true`
-If you want to support more than one grant type it is possible to add more than 1 type to the $server as you
-initialize, see below.
+If you want to support more than one grant type it is possible to add more when the Server object is created:
```php
$server->addGrantType(new OAuth2_GrantType_UserCredentials($storage));
@@ -202,7 +293,64 @@ $server->addGrantType(new OAuth2_GrantType_RefreshToken($storage));
$server->addGrantType(new OAuth2_GrantType_AuthorizationCode($storage));
```
-Create a custom grant type by implementing the `OAuth2_GrantTypeInterface` and adding it to the OAuth2 Server object.
+Server Methods
+--------------
+
+> ...an end-user (resource owner) can grant a printing
+> service (client) access to her protected photos stored at a photo
+> sharing service (resource server), without sharing her username and
+> password with the printing service. Instead, she authenticates
+> directly with a server trusted by the photo sharing service
+> (authorization server), which issues the printing service delegation-
+> specific credentials (access token).
+>
+> ~ OAuth2 ([draft #31](http://tools.ietf.org/html/rfc6749#section-1))
+
+Most OAuth2 APIs will have endpoints for `Authorize Requests`, `Token Requests`, and `Resource Requests`. The `OAuth2_Server` object has methods to handle each of these requests.
+
+### Authorize Requests
+
+An endpoint requiring the user to authenticate, which redirects back to the client with an `authorization code`.
+
+**methods**:
+
+`handleAuthorizeRequest`
+ * Receives a request object for an authorize request, returns a response object with the appropriate response
+
+`validateAuthorizeRequest`
+ * Receives a request object, returns false if the incoming request is not a valid Authorize Request. If the request
+is valid, returns an array of retrieved client details together with input.
+Applications should call this before displaying a login or authorization form to the user
+
+### Token Requests
+
+An endpoint which the client uses to exchange the `authorization code` for an `access token`.
+
+**methods**:
+
+`grantAccessToken`
+
+ * Receives a request object for a token request, returns a token if the request is valid.
+
+`handleTokenRequest`
+
+ * Receives a request object for a token request, returns a response object for the appropriate response.
+
+### Resource Requests
+
+Any API method requiring oauth2 authentication. The server will validate the incomming request, and then allow
+the application to serve back the protected resource.
+
+**methods**:
+
+`verifyResourceRequest`
+
+ * Receives a request object for a resource request, finds the token if it exists, and returns a Boolean for whether
+the incomming request is valid
+
+`getAccessTokenData`
+
+ * Takes a token string as an argument and returns the token data if applicable, or null if the token is invalid
The Response Object
-------------------
@@ -211,16 +359,19 @@ The response object serves the purpose of making your server OAuth2 compliant.
and response body for a valid or invalid oauth request. To use it as it's simplest level, just send the output and exit:
```php
+$request = OAuth2_Request::createFromGlobals();
+$response = new OAuth2_Response();
+
// will set headers, status code, and json response appropriately for success or failure
-$server->grantAccessToken();
-$server->getResponse()->send();
+$server->grantAccessToken($request, $response);
+$response->send();
```
The response object can also be used to customize output. Below, if the request is NOT valid, the error is sent to the browser:
```php
-if (!$token = $server->grantAccessToken()) {
- $server->getResponse()->send();
+if (!$token = $server->grantAccessToken($request, $response)) {
+ $response->send();
die();
}
echo sprintf('Your token is %s!!', $token);
@@ -230,8 +381,7 @@ This will populate the appropriate error headers, and return a json error respon
the response object can be used to display the information in any other format:
```php
-if (!$token = $server->grantAccessToken()) {
- $response = $server->getResponse();
+if (!$token = $server->grantAccessToken($request, $response)) {
$parameters = $response->getParameters();
// format as XML
header("HTTP/1.1 " . $response->getStatusCode());
@@ -309,10 +459,11 @@ access it:
```php
// https://api.example.com/resource-requiring-postonwall-scope
$request = OAuth2_Request::createFromGlobals();
+$response = new OAuth2_Response();
$scopeRequired = 'postonwall'; // this resource requires "postonwall" scope
-if (!$server->verifyResourceRequest($request, $scopeRequired)) {
+if (!$server->verifyResourceRequest($request, $response, $scopeRequired)) {
// if the scope required is different from what the token allows, this will send a "401 insufficient_scope" error
- $server->getRequest()->send();
+ $response->send();
}
```
diff --git a/src/OAuth2/ClientAssertionType/HttpBasic.php b/src/OAuth2/ClientAssertionType/HttpBasic.php
index aa1d130..db376b0 100644
--- a/src/OAuth2/ClientAssertionType/HttpBasic.php
+++ b/src/OAuth2/ClientAssertionType/HttpBasic.php
@@ -5,11 +5,11 @@
*
* @author Brent Shaffer <bshafs at gmail dot com>
*/
-class OAuth2_ClientAssertionType_HttpBasic implements OAuth2_ClientAssertionTypeInterface, OAuth2_Response_ProviderInterface
+class OAuth2_ClientAssertionType_HttpBasic implements OAuth2_ClientAssertionTypeInterface
{
- private $response;
private $storage;
private $config;
+ private $clientData;
public function __construct(OAuth2_Storage_ClientCredentialsInterface $storage, array $config = array())
{
@@ -19,34 +19,36 @@ class OAuth2_ClientAssertionType_HttpBasic implements OAuth2_ClientAssertionType
$this->storage = $storage;
}
- public function getClientDataFromRequest(OAuth2_RequestInterface $request)
+ public function validateRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- if (!$clientData = $this->getClientCredentials($request)) {
- return null;
- }
-
- if (!isset($clientData['client_id']) || !isset($clientData['client_secret'])) {
- throw new LogicException('the clientData array must have "client_id" and "client_secret" values set.');
- }
+ if (!$clientData = $this->getClientCredentials($request, $response)) {
+ return false;
+ }
- return $clientData;
- }
+ if (!isset($clientData['client_id']) || !isset($clientData['client_secret'])) {
+ throw new LogicException('the clientData array must have "client_id" and "client_secret" values set.');
+ }
- public function validateClientData(array $clientData, $grantTypeIdentifier)
- {
if ($this->storage->checkClientCredentials($clientData['client_id'], $clientData['client_secret']) === false) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_client', 'The client credentials are invalid');
+ $response->setError(400, 'invalid_client', 'The client credentials are invalid');
return false;
}
- if (!$this->storage->checkRestrictedGrantType($clientData['client_id'], $grantTypeIdentifier)) {
- $this->response = new OAuth2_Response_Error(400, 'unauthorized_client', 'The grant type is unauthorized for this client_id');
+ if (!$this->storage->checkRestrictedGrantType($clientData['client_id'], $request->request('grant_type'))) {
+ $response->setError(400, 'unauthorized_client', 'The grant type is unauthorized for this client_id');
return false;
}
+ $this->clientData = $clientData;
+
return true;
}
+ public function getClientId()
+ {
+ return $this->clientData['client_id'];
+ }
+
/**
* Internal function used to get the client credentials from HTTP basic
* auth or POST data.
@@ -67,7 +69,7 @@ class OAuth2_ClientAssertionType_HttpBasic implements OAuth2_ClientAssertionType
*
* @ingroup oauth2_section_2
*/
- public function getClientCredentials(OAuth2_RequestInterface $request)
+ public function getClientCredentials(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response = null)
{
if (!is_null($request->headers('PHP_AUTH_USER')) && !is_null($request->headers('PHP_AUTH_PW'))) {
return array('client_id' => $request->headers('PHP_AUTH_USER'), 'client_secret' => $request->headers('PHP_AUTH_PW'));
@@ -80,12 +82,10 @@ class OAuth2_ClientAssertionType_HttpBasic implements OAuth2_ClientAssertionType
}
}
- $this->response = new OAuth2_Response_Error(400, 'invalid_client', 'Client credentials were not found in the headers or body');
- return null;
- }
+ if ($response) {
+ $response->setError(400, 'invalid_client', 'Client credentials were not found in the headers or body');
+ }
- public function getResponse()
- {
- return $this->response;
+ return null;
}
} \ No newline at end of file
diff --git a/src/OAuth2/ClientAssertionTypeInterface.php b/src/OAuth2/ClientAssertionTypeInterface.php
index 9d034c4..93f6005 100644
--- a/src/OAuth2/ClientAssertionTypeInterface.php
+++ b/src/OAuth2/ClientAssertionTypeInterface.php
@@ -1,8 +1,11 @@
<?php
-interface OAuth2_ClientAssertionTypeInterface
+/**
+ * Interface for all OAuth2 Client Assertion Types
+ *
+ * @see OAuth2_CompatibilityInterface
+ */
+interface OAuth2_ClientAssertionTypeInterface extends OAuth2_CompatibilityInterface
{
- public function getClientDataFromRequest(OAuth2_RequestInterface $request);
- public function validateClientData(array $clientData, $grantTypeIdentifier);
}
diff --git a/src/OAuth2/CompatibilityInterface.php b/src/OAuth2/CompatibilityInterface.php
new file mode 100644
index 0000000..6707573
--- /dev/null
+++ b/src/OAuth2/CompatibilityInterface.php
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * OAuth2_CompatibilityInterface
+ * This interface exists because PHP 5.2 cannot combine matching
+ * functions from separate interfaces.
+ *
+ * Even though OAuth2_ClientAssertionTypeInterface and OAuth2_GrantTypeInterface
+ * have common methods, it doesn't actually make sense to extend a base interface.
+ * However, this is temporarily required for the library to run in PHP 5.2
+ *
+ */
+interface OAuth2_CompatibilityInterface
+{
+
+} \ No newline at end of file
diff --git a/src/OAuth2/Controller/AuthorizeController.php b/src/OAuth2/Controller/AuthorizeController.php
index 7075c3d..701e886 100644
--- a/src/OAuth2/Controller/AuthorizeController.php
+++ b/src/OAuth2/Controller/AuthorizeController.php
@@ -1,11 +1,10 @@
<?php
/**
- * @see OAuth2_Controller_AuthorizeControllerInterface
+ * @see OAuth2_Controller_AuthorizeControllerInterface
*/
class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_AuthorizeControllerInterface
{
- private $response;
private $clientStorage;
private $responseTypes;
private $config;
@@ -26,7 +25,7 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori
$this->scopeUtil = $scopeUtil;
}
- public function handleAuthorizeRequest(OAuth2_RequestInterface $request, $is_authorized, $user_id = null)
+ public function handleAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $is_authorized, $user_id = null)
{
if (!is_bool($is_authorized)) {
throw new InvalidArgumentException('Argument "is_authorized" must be a boolean. This method must know if the user has granted access to the client.');
@@ -34,45 +33,42 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori
// We repeat this, because we need to re-validate. In theory, this could be POSTed
// by a 3rd-party (because we are not internally enforcing NONCEs, etc)
- if (!$params = $this->validateAuthorizeRequest($request)) {
- return $this->response;
+ if (!$params = $this->validateAuthorizeRequest($request, $response)) {
+ return;
}
if ($is_authorized === false) {
- $this->response = new OAuth2_Response_Redirect($params['redirect_uri'], 302, 'access_denied', "The user denied access to your application", $params['state']);
- return $this->response;
+ $response->setRedirect(302, $params['redirect_uri'], $params['state'], 'access_denied', "The user denied access to your application");
+ return;
}
- if (!$authResult = $this->responseTypes[$params['response_type']]->getAuthorizeResponse($params, $user_id)) {
- // an error has occurred along the way, return error response
- return $this->response;
- }
+ $authResult = $this->responseTypes[$params['response_type']]->getAuthorizeResponse($params, $user_id);
- list($redirect_uri, $result) = $authResult;
- $uri = $this->buildUri($redirect_uri, $result);
+ list($redirect_uri, $uri_params) = $authResult;
+ $uri = $this->buildUri($redirect_uri, $uri_params);
// return redirect response
- return $this->response = new OAuth2_Response_Redirect($uri);
+ $response->setRedirect(302, $uri);
}
- public function validateAuthorizeRequest(OAuth2_RequestInterface $request)
+ public function validateAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
// Make sure a valid client id was supplied (we can not redirect because we were unable to verify the URI)
if (!$client_id = $request->query("client_id")) {
// We don't have a good URI to use
- $this->response = new OAuth2_Response_Error(400, 'invalid_client', "No client id supplied");
+ $response->setError(400, 'invalid_client', "No client id supplied");
return false;
}
// Get client details
if (!$clientData = $this->clientStorage->getClientDetails($client_id)) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_client', 'The client id supplied is invalid');
+ $response->setError(400, 'invalid_client', 'The client id supplied is invalid');
return false;
}
$clientData += array('redirect_uri' => null); // this should be set. We should create ClientData interface
if ($clientData === false) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_client', "Client id does not exist");
+ $response->setError(400, 'invalid_client', "Client id does not exist");
return false;
}
@@ -81,20 +77,20 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori
// @see http://tools.ietf.org/html/rfc6749#section-4.1.2.1
// @see http://tools.ietf.org/html/rfc6749#section-4.2.2.1
if (!($redirect_uri = $request->query('redirect_uri')) && !($redirect_uri = $clientData['redirect_uri'])) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_uri', 'No redirect URI was supplied or stored');
+ $response->setError(400, 'invalid_uri', 'No redirect URI was supplied or stored');
return false;
}
$parts = parse_url($redirect_uri);
if (isset($parts['fragment']) && $parts['fragment']) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_uri', 'The redirect URI must not contain a fragment');
+ $response->setError(400, 'invalid_uri', 'The redirect URI must not contain a fragment');
return false;
}
// Only need to validate if redirect_uri provided on input and clientData.
if ($clientData["redirect_uri"] && $redirect_uri && !$this->validateRedirectUri($redirect_uri, $clientData["redirect_uri"])) {
- $this->response = new OAuth2_Response_Error(400, 'redirect_uri_mismatch', 'The redirect URI provided is missing or does not match');
+ $response->setError(400, 'redirect_uri_mismatch', 'The redirect URI provided is missing or does not match');
return false;
}
@@ -108,54 +104,54 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori
// type and client_id are required
if (!$response_type || !in_array($response_type, array(self::RESPONSE_TYPE_AUTHORIZATION_CODE, self::RESPONSE_TYPE_ACCESS_TOKEN))) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'invalid_request', 'Invalid or missing response type', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'invalid_request', 'Invalid or missing response type', null);
return false;
}
if ($response_type == self::RESPONSE_TYPE_AUTHORIZATION_CODE) {
if (!isset($this->responseTypes['code'])) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'unsupported_response_type', 'authorization code grant type not supported', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'unsupported_response_type', 'authorization code grant type not supported', null);
return false;
}
if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'authorization_code')) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'unauthorized_client', 'The grant type is unauthorized for this client_id', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null);
return false;
}
if ($this->responseTypes['code']->enforceRedirect() && !$redirect_uri) {
- $this->response = new OAuth2_Response_Error(400, 'redirect_uri_mismatch', 'The redirect URI is mandatory and was not supplied.');
+ $response->setError(400, 'redirect_uri_mismatch', 'The redirect URI is mandatory and was not supplied.');
return false;
}
}
if ($response_type == self::RESPONSE_TYPE_ACCESS_TOKEN) {
if (!$this->config['allow_implicit']) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'unsupported_response_type', 'implicit grant type not supported', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'unsupported_response_type', 'implicit grant type not supported', null);
return false;
}
if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'implicit')) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'unauthorized_client', 'The grant type is unauthorized for this client_id', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null);
return false;
}
}
// Validate that the requested scope is supported
if (false === $scope) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'invalid_client', 'This application requires you specify a scope parameter', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'invalid_client', 'This application requires you specify a scope parameter', null);
return false;
}
if (!is_null($scope) && !$this->scopeUtil->scopeExists($scope, $client_id)) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'invalid_scope', 'An unsupported scope was requested', $state);
+ $response->setRedirect(302, $redirect_uri, $state, 'invalid_scope', 'An unsupported scope was requested', null);
return false;
}
// Validate state parameter exists (if configured to enforce this)
if ($this->config['enforce_state'] && !$state) {
- $this->response = new OAuth2_Response_Redirect($redirect_uri, 302, 'invalid_request', 'The state parameter is required');
+ $response->setRedirect(302, $redirect_uri, null, 'invalid_request', 'The state parameter is required');
return false;
}
// Return retrieved client details together with input
- return array_merge(array('scope' => $scope, 'state' => null), $clientData, $request->getAllQueryParameters(), array('redirect_uri' => $redirect_uri));
+ return array_merge(array('scope' => $scope, 'state' => $state), $clientData, $request->getAllQueryParameters(), array('redirect_uri' => $redirect_uri));
}
/**
@@ -199,19 +195,27 @@ class OAuth2_Controller_AuthorizeController implements OAuth2_Controller_Authori
/**
* Internal method for validating redirect URI supplied
+ *
* @param string $inputUri
+ * The submitted URI to be validated
+ *
* @param string $storedUri
+ * The allowed URIs to validate against. Can be a space-delimited string of URIs to
+ * allow for multiple URIs
*/
private function validateRedirectUri($inputUri, $storedUri)
{
if (!$inputUri || !$storedUri) {
return false; // if either one is missing, assume INVALID
}
- return strcasecmp(substr($inputUri, 0, strlen($storedUri)), $storedUri) === 0;
- }
- public function getResponse()
- {
- return $this->response;
+ $stored_uris = explode(' ', $storedUri);
+
+ foreach ($stored_uris as $stored) {
+ if (strcasecmp(substr($inputUri, 0, strlen($stored)), $stored) === 0) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/src/OAuth2/Controller/AuthorizeControllerInterface.php b/src/OAuth2/Controller/AuthorizeControllerInterface.php
index 88c8793..7d9ae68 100644
--- a/src/OAuth2/Controller/AuthorizeControllerInterface.php
+++ b/src/OAuth2/Controller/AuthorizeControllerInterface.php
@@ -9,14 +9,16 @@
* ex:
* > $user_id = $this->somehowDetermineUserId();
* > $is_authorized = $this->somehowDetermineUserAuthorization();
- * > $response = $authorizeController->handleAuthorizeRequest(
+ * > $response = new OAuth2_Response();
+ * > $authorizeController->handleAuthorizeRequest(
* > OAuth2_Request::createFromGlobals(),
+ * > $response,
* > $is_authorized,
* > $user_id);
* > $response->send();
*
*/
-interface OAuth2_Controller_AuthorizeControllerInterface extends OAuth2_Response_ProviderInterface
+interface OAuth2_Controller_AuthorizeControllerInterface
{
/**
* List of possible authentication response types.
@@ -30,7 +32,7 @@ interface OAuth2_Controller_AuthorizeControllerInterface extends OAuth2_Response
const RESPONSE_TYPE_AUTHORIZATION_CODE = 'code';
const RESPONSE_TYPE_ACCESS_TOKEN = 'token';
- public function handleAuthorizeRequest(OAuth2_RequestInterface $request, $is_authorized, $user_id = null);
+ public function handleAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $is_authorized, $user_id = null);
- public function validateAuthorizeRequest(OAuth2_RequestInterface $request);
+ public function validateAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response);
}
diff --git a/src/OAuth2/Controller/ResourceController.php b/src/OAuth2/Controller/ResourceController.php
index 4dd827d..017b2cf 100644
--- a/src/OAuth2/Controller/ResourceController.php
+++ b/src/OAuth2/Controller/ResourceController.php
@@ -1,7 +1,7 @@
<?php
/**
- * @see OAuth2_Controller_ResourceControllerInterface
+ * @see OAuth2_Controller_ResourceControllerInterface
*/
class OAuth2_Controller_ResourceController implements OAuth2_Controller_ResourceControllerInterface
{
@@ -26,52 +26,43 @@ class OAuth2_Controller_ResourceController implements OAuth2_Controller_Resource
$this->scopeUtil = $scopeUtil;
}
- public function verifyResourceRequest(OAuth2_RequestInterface $request, $scope = null)
+ public function verifyResourceRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $scope = null)
{
- $token_data = $this->getAccessTokenData($request, $scope);
+ $token = $this->getAccessTokenData($request, $response, $scope);
- return (bool) $token_data;
+ // Check scope, if provided
+ // If token doesn't have a scope, it's null/empty, or it's insufficient, then throw an error
+ if ($scope && (!isset($token["scope"]) || !$token["scope"] || !$this->scopeUtil->checkScope($scope, $token["scope"]))) {
+ $response->setError(401, 'insufficient_scope', 'The request requires higher privileges than provided by the access token');
+ $response->addHttpHeaders(array('WWW-Authenticate' => sprintf('%s, realm="%s", scope="%s"', $this->tokenType->getTokenType(), $this->config['www_realm'], $scope)));
+ return false;
+ }
+
+ return (bool) $token;
}
- public function getAccessTokenData(OAuth2_RequestInterface $request, $scope = null)
+ public function getAccessTokenData(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
// Get the token parameter
- $token_param = $this->tokenType->getAccessTokenParameter($request);
+ $token_param = $this->tokenType->getAccessTokenParameter($request, $response);
if (is_null($token_param)) {
- $this->response = $this->tokenType->getResponse();
return null;
}
// Get the stored token data (from the implementing subclass)
- if (!$token = $this->tokenStorage->getAccessToken($token_param)) {
- $this->response = new OAuth2_Response_AuthenticationError(401, 'invalid_grant', 'The access token provided is invalid', $this->tokenType->getTokenType(), $this->config['www_realm'], $scope);
- return null;
- }
-
// Check we have a well formed token
- if (!isset($token["expires"]) || !isset($token["client_id"])) {
- $this->response = new OAuth2_Response_AuthenticationError(401, 'invalid_grant', 'Malformed token (missing "expires" or "client_id")', $this->tokenType->getTokenType(), $this->config['www_realm'], $scope);
- return null;
- }
-
// Check token expiration (expires is a mandatory paramter)
- if (isset($token["expires"]) && time() > $token["expires"]) {
- $this->response = new OAuth2_Response_AuthenticationError(401, 'invalid_grant', 'The access token provided has expired', $this->tokenType->getTokenType(), $this->config['www_realm'], $scope);
- return null;
- }
-
- // Check scope, if provided
- // If token doesn't have a scope, it's null/empty, or it's insufficient, then throw an error
- if ($scope && (!isset($token["scope"]) || !$token["scope"] || !$this->scopeUtil->checkScope($scope, $token["scope"]))) {
- $this->response = new OAuth2_Response_AuthenticationError(401, 'insufficient_scope', 'The request requires higher privileges than provided by the access token', $this->tokenType->getTokenType(), $this->config['www_realm'], $scope);
- return null;
+ if (!$token = $this->tokenStorage->getAccessToken($token_param)) {
+ $response->setError(401, 'invalid_grant', 'The access token provided is invalid', $this->tokenType->getTokenType(), $this->config['www_realm']);
+ } else if (!isset($token["expires"]) || !isset($token["client_id"])) {
+ $response->setError(401, 'invalid_grant', 'Malformed token (missing "expires" or "client_id")', $this->tokenType->getTokenType(), $this->config['www_realm']);
+ } else if (isset($token["expires"]) && time() > $token["expires"]) {
+ $response->setError(401, 'invalid_grant', 'The access token provided has expired');
+ } else {
+ return $token;
}
- return $token;
- }
-
- public function getResponse()
- {
- return $this->response;
+ $response->addHttpHeaders(array('WWW-Authenticate' => sprintf('%s, realm="%"', $this->tokenType->getTokenType(), $this->config['www_realm'])));
+ return null;
}
}
diff --git a/src/OAuth2/Controller/ResourceControllerInterface.php b/src/OAuth2/Controller/ResourceControllerInterface.php
index f98f697..df87338 100644
--- a/src/OAuth2/Controller/ResourceControllerInterface.php
+++ b/src/OAuth2/Controller/ResourceControllerInterface.php
@@ -6,16 +6,16 @@
* contains a valid token.
*
* ex:
- * > if (!$resourceController->verifyResourceRequest(OAuth2_Request::createFromGlobals())) {
- * > $resourceController->getResponse()->send(); // authorization failed
+ * > if (!$resourceController->verifyResourceRequest(OAuth2_Request::createFromGlobals(), $response = new OAuth2_Response())) {
+ * > $response->send(); // authorization failed
* > die();
* > }
* > return json_encode($resource); // valid token! Send the stuff!
*
*/
-interface OAuth2_Controller_ResourceControllerInterface extends OAuth2_Response_ProviderInterface
+interface OAuth2_Controller_ResourceControllerInterface
{
- public function verifyResourceRequest(OAuth2_RequestInterface $request, $scope = null);
+ public function verifyResourceRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $scope = null);
- public function getAccessTokenData(OAuth2_RequestInterface $request);
+ public function getAccessTokenData(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response);
}
diff --git a/src/OAuth2/Controller/TokenController.php b/src/OAuth2/Controller/TokenController.php
index f1be5da..1652531 100644
--- a/src/OAuth2/Controller/TokenController.php
+++ b/src/OAuth2/Controller/TokenController.php
@@ -1,7 +1,7 @@
<?php
/**
- * @see OAuth2_Controller_TokenControllerInterface
+ * @see OAuth2_Controller_TokenControllerInterface
*/
class OAuth2_Controller_TokenController implements OAuth2_Controller_TokenControllerInterface
{
@@ -11,14 +11,14 @@ class OAuth2_Controller_TokenController implements OAuth2_Controller_TokenContro
private $grantTypes;
private $scopeUtil;
- public function __construct($clientAssertionType = null, OAuth2_ResponseType_AccessTokenInterface $accessToken, array $grantTypes = array(), OAuth2_ScopeInterface $scopeUtil = null)
+ public function __construct(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $grantTypes = array(), OAuth2_ClientAssertionTypeInterface $clientAssertionType = null, OAuth2_ScopeInterface $scopeUtil = null)
{
- if ($clientAssertionType instanceof OAuth2_Storage_ClientCredentialsInterface) {
- // this is for backwards compatibility
- $clientAssertionType = new OAuth2_ClientAssertionType_HttpBasic($clientAssertionType);
- }
- if (!is_null($clientAssertionType) && !$clientAssertionType instanceof OAuth2_ClientAssertionTypeInterface) {
- throw new LogicException('$clientAssertionType must be an instance of OAuth2_Storage_ClientCredentialsInterface, OAuth2_ClientAssertionTypeInterface, or null');
+ if (is_null($clientAssertionType)) {
+ foreach ($grantTypes as $grantType) {
+ if (!$grantType instanceof OAuth2_ClientAssertionTypeInterface) {
+ throw new InvalidArgumentException('You must supply an instance of OAuth2_ClientAssertionTypeInterface or only use grant types which implement OAuth2_ClientAssertionTypeInterface');
+ }
+ }
}
$this->clientAssertionType = $clientAssertionType;
$this->accessToken = $accessToken;
@@ -32,14 +32,15 @@ class OAuth2_Controller_TokenController implements OAuth2_Controller_TokenContro
$this->scopeUtil = $scopeUtil;
}
- public function handleTokenRequest(OAuth2_RequestInterface $request)
+ public function handleTokenRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- if ($token = $this->grantAccessToken($request)) {
+ if ($token = $this->grantAccessToken($request, $response)) {
// @see http://tools.ietf.org/html/rfc6749#section-5.1
// server MUST disable caching in headers when tokens are involved
- $this->response = new OAuth2_Response($token, 200, array('Cache-Control' => 'no-store', 'Pragma' => 'no-cache'));
+ $response->setStatusCode(200);
+ $response->addParameters($token);
+ $response->addHttpHeaders(array('Cache-Control' => 'no-store', 'Pragma' => 'no-cache'));
}
- return $this->response;
}
/**
@@ -61,67 +62,71 @@ class OAuth2_Controller_TokenController implements OAuth2_Controller_TokenContro
*
* @ingroup oauth2_section_4
*/
- public function grantAccessToken(OAuth2_RequestInterface $request)
+ public function grantAccessToken(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
if (strtolower($request->server('REQUEST_METHOD')) != 'post') {
- $this->response = new OAuth2_Response_Error(405, 'invalid_request', 'The request method must be POST when requesting an access token', 'http://tools.ietf.org/html/rfc6749#section-3.2');
- $this->response->setHttpHeader( 'Allow', 'POST' );
+ $response->setError(405, 'invalid_request', 'The request method must be POST when requesting an access token', '#section-3.2');
+ $response->addHttpHeaders(array('Allow' => 'POST'));
return null;
}
- // Determine grant type from request
- if (!$grantType = $request->request('grant_type')) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'The grant type was not specified in the request');
+ /* Determine grant type from request
+ * and validate the request for that grant type
+ */
+ if (!$grantTypeIdentifier = $request->request('grant_type')) {
+ $response->setError(400, 'invalid_request', 'The grant type was not specified in the request');
return null;
}
- if (!isset($this->grantTypes[$grantType])) {
+ if (!isset($this->grantTypes[$grantTypeIdentifier])) {
/* TODO: If this is an OAuth2 supported grant type that we have chosen not to implement, throw a 501 Not Implemented instead */
- $this->response = new OAuth2_Response_Error(400, 'unsupported_grant_type', sprintf('Grant type "%s" not supported', $grantType));
- return null;
- }
- $grantType = $this->grantTypes[$grantType];
-
- // Hack to see if clientAssertionType is part of the grant type
- // this should change, but right now changing it will break BC
- $clientAssertionType = $grantType instanceof OAuth2_ClientAssertionTypeInterface ? $grantType : $this->clientAssertionType;
-
- $clientData = $clientAssertionType->getClientDataFromRequest($request);
-
- if (!$clientData || !$clientAssertionType->validateClientData($clientData, $grantType->getQuerystringIdentifier())) {
- $this->response = $this->getObjectResponse($clientAssertionType, new OAuth2_Response_Error(400, 'invalid_request', 'Unable to verify client'));
- return null;
- }
-
- // validate the request for the token
- if (!$grantType->validateRequest($request)) {
- $this->response = $this->getObjectResponse($grantType, new OAuth2_Response_Error(400, 'invalid_request', sprintf('Invalid request for "%s" grant type', $grantType->getQuerystringIdentifier())));
- return null;
- }
-
- if (!$tokenData = $grantType->getTokenDataFromRequest($request)) {
- $this->response = $this->getObjectResponse($grantType, new OAuth2_Response_Error(400, 'invalid_grant', sprintf('Unable to retrieve token for "%s" grant type', $grantType->getQuerystringIdentifier())));
+ $response->setError(400, 'unsupported_grant_type', sprintf('Grant type "%s" not supported', $grantTypeIdentifier));
return null;
}
- if (!$grantType->validateTokenData($tokenData, $clientData)) {
- $this->response = $this->getObjectResponse($grantType, new OAuth2_Response_Error(400, 'invalid_grant', 'Token is no longer valid'));
+ $grantType = $this->grantTypes[$grantTypeIdentifier];
+ if (!$grantType->validateRequest($request, $response)) {
return null;
}
- if (!isset($tokenData["scope"])) {
- $tokenData["scope"] = $this->scopeUtil->getDefaultScope();
- }
-
- $scope = $this->scopeUtil->getScopeFromRequest($request);
- // Check scope, if provided
- if (!is_null($scope) && !$this->scopeUtil->checkScope($scope, $tokenData["scope"])) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_scope', 'An unsupported scope was requested.');
+ /* Retrieve the client information from the request
+ * ClientAssertionTypes allow for grant types which also assert the client data
+ * in which case ClientAssertion is handled in the validateRequest method
+ *
+ * @see OAuth2_GrantType_JWTBearer
+ * @see OAuth2_GrantType_ClientCredentials
+ */
+ if ($grantType instanceof OAuth2_ClientAssertionTypeInterface) {
+ $clientId = $grantType->getClientId();
+ } else {
+ if (!$this->clientAssertionType->validateRequest($request, $response)) {
+ return null;
+ }
+ $clientId = $this->clientAssertionType->getClientId();
+
+ // validate the Client ID (if applicable)
+ if (!is_null($storedClientId = $grantType->getClientId()) && $storedClientId != $clientId) {
+ $response->setError(400, 'invalid_grant', sprintf('%s doesn\'t exist or is invalid for the client', $grantTypeIdentifier));
+ return null;
+ }
+ }
+
+ /*
+ * Validate the scope of the token
+ * If the grant type returns a value for the scope,
+ * this value must be verified with the scope being requested
+ */
+ $availableScope = $grantType->getScope();
+ if (!$requestedScope = $this->scopeUtil->getScopeFromRequest($request)) {
+ $requestedScope = $availableScope ? $availableScope : $this->scopeUtil->getDefaultScope();
+ }
+
+ if (($requestedScope && !$this->scopeUtil->scopeExists($requestedScope, $clientId))
+ || ($availableScope && !$this->scopeUtil->checkScope($requestedScope, $availableScope))) {
+ $response->setError(400, 'invalid_scope', 'An unsupported scope was requested.');
return null;
}
- $tokenData['user_id'] = isset($tokenData['user_id']) ? $tokenData['user_id'] : null;
-
- return $grantType->createAccessToken($this->accessToken, $clientData, $tokenData);
+ return $grantType->createAccessToken($this->accessToken, $clientId, $grantType->getUserId(), $requestedScope);
}
/**
@@ -140,17 +145,4 @@ class OAuth2_Controller_TokenController implements OAuth2_Controller_TokenContro
$this->grantTypes[$identifier] = $grantType;
}
-
- public function getResponse()
- {
- return $this->response;
- }
-
- public function getObjectResponse($object, OAuth2_Response $defaultResponse = null)
- {
- if ($object instanceof OAuth2_Response_ProviderInterface && $response = $object->getResponse()) {
- return $response;
- }
- return $defaultResponse;
- }
}
diff --git a/src/OAuth2/Controller/TokenControllerInterface.php b/src/OAuth2/Controller/TokenControllerInterface.php
index 3af5ac5..4fe9624 100644
--- a/src/OAuth2/Controller/TokenControllerInterface.php
+++ b/src/OAuth2/Controller/TokenControllerInterface.php
@@ -6,11 +6,11 @@
* It also validates the client's credentials
*
* ex:
- * > $response = $tokenController->handleTokenRequest(OAuth2_Request::createFromGlobals());
+ * > $tokenController->handleTokenRequest(OAuth2_Request::createFromGlobals(), $response = new OAuth2_Response());
* > $response->send();
*
*/
-interface OAuth2_Controller_TokenControllerInterface extends OAuth2_Response_ProviderInterface
+interface OAuth2_Controller_TokenControllerInterface
{
/**
* handleTokenRequest
@@ -18,8 +18,11 @@ interface OAuth2_Controller_TokenControllerInterface extends OAuth2_Response_Pro
* @param $request
* OAuth2_RequestInterface - The current http request
*
- **/
- public function handleTokenRequest(OAuth2_RequestInterface $request);
+ * @param $response
+ * OAuth2_ResponseInterface - An instance of OAuth2_ResponseInterface to contain the response data
+ *
+ */
+ public function handleTokenRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response);
- public function grantAccessToken(OAuth2_RequestInterface $request);
+ public function grantAccessToken(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response);
}
diff --git a/src/OAuth2/Encryption/JWT.php b/src/OAuth2/Encryption/JWT.php
index 2398808..16d3102 100644
--- a/src/OAuth2/Encryption/JWT.php
+++ b/src/OAuth2/Encryption/JWT.php
@@ -1,9 +1,9 @@
<?php
+
/**
* @link https://github.com/F21/jwt
* @author F21
*/
-
class OAuth2_Encryption_JWT
{
public function encode($payload, $key, $algo = 'HS256')
@@ -33,23 +33,22 @@ class OAuth2_Encryption_JWT
list($headb64, $payloadb64, $cryptob64) = $tks;
- if (null === ($header = json_decode($this->urlsafeB64Decode($headb64)))){
+ if (null === ($header = json_decode($this->urlsafeB64Decode($headb64), true))) {
return false;
}
- if (null === $payload = json_decode($this->urlsafeB64Decode($payloadb64))){
+ if (null === $payload = json_decode($this->urlsafeB64Decode($payloadb64), true)) {
return false;
}
$sig = $this->urlsafeB64Decode($cryptob64);
if ($verify) {
-
- if (empty($header->alg)) {
+ if (!isset($header['alg'])) {
return false;
}
- if (!$this->verifySignature($sig, "$headb64.$payloadb64", $key, $header->alg)) {
+ if (!$this->verifySignature($sig, "$headb64.$payloadb64", $key, $header['alg'])) {
return false;
}
}
@@ -66,7 +65,7 @@ class OAuth2_Encryption_JWT
return $this->sign($input, $key, $algo) === $signature;
case 'RS256':
- return @openssl_verify($input, $signature, $key, 'sha256') === 1;
+ return openssl_verify($input, $signature, $key, 'sha256') === 1;
case 'RS384':
return @openssl_verify($input, $signature, $key, 'sha384') === 1;
@@ -82,7 +81,6 @@ class OAuth2_Encryption_JWT
private function sign($input, $key, $algo = 'HS256')
{
switch($algo){
-
case 'HS256':
return hash_hmac('sha256', $input, $key, true);
diff --git a/src/OAuth2/GrantType/AuthorizationCode.php b/src/OAuth2/GrantType/AuthorizationCode.php
index 6067c5a..55b8fba 100644
--- a/src/OAuth2/GrantType/AuthorizationCode.php
+++ b/src/OAuth2/GrantType/AuthorizationCode.php
@@ -1,13 +1,13 @@
<?php
/**
-*
-*/
-class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, OAuth2_Response_ProviderInterface
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
+class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface
{
private $storage;
- private $response;
- private $code;
+ private $authCode;
public function __construct(OAuth2_Storage_AuthorizationCodeInterface $storage)
{
@@ -19,65 +19,68 @@ class OAuth2_GrantType_AuthorizationCode implements OAuth2_GrantTypeInterface, O
return 'authorization_code';
}
- public function validateRequest($request)
+ public function validateRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
if (!$request->request('code')) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameter: "code" is required');
+ $response->setError(400, 'invalid_request', 'Missing parameter: "code" is required');
return false;
}
- return true;
- }
-
- public function getTokenDataFromRequest($request)
- {
- $this->code = $request->request('code');
- if (!$tokenData = $this->storage->getAuthorizationCode($this->code)) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Authorization code doesn't exist or is invalid for the client");
- return null;
+ $code = $request->request('code');
+ if (!$authCode = $this->storage->getAuthorizationCode($code)) {
+ $response->setError(400, 'invalid_grant', 'Authorization code doesn\'t exist or is invalid for the client');
+ return false;
}
/*
* 4.1.3 - ensure that the "redirect_uri" parameter is present if the "redirect_uri" parameter was included in the initial authorization request
* @uri - http://tools.ietf.org/html/rfc6749#section-4.1.3
*/
- if (isset($tokenData['redirect_uri']) && $tokenData['redirect_uri']) {
- if (!$request->request('redirect_uri') || urldecode($request->request('redirect_uri')) != $tokenData['redirect_uri']) {
- $this->response = new OAuth2_Response_Error(400, 'redirect_uri_mismatch', "The redirect URI is missing or do not match", "#section-4.1.3");
+ if (isset($authCode['redirect_uri']) && $authCode['redirect_uri']) {
+ if (!$request->request('redirect_uri') || urldecode($request->request('redirect_uri')) != $authCode['redirect_uri']) {
+ $response->setError(400, 'redirect_uri_mismatch', "The redirect URI is missing or do not match", "#section-4.1.3");
return false;
}
}
- return $tokenData;
- }
+ if (!isset($authCode['expires'])) {
+ throw new Exception('Storage must return authcode with a value for "expires"');
+ }
- public function validateTokenData($tokenData, array $clientData)
- {
- // Check the code exists
- if ($tokenData === null || $clientData['client_id'] != $tokenData['client_id']) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Authorization code doesn't exist or is invalid for the client");
+ if ($authCode["expires"] < time()) {
+ $response->setError(400, 'invalid_grant', "The authorization code has expired");
return false;
}
- if ($tokenData["expires"] < time()) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "The authorization code has expired");
- return false;
+ if (!isset($authCode['code'])) {
+ $authCode['code'] = $code; // used to expire the code after the access token is granted
}
- // Scope is validated in the client class
+ $this->authCode = $authCode;
+
return true;
}
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData)
+ public function getClientId()
{
- $this->storage->expireAuthorizationCode($this->code);
- $token = $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope']);
+ return $this->authCode['client_id'];
+ }
- return $token;
+ public function getScope()
+ {
+ return isset($this->authCode['scope']) ? $this->authCode['scope'] : null;
}
- public function getResponse()
+ public function getUserId()
{
- return $this->response;
+ return $this->authCode['user_id'];
+ }
+
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
+ {
+ $token = $accessToken->createAccessToken($client_id, $user_id, $scope);
+ $this->storage->expireAuthorizationCode($this->authCode['code']);
+
+ return $token;
}
}
diff --git a/src/OAuth2/GrantType/ClientCredentials.php b/src/OAuth2/GrantType/ClientCredentials.php
index d518414..ab37409 100644
--- a/src/OAuth2/GrantType/ClientCredentials.php
+++ b/src/OAuth2/GrantType/ClientCredentials.php
@@ -1,40 +1,33 @@
<?php
-class OAuth2_GrantType_ClientCredentials implements OAuth2_GrantTypeInterface
+/**
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
+class OAuth2_GrantType_ClientCredentials extends OAuth2_ClientAssertionType_HttpBasic implements OAuth2_GrantTypeInterface
{
public function getQuerystringIdentifier()
{
return 'client_credentials';
}
- public function validateRequest($request)
+ public function getScope()
{
- // This has been done in the server class
- return true;
+ return null;
}
- public function getTokenDataFromRequest($request)
+ public function getUserId()
{
- // the only piece to pull is the "scope" parameter
- $scope = $request->request('scope');
- return array(
- "scope" => $scope
- );
+ return null;
}
- public function validateTokenData($tokenData, array $clientData)
- {
- // Scope is validated in the client class
- return true;
- }
-
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData)
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
{
/*
* Client Credentials Grant does NOT include a refresh token
* @see http://tools.ietf.org/html/rfc6749#section-4.4.3
*/
$includeRefreshToken = false;
- return $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope'], $includeRefreshToken);
+ return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
}
}
diff --git a/src/OAuth2/GrantType/JWTBearer.php b/src/OAuth2/GrantType/JWTBearer.php
index 705856a..6ba2521 100644
--- a/src/OAuth2/GrantType/JWTBearer.php
+++ b/src/OAuth2/GrantType/JWTBearer.php
@@ -1,17 +1,19 @@
<?php
/**
-* The JWT bearer authorization grant implements JWT (JSON Web Tokens) as a grant type per the IETF draft.
-* @see http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-04#section-4
-*/
-class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Response_ProviderInterface, OAuth2_ClientAssertionTypeInterface
+ * The JWT bearer authorization grant implements JWT (JSON Web Tokens) as a grant type per the IETF draft.
+ *
+ * @see http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-04#section-4
+ *
+ * @author F21
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
+class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_ClientAssertionTypeInterface
{
private $storage;
- private $response;
- private $audience = null;
- private $jwt = null;
- private $undecodedJWT = null;
+ private $audience;
private $jwtUtil;
+ private $jwt;
/**
* Creates an instance of the JWT bearer grant type.
@@ -23,7 +25,7 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re
* @param OAuth2_Encryption_JWT OPTIONAL $jwtUtil
* The class used to decode, encode and verify JWTs.
*/
- public function __construct(OAuth2_Storage_JWTBearerInterface $storage, $audience, OAuth2_Encryption_JWT $jwtUtil = null)
+ public function __construct(OAuth2_Storage_JWTBearerInterface $storage, $audience, OAuth2_Encryption_JWT $jwtUtil = null)
{
$this->storage = $storage;
$this->audience = $audience;
@@ -37,7 +39,10 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re
/**
* Returns the grant_type get parameter to identify the grant type request as JWT bearer authorization grant.
- * @return The string identifier for grant_type.
+ *
+ * @return
+ * The string identifier for grant_type.
+ *
* @see OAuth2_GrantTypeInterface::getQuerystringIdentifier()
*/
public function getQuerystringIdentifier()
@@ -46,240 +51,137 @@ class OAuth2_GrantType_JWTBearer implements OAuth2_GrantTypeInterface, OAuth2_Re
}
/**
- * Validates the request by making share all GET parameters exists.
- * @return TRUE if the request is valid, otherwise FALSE.
- * @see OAuth2_GrantTypeInterface::validateRequest()
+ * Validates the data from the decoded JWT.
+ *
+ * @return
+ * TRUE if the JWT request is valid and can be decoded. Otherwise, FALSE is returned.
+ *
+ * @see OAuth2_GrantTypeInterface::getTokenData()
*/
- public function validateRequest($request)
+ public function validateRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
if (!$request->request("assertion")) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameters: "assertion" required');
-
- return false;
+ $response->setError(400, 'invalid_request', 'Missing parameters: "assertion" required');
+ return null;
}
- return true;
- }
-
- /**
- * Gets the data from the decoded JWT.
- * @return Array containing the token data if the JWT can be decoded. Otherwise, NULL is returned.
- * @see OAuth2_GrantTypeInterface::getTokenDataFromRequest()
- */
- public function getTokenDataFromRequest($request)
- {
-
if (!$request->request("assertion")) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameters: "assertion" required');
-
+ $response->setError(400, 'invalid_request', 'Missing parameters: "assertion" required');
return null;
}
- //Store the undecoded JWT for later use
- $this->undecodedJWT = $request->request('assertion');
+ // Store the undecoded JWT for later use
+ $undecodedJWT = $request->request('assertion');
- //Decode the JWT
+ // Decode the JWT
$jwt = $this->jwtUtil->decode($request->request('assertion'), null, false);
if (!$jwt) {
-
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', "JWT is malformed");
+ $response->setError(400, 'invalid_request', "JWT is malformed");
return null;
}
- $this->jwt = $jwt;
-
- $tokenData = array();
-
- $tokenData['scope'] = $this->getJWTParameter('scope');
- $tokenData['iss'] = $this->getJWTParameter('iss');
- $tokenData['sub'] = $this->getJWTParameter('sub');
- $tokenData['aud'] = $this->getJWTParameter('aud');
- $tokenData['exp'] = $this->getJWTParameter('exp');
- $tokenData['nbf'] = $this->getJWTParameter('nbf');
- $tokenData['iat'] = $this->getJWTParameter('iat');
- $tokenData['jti'] = $this->getJWTParameter('jti');
- $tokenData['typ'] = $this->getJWTParameter('typ');
-
- //Other token data in the claim
- foreach ($this->jwt as $key => $value) {
- if (!array_key_exists($key, $tokenData)) {
- $tokenData[$key] = $value;
- }
- }
-
- return $tokenData;
- }
-
-
- /**
- * Helper function to make it easier to return a JWT parameter.
- * @param string $parameter
- * The JWT parameter to get.
- * @param mixed $default
- * The value to return if the JWT parameter does not exist.
- * @return mixed
- * The JWT parameter.
- */
- private function getJWTParameter($parameter, $default = null)
- {
- return isset($this->jwt->$parameter) ? $this->jwt->$parameter : null;
- }
-
- /**
- * Return the data used to verify the request. For JWT bearer authorization grants, the 'iss' is synonymous to the 'client_id'.
- * The subject is 'sub' and the 'client_secret' is the key to decode the JWT.
- * @return array An array of the client data containing the client_id.
- * @see OAuth2_ClientAssertionTypeInterface::getClientDataFromRequest()
- */
- public function getClientDataFromRequest(OAuth2_RequestInterface $request)
- {
- $tokenData = $this->getTokenDataFromRequest($request);
-
- if (!$tokenData) {
+ // ensure these properties contain a value
+ // @todo: throw malformed error for missing properties
+ $jwt = array_merge(array(
+ 'scope' => null,
+ 'iss' => null,
+ 'sub' => null,
+ 'aud' => null,
+ 'exp' => null,
+ 'nbf' => null,
+ 'iat' => null,
+ 'jti' => null,
+ 'typ' => null,
+ ), $jwt);
+
+ if (!isset($jwt['iss'])) {
+ $response->setError(400, 'invalid_grant', "Invalid issuer (iss) provided");
return null;
}
- if (!isset($tokenData['iss'])) {
-
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Invalid issuer (iss) provided");
-
+ if (!isset($jwt['sub'])) {
+ $response->setError(400, 'invalid_grant', "Invalid subject (sub) provided");
return null;
}
- //Get the iss's public key (http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06#section-4.1.1)
- $key = $this->storage->getClientKey($tokenData['iss'], $tokenData['sub']);
-
- if (!$key) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Invalid issuer (iss) or subject (sub) provided");
-
+ if (!isset($jwt['exp'])) {
+ $response->setError(400, 'invalid_grant', "Expiration (exp) time must be present");
return null;
}
- return array('client_id' => $tokenData['iss'], 'subject' => $tokenData['sub'], 'client_secret' => $key);
- }
-
- /**
- * Validates the client data by checking if the client_id exists in storage. It also checks to see if the client_id is associated with a key to decode the JWT.
- * @see OAuth2_ClientAssertionTypeInterface::validateClientData()
- */
- public function validateClientData(array $clientData, $grantTypeIdentifier)
- {
-
- //Check that all array keys exist
- $diff = array_diff(array_keys($clientData), array('client_id', 'subject', 'client_secret'));
-
- if (!empty($diff)) {
-
- throw new DomainException('The clientData array is missing one or more of the following: client_id, subject, client_secret');
-
- return false;
- }
-
- foreach ($clientData as $key => $value) {
-
- if (in_array($key, array('client_id', 'client_secret'))) {
-
- if (!isset($value)) {
- throw new LogicException('client_id and client_secret in the clientData array may not be null');
-
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Validates the token data using the rules in the IETF draft.
- * @see http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-04#section-3
- * @see OAuth2_GrantTypeInterface::validateTokenData()
- */
- public function validateTokenData($tokenData, array $clientData)
- {
- // Note: Scope is validated in the client class
-
- //Check the expiry time
- $expiration = $tokenData['exp'];
-
- if (ctype_digit($expiration)) {
-
- if ($expiration <= time()) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "JWT has expired");
-
- return false;
+ // Check expiration
+ if (ctype_digit($jwt['exp'])) {
+ if ($jwt['exp'] <= time()) {
+ $response->setError(400, 'invalid_grant', "JWT has expired");
+ return null;
}
-
- } elseif (!$expiration) {
-
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Expiration (exp) time must be present");
-
- return false;
-
} else {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Expiration (exp) time must be a unix time stamp");
-
- return false;
+ $response->setError(400, 'invalid_grant', "Expiration (exp) time must be a unix time stamp");
+ return null;
}
-
- //Check the not before time
- if ($notBefore = $tokenData['nbf']) {
-
+ // Check the not before time
+ if ($notBefore = $jwt['nbf']) {
if (ctype_digit($notBefore)) {
-
if ($notBefore > time()) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time");
-
- return false;
+ $response->setError(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time");
+ return null;
}
-
} else {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Not Before (nbf) time must be a unix time stamp");
-
- return false;
+ $response->setError(400, 'invalid_grant', "Not Before (nbf) time must be a unix time stamp");
+ return null;
}
}
- //Check the audience if required to match
- $aud = $tokenData['aud'];
- if (!isset($aud) || ($aud != $this->audience)) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "Invalid audience (aud)");
-
- return false;
+ // Check the audience if required to match
+ if (!isset($jwt['aud']) || ($jwt['aud'] != $this->audience)) {
+ $response->setError(400, 'invalid_grant', "Invalid audience (aud)");
+ return null;
}
- //Verify the JWT
- $jwt = $this->jwtUtil->decode($this->undecodedJWT, $clientData['client_secret'], true);
-
- if (!$jwt) {
+ // Get the iss's public key
+ // @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06#section-4.1.1
+ if (!$key = $this->storage->getClientKey($jwt['iss'], $jwt['sub'])) {
+ $response->setError(400, 'invalid_grant', "Invalid issuer (iss) or subject (sub) provided");
+ return null;
+ }
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', "JWT failed signature verification");
+ // Verify the JWT
+ if (!$this->jwtUtil->decode($undecodedJWT, $key, true)) {
+ $response->setError(400, 'invalid_grant', "JWT failed signature verification");
return null;
}
+ $this->jwt = $jwt;
+
return true;
}
- /**
- * Creates an access token that is NOT associated with a refresh token.
- * If a subject (sub) the name of the user/account we are accessing data on behalf of.
- * @see OAuth2_GrantTypeInterface::createAccessToken()
- */
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData)
+ public function getClientId()
{
- $includeRefreshToken = false;
+ return $this->jwt['iss'];
+ }
+
+ public function getUserId()
+ {
+ return $this->jwt['sub'];
+ }
- return $accessToken->createAccessToken($clientData['client_id'], $tokenData['sub'], $tokenData['scope'], $includeRefreshToken);
+ public function getScope()
+ {
+ return $this->jwt['scope'];
}
/**
- * Returns the response of this grant type.
- * @see OAuth2_Response_ProviderInterface::getResponse()
+ * Creates an access token that is NOT associated with a refresh token.
+ * If a subject (sub) the name of the user/account we are accessing data on behalf of.
+ *
+ * @see OAuth2_GrantTypeInterface::createAccessToken()
*/
- public function getResponse()
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
{
- return $this->response;
+ $includeRefreshToken = false;
+ return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
}
}
diff --git a/src/OAuth2/GrantType/RefreshToken.php b/src/OAuth2/GrantType/RefreshToken.php
index 2f5336a..f98ed84 100644
--- a/src/OAuth2/GrantType/RefreshToken.php
+++ b/src/OAuth2/GrantType/RefreshToken.php
@@ -1,14 +1,14 @@
<?php
/**
-*
-*/
-class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2_Response_ProviderInterface
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
+class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface
{
private $storage;
- private $response;
private $config;
- private $oldRefreshToken;
+ private $refreshToken;
public function __construct(OAuth2_Storage_RefreshTokenInterface $storage, $config = array())
{
@@ -23,45 +23,45 @@ class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2
return 'refresh_token';
}
- public function validateRequest($request)
+ public function validateRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
if (!$request->request("refresh_token")) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameter: "refresh_token" is required');
- return false;
+ $response->setError(400, 'invalid_request', 'Missing parameter: "refresh_token" is required');
+ return null;
}
- return true;
- }
+ if (!$refreshToken = $this->storage->getRefreshToken($request->request("refresh_token"))) {
+ $response->setError(400, 'invalid_grant', 'Invalid refresh token');
+ return null;
+ }
- public function getTokenDataFromRequest($request)
- {
- if (!$stored = $this->storage->getRefreshToken($request->request("refresh_token"))) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Invalid refresh token');
- return false;
+ if ($refreshToken["expires"] < time()) {
+ $response->setError(400, 'invalid_grant', 'Refresh token has expired');
+ return null;
}
- return $stored;
+ // store the refresh token locally so we can delete it when a new refresh token is generated
+ $this->refreshToken = $refreshToken;
+
+ return true;
}
- public function validateTokenData($tokenData, array $clientData)
+ public function getClientId()
{
- if ($tokenData === null || $clientData['client_id'] != $tokenData["client_id"]) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Invalid refresh token');
- return false;
- }
-
- if ($tokenData["expires"] < time()) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Refresh token has expired');
- return false;
- }
+ return $this->refreshToken['client_id'];
+ }
- // store the refresh token locally so we can delete it when a new refresh token is generated
- $this->oldRefreshToken = $tokenData["refresh_token"];
+ public function getUserId()
+ {
+ return $this->refreshToken['user_id'];
+ }
- return true;
+ public function getScope()
+ {
+ return $this->refreshToken['scope'];
}
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData)
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
{
/*
* It is optional to force a new refresh token when a refresh token is used.
@@ -69,17 +69,12 @@ class OAuth2_GrantType_RefreshToken implements OAuth2_GrantTypeInterface, OAuth2
* @see http://tools.ietf.org/html/rfc6749#section-6
*/
$issueNewRefreshToken = $this->config['always_issue_new_refresh_token'];
- $token = $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope'], $issueNewRefreshToken);
+ $token = $accessToken->createAccessToken($client_id, $user_id, $scope, $issueNewRefreshToken);
if ($issueNewRefreshToken) {
- $this->storage->unsetRefreshToken($this->oldRefreshToken);
+ $this->storage->unsetRefreshToken($this->refreshToken['refresh_token']);
}
return $token;
}
-
- public function getResponse()
- {
- return $this->response;
- }
}
diff --git a/src/OAuth2/GrantType/UserCredentials.php b/src/OAuth2/GrantType/UserCredentials.php
index 0d4638e..c41e8db 100644
--- a/src/OAuth2/GrantType/UserCredentials.php
+++ b/src/OAuth2/GrantType/UserCredentials.php
@@ -1,12 +1,13 @@
<?php
/**
-*
-*/
-class OAuth2_GrantType_UserCredentials implements OAuth2_GrantTypeInterface, OAuth2_Response_ProviderInterface
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
+class OAuth2_GrantType_UserCredentials implements OAuth2_GrantTypeInterface
{
private $storage;
- private $response;
+ private $userInfo;
public function __construct(OAuth2_Storage_UserCredentialsInterface $storage)
{
@@ -18,47 +19,48 @@ class OAuth2_GrantType_UserCredentials implements OAuth2_GrantTypeInterface, OAu
return 'password';
}
- public function validateRequest($request)
+ public function validateRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
if (!$request->request("password") || !$request->request("username")) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Missing parameters: "username" and "password" required');
- return false;
+ $response->setError(400, 'invalid_request', 'Missing parameters: "username" and "password" required');
+ return null;
}
- return true;
- }
-
- public function getTokenDataFromRequest($request)
- {
if (!$this->storage->checkUserCredentials($request->request("username"), $request->request("password"))) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Invalid username and password combination');
- return false;
+ $response->setError(400, 'invalid_grant', 'Invalid username and password combination');
+ return null;
}
- $tokenData = $this->storage->getUserDetails($request->request("username"));
+ $userInfo = $this->storage->getUserDetails($request->request("username"));
- // tokenData can be an empty array
- if (false === $tokenData || is_null($tokenData)) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_grant', 'Unable to retrieve user information');
- return false;
+ // userInfo can be an empty array
+ if (false === $userInfo || is_null($userInfo)) {
+ $response->setError(400, 'invalid_grant', 'Unable to retrieve user information');
+ return null;
}
- return $tokenData;
+ $this->userInfo = $userInfo;
+
+ return true;
}
- public function validateTokenData($tokenData, array $clientData)
+ public function getClientId()
{
- // Scope is validated in the client class
- return true;
+ return null;
+ }
+
+ public function getUserId()
+ {
+ return $this->userInfo['user_id'];
}
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData)
+ public function getScope()
{
- return $accessToken->createAccessToken($clientData['client_id'], $tokenData['user_id'], $tokenData['scope']);
+ return isset($this->userInfo['scope']) ? $this->userInfo['scope'] : null;
}
- public function getResponse()
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
{
- return $this->response;
+ return $accessToken->createAccessToken($client_id, $user_id, $scope);
}
}
diff --git a/src/OAuth2/GrantTypeInterface.php b/src/OAuth2/GrantTypeInterface.php
index c029a1d..9bc2327 100644
--- a/src/OAuth2/GrantTypeInterface.php
+++ b/src/OAuth2/GrantTypeInterface.php
@@ -1,10 +1,13 @@
<?php
-interface OAuth2_GrantTypeInterface
+/**
+ * Interface for all OAuth2 Grant Types
+ *
+ * @see OAuth2_CompatibilityInterface
+ */
+interface OAuth2_GrantTypeInterface extends OAuth2_CompatibilityInterface
{
- public function getQuerystringIdentifier();
- public function validateRequest($request);
- public function getTokenDataFromRequest($request);
- public function validateTokenData($tokenData, array $clientData);
- public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, array $clientData, array $tokenData);
+ public function getUserId();
+ public function getScope();
+ public function createAccessToken(OAuth2_ResponseType_AccessTokenInterface $accessToken, $client_id, $user_id, $scope);
}
diff --git a/src/OAuth2/Request.php b/src/OAuth2/Request.php
index 8f2ca3e..063966d 100644
--- a/src/OAuth2/Request.php
+++ b/src/OAuth2/Request.php
@@ -1,10 +1,10 @@
<?php
/**
-* OAuth2_Request
-* This class is taken from the Symfony2 Framework and is part of the Symfony package.
-* See Symfony\Component\HttpFoundation\Request (https://github.com/symfony/symfony)
-*/
+ * OAuth2_Request
+ * This class is taken from the Symfony2 Framework and is part of the Symfony package.
+ * See Symfony\Component\HttpFoundation\Request (https://github.com/symfony/symfony)
+ */
class OAuth2_Request implements OAuth2_RequestInterface
{
public $attributes;
@@ -29,9 +29,9 @@ class OAuth2_Request implements OAuth2_RequestInterface
*
* @api
*/
- public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+ public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, array $headers = null)
{
- $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
+ $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content, $headers);
}
/**
@@ -49,7 +49,7 @@ class OAuth2_Request implements OAuth2_RequestInterface
*
* @api
*/
- public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
+ public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, array $headers = null)
{
$this->request = $request;
$this->query = $query;
@@ -57,7 +57,8 @@ class OAuth2_Request implements OAuth2_RequestInterface
$this->cookies = $cookies;
$this->files = $files;
$this->server = $server;
- $this->headers = $this->getHeadersFromServer($this->server);
+ $this->content = $content;
+ $this->headers = is_null($headers) ? $this->getHeadersFromServer($this->server) : $headers;
}
public function query($name, $default = null)
diff --git a/src/OAuth2/Response.php b/src/OAuth2/Response.php
index bc88f2e..4ef801a 100644
--- a/src/OAuth2/Response.php
+++ b/src/OAuth2/Response.php
@@ -1,9 +1,15 @@
<?php
/**
-*
-*/
-class OAuth2_Response
+ * Class to handle OAuth2 Responses in a graceful way. Use this interface
+ * to output the proper OAuth2 responses.
+ *
+ * @see OAuth2_ResponseInterface
+ *
+ * This class borrows heavily from the Symfony2 Framework and is part of the symfony package
+ * @see Symfony\Component\HttpFoundation\Request (https://github.com/symfony/symfony)
+ */
+class OAuth2_Response implements OAuth2_ResponseInterface
{
public $version;
protected $statusCode = 200;
@@ -119,10 +125,16 @@ class OAuth2_Response
return $this->parameters;
}
- public function setParameters($parameters)
+ public function setParameters(array $parameters)
{
$this->parameters = $parameters;
}
+
+ public function addParameters(array $parameters)
+ {
+ $this->parameters = array_merge($this->parameters, $parameters);
+ }
+
public function getParameter($name, $default = null)
{
return isset($this->parameters[$name]) ? $this->parameters[$name] : $default;
@@ -133,7 +145,7 @@ class OAuth2_Response
$this->parameters[$name] = $value;
}
- public function setHttpHeaders($httpHeaders)
+ public function setHttpHeaders(array $httpHeaders)
{
$this->httpHeaders = $httpHeaders;
}
@@ -143,6 +155,11 @@ class OAuth2_Response
$this->httpHeaders[$name] = $value;
}
+ public function addHttpHeaders(array $httpHeaders)
+ {
+ $this->httpHeaders = array_merge($this->httpHeaders, $httpHeaders);
+ }
+
public function getHttpHeaders()
{
return $this->httpHeaders;
@@ -193,7 +210,67 @@ class OAuth2_Response
echo $this->getResponseBody($format);
}
-// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+ public function setError($statusCode, $error, $errorDescription = null, $errorUri = null)
+ {
+ $parameters = array(
+ 'error' => $error,
+ 'error_description' => $errorDescription,
+ );
+
+ if (!is_null($errorUri)) {
+ if (strlen($errorUri) > 0 && $errorUri[0] == '#') {
+ // we are referencing an oauth bookmark (for brevity)
+ $errorUri = 'http://tools.ietf.org/html/rfc6749' . $errorUri;
+ }
+ $parameters['error_uri'] = $errorUri;
+ }
+
+ $httpHeaders = array(
+ 'Cache-Control' => 'no-store'
+ );
+
+ $this->setStatusCode($statusCode);
+ $this->addParameters($parameters);
+ $this->addHttpHeaders($httpHeaders);
+
+ if (!$this->isClientError() && !$this->isServerError()) {
+ throw new InvalidArgumentException(sprintf('The HTTP status code is not an error ("%s" given).', $statusCode));
+ }
+ }
+
+ public function setRedirect($statusCode = 302, $url, $state = null, $error = null, $errorDescription = null, $errorUri = null)
+ {
+ if (empty($url)) {
+ throw new InvalidArgumentException('Cannot redirect to an empty URL.');
+ }
+
+ $parameters = array();
+
+ if (!is_null($state)) {
+ $parameters['state'] = $state;
+ }
+
+ if (!is_null($error)) {
+ $this->setError(400, $error, $errorDescription, $errorUri);
+ }
+ $this->setStatusCode($statusCode);
+ $this->addParameters($parameters);
+
+ if (count($this->parameters) > 0) {
+ // add parameters to URL redirection
+ $parts = parse_url($url);
+ $sep = isset($parts['query']) && count($parts['query']) > 0 ? '&' : '?';
+ $url .= $sep . http_build_query($this->parameters);
+ }
+
+ $this->addHttpHeaders(array('Location' => $url));
+
+ if (!$this->isRedirection()) {
+ throw new InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $statusCode));
+ }
+ }
+
+ // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
/**
* @return Boolean
*
@@ -262,6 +339,7 @@ class OAuth2_Response
if (count($headers) == 0) {
return '';
}
+
$max = max(array_map('strlen', array_keys($headers))) + 1;
$content = '';
ksort($headers);
diff --git a/src/OAuth2/Response/AuthenticationError.php b/src/OAuth2/Response/AuthenticationError.php
deleted file mode 100644
index dadd4bf..0000000
--- a/src/OAuth2/Response/AuthenticationError.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
-*
-*/
-class OAuth2_Response_AuthenticationError extends OAuth2_Response_Error
-{
- public function __construct($statusCode, $error, $errorDescription, $tokenType, $realm, $scope = null)
- {
- parent::__construct($statusCode, $error, $errorDescription);
- $authHeader = sprintf('%s realm="%s"', $tokenType, $realm);
- if ($scope) {
- $authHeader = sprintf('%s, scope="%s"', $authHeader, $scope);
- }
- $this->setHttpHeader('WWW-Authenticate', $authHeader);
- }
-}
diff --git a/src/OAuth2/Response/Error.php b/src/OAuth2/Response/Error.php
deleted file mode 100644
index 305abd4..0000000
--- a/src/OAuth2/Response/Error.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-/**
-*
-*/
-class OAuth2_Response_Error extends OAuth2_Response
-{
- public function __construct($statusCode, $error, $errorDescription, $errorUri = null)
- {
- $parameters = array(
- 'error' => $error,
- 'error_description' => $errorDescription,
- );
-
- if (!is_null($errorUri)) {
- if (strlen($errorUri) > 0 && $errorUri[0] == '#') {
- // we are referencing an oauth bookmark (for brevity)
- $errorUri = 'http://tools.ietf.org/html/rfc6749' . $errorUri;
- }
- $parameters['error_uri'] = $errorUri;
- }
-
- $httpHeaders = array(
- 'Cache-Control' => 'no-store'
- );
-
- parent::__construct($parameters, $statusCode, $httpHeaders);
-
- if (!$this->isClientError() && !$this->isServerError()) {
- throw new InvalidArgumentException(sprintf('The HTTP status code is not an error ("%s" given).', $statusCode));
- }
- }
-
- public function getError()
- {
- return $this->parameters['error'];
- }
-
- public function getErrorDescription()
- {
- return $this->parameters['error_description'];
- }
-
- public function getErrorUri()
- {
- return $this->parameters['error_uri'];
- }
-}
diff --git a/src/OAuth2/Response/ProviderInterface.php b/src/OAuth2/Response/ProviderInterface.php
deleted file mode 100644
index 9b147c0..0000000
--- a/src/OAuth2/Response/ProviderInterface.php
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-interface OAuth2_Response_ProviderInterface
-{
- public function getResponse();
-}
diff --git a/src/OAuth2/Response/Redirect.php b/src/OAuth2/Response/Redirect.php
deleted file mode 100644
index 86ec291..0000000
--- a/src/OAuth2/Response/Redirect.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
-*
-*/
-class OAuth2_Response_Redirect extends OAuth2_Response
-{
- public function __construct($url, $statusCode = 302, $error = null, $errorDescription = null, $state = null)
- {
- if (empty($url)) {
- throw new InvalidArgumentException('Cannot redirect to an empty URL.');
- }
-
- $query = array();
-
- if (!is_null($error)) {
- $query['error'] = $error;
- }
-
- if (!is_null($errorDescription)) {
- $query['error_description'] = $errorDescription;
- }
-
- if (!is_null($state)) {
- $query['state'] = $state;
- }
-
- if (count($query) > 0) {
- $parts = parse_url($url);
- $sep = isset($parts['query']) && count($parts['query']) > 0 ? '&' : '?';
- $url = $url . $sep . http_build_query($query);
- }
-
- $httpHeaders = array(
- 'Location' => $url,
- );
-
- parent::__construct(array(), $statusCode, $httpHeaders);
-
- if (!$this->isRedirection()) {
- throw new InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $statusCode));
- }
- }
-}
diff --git a/src/OAuth2/ResponseInterface.php b/src/OAuth2/ResponseInterface.php
new file mode 100644
index 0000000..4aa32cd
--- /dev/null
+++ b/src/OAuth2/ResponseInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * Interface which represents an object response. Meant to handle and display the proper OAuth2 Responses
+ * for errors and successes
+ *
+ * @see OAuth2_Response
+ */
+interface OAuth2_ResponseInterface
+{
+ public function addParameters(array $parameters);
+
+ public function addHttpHeaders(array $httpHeaders);
+
+ public function setStatusCode($statusCode);
+
+ public function setError($statusCode, $name, $description = null, $uri = null);
+
+ public function setRedirect($statusCode = 302, $url, $state = null, $error = null, $errorDescription = null, $errorUri = null);
+} \ No newline at end of file
diff --git a/src/OAuth2/ResponseType/AccessToken.php b/src/OAuth2/ResponseType/AccessToken.php
index 30beaf6..b92ead9 100644
--- a/src/OAuth2/ResponseType/AccessToken.php
+++ b/src/OAuth2/ResponseType/AccessToken.php
@@ -1,8 +1,9 @@
<?php
/**
-*
-*/
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
class OAuth2_ResponseType_AccessToken implements OAuth2_ResponseType_AccessTokenInterface
{
private $tokenStorage;
@@ -20,7 +21,6 @@ class OAuth2_ResponseType_AccessToken implements OAuth2_ResponseType_AccessToken
), $config);
}
- // same params as above
public function getAuthorizeResponse($params, $user_id = null)
{
// build the URL to redirect to
@@ -44,17 +44,16 @@ class OAuth2_ResponseType_AccessToken implements OAuth2_ResponseType_AccessToken
}
/**
- * Handle the creation of access token, also issue refresh token if support.
- *
- * This belongs in a separate factory, but to keep it simple, I'm just
- * keeping it here.
+ * Handle the creation of access token, also issue refresh token if supported / desirable.
*
* @param $client_id
* Client identifier related to the access token.
+ * @param $user_id
+ * User ID associated with the access token
* @param $scope
* (optional) Scopes to be stored in space-separated string.
- * @param bool $excludeRefreshToken
- * If true, the refresh_token will be omitted from the response
+ * @param bool $includeRefreshToken
+ * If true, a new refresh_token will be added to the response
*
* @see http://tools.ietf.org/html/rfc6749#section-5
* @ingroup oauth2_section_5
diff --git a/src/OAuth2/ResponseType/AccessTokenInterface.php b/src/OAuth2/ResponseType/AccessTokenInterface.php
index d362893..19180b9 100644
--- a/src/OAuth2/ResponseType/AccessTokenInterface.php
+++ b/src/OAuth2/ResponseType/AccessTokenInterface.php
@@ -1,6 +1,25 @@
<?php
+/**
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
interface OAuth2_ResponseType_AccessTokenInterface extends OAuth2_ResponseTypeInterface
{
+ /**
+ * Handle the creation of access token, also issue refresh token if supported / desirable.
+ *
+ * @param $client_id
+ * Client identifier related to the access token.
+ * @param $user_id
+ * User ID associated with the access token
+ * @param $scope
+ * (optional) Scopes to be stored in space-separated string.
+ * @param bool $includeRefreshToken
+ * If true, a new refresh_token will be added to the response
+ *
+ * @see http://tools.ietf.org/html/rfc6749#section-5
+ * @ingroup oauth2_section_5
+ */
public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true);
}
diff --git a/src/OAuth2/ResponseType/AuthorizationCode.php b/src/OAuth2/ResponseType/AuthorizationCode.php
index d8a2626..84f2eeb 100644
--- a/src/OAuth2/ResponseType/AuthorizationCode.php
+++ b/src/OAuth2/ResponseType/AuthorizationCode.php
@@ -1,8 +1,9 @@
<?php
/**
-*
-*/
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
class OAuth2_ResponseType_AuthorizationCode implements OAuth2_ResponseType_AuthorizationCodeInterface
{
private $storage;
@@ -17,7 +18,7 @@ class OAuth2_ResponseType_AuthorizationCode implements OAuth2_ResponseType_Autho
), $config);
}
- // same params as above
+
public function getAuthorizeResponse($params, $user_id = null)
{
// build the URL to redirect to
@@ -35,19 +36,19 @@ class OAuth2_ResponseType_AuthorizationCode implements OAuth2_ResponseType_Autho
}
/**
- * Handle the creation of auth code.
- *
- * This belongs in a separate factory, but to keep it simple, I'm just
- * keeping it here.
+ * Handle the creation of the authorization code.
*
* @param $client_id
- * Client identifier related to the access token.
+ * Client identifier related to the authorization code
+ * @param $user_id
+ * User ID associated with the authorization code
* @param $redirect_uri
* An absolute URI to which the authorization server will redirect the
* user-agent to when the end-user authorization step is completed.
* @param $scope
* (optional) Scopes to be stored in space-separated string.
*
+ * @see http://tools.ietf.org/html/rfc6749#section-4
* @ingroup oauth2_section_4
*/
public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null)
@@ -57,6 +58,10 @@ class OAuth2_ResponseType_AuthorizationCode implements OAuth2_ResponseType_Autho
return $code;
}
+ /**
+ * @return
+ * TRUE if the grant type requires a redirect_uri, FALSE if not
+ */
public function enforceRedirect()
{
return $this->config['enforce_redirect'];
diff --git a/src/OAuth2/ResponseType/AuthorizationCodeInterface.php b/src/OAuth2/ResponseType/AuthorizationCodeInterface.php
index f139ae3..6a791b3 100644
--- a/src/OAuth2/ResponseType/AuthorizationCodeInterface.php
+++ b/src/OAuth2/ResponseType/AuthorizationCodeInterface.php
@@ -1,5 +1,9 @@
<?php
+/**
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
+ */
interface OAuth2_ResponseType_AuthorizationCodeInterface extends OAuth2_ResponseTypeInterface
{
/**
@@ -8,5 +12,21 @@ interface OAuth2_ResponseType_AuthorizationCodeInterface extends OAuth2_Response
*/
public function enforceRedirect();
+ /**
+ * Handle the creation of the authorization code.
+ *
+ * @param $client_id
+ * Client identifier related to the authorization code
+ * @param $user_id
+ * User ID associated with the authorization code
+ * @param $redirect_uri
+ * An absolute URI to which the authorization server will redirect the
+ * user-agent to when the end-user authorization step is completed.
+ * @param $scope
+ * (optional) Scopes to be stored in space-separated string.
+ *
+ * @see http://tools.ietf.org/html/rfc6749#section-4
+ * @ingroup oauth2_section_4
+ */
public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null);
}
diff --git a/src/OAuth2/ScopeInterface.php b/src/OAuth2/ScopeInterface.php
index 63fb906..bab9ac6 100644
--- a/src/OAuth2/ScopeInterface.php
+++ b/src/OAuth2/ScopeInterface.php
@@ -1,8 +1,10 @@
<?php
/**
-* Class to handle scope implementation logic
-*/
+ * Class to handle scope implementation logic
+ *
+ * @see OAuth2_Storage_ScopeInterface
+ */
interface OAuth2_ScopeInterface extends OAuth2_Storage_ScopeInterface
{
/**
diff --git a/src/OAuth2/Server.php b/src/OAuth2/Server.php
index be115b7..c730607 100644
--- a/src/OAuth2/Server.php
+++ b/src/OAuth2/Server.php
@@ -1,8 +1,9 @@
<?php
/**
-* Service class for OAuth
-* This class serves only to wrap the other Controller classes
+* Server class for OAuth2
+* This class serves as a convience class which wraps the other Controller classes
+*
* @see OAuth2_Controller_ResourceController
* @see OAuth2_Controller_AuthorizeController
* @see OAuth2_Controller_TokenController
@@ -16,15 +17,15 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
protected $storages;
// servers
- protected $resourceController;
protected $authorizeController;
protected $tokenController;
+ protected $resourceController;
// config classes
protected $responseTypes;
protected $grantTypes;
- protected $accessTokenResponseType;
protected $scopeUtil;
+ protected $clientAssertionType;
protected $storageMap = array(
'access_token' => 'OAuth2_Storage_AccessTokenInterface',
@@ -34,6 +35,7 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
'refresh_token' => 'OAuth2_Storage_RefreshTokenInterface',
'user_credentials' => 'OAuth2_Storage_UserCredentialsInterface',
'jwt_bearer' => 'OAuth2_Storage_JWTBearerInterface',
+ 'scope' => 'OAuth2_Storage_ScopeInterface',
);
protected $responseTypeMap = array(
'token' => 'OAuth2_ResponseType_AccessTokenInterface',
@@ -55,8 +57,14 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
* Response types to use. array keys should be "code" and and "token" for
* Access Token and Authorization Code response types
*
- * @param OAuth2_ResponseType_AccessTokenInterface $accessTokenResponseType
- * Response type to use for access token
+ * @param OAuth2_TokenTypeInterface $tokenType
+ * The token type object to use. Valid token types are "bearer" and "mac"
+ *
+ * @param OAuth2_ScopeInterface $scopeUtil
+ * The scope utility class to use to validate scope
+ *
+ * @param OAuth2_ClientAssertionTypeInterface $clientAssertionType
+ * The method in which to verify the client identity. Default is HttpBasic
*
* @return
* TRUE if everything in required scope is contained in available scope,
@@ -66,7 +74,7 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
*
* @ingroup oauth2_section_7
*/
- public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), OAuth2_ResponseType_AccessTokenInterface $accessTokenResponseType = null, OAuth2_ScopeInterface $scopeUtil = null)
+ public function __construct($storage = array(), array $config = array(), array $grantTypes = array(), array $responseTypes = array(), OAuth2_TokenTypeInterface $tokenType = null, OAuth2_ScopeInterface $scopeUtil = null, OAuth2_ClientAssertionTypeInterface $clientAssertionType = null)
{
$storage = is_array($storage) ? $storage : array($storage);
$this->storages = array();
@@ -76,7 +84,6 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
// merge all config values. These get passed to our controller objects
$this->config = array_merge(array(
- 'token_type' => 'bearer',
'access_lifetime' => 3600,
'www_realm' => 'Service',
'token_param_name' => 'access_token',
@@ -85,51 +92,21 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
'allow_implicit' => false,
), $config);
- foreach ($responseTypes as $key => $responseType) {
- $this->addResponseType($responseType, $key);
- }
foreach ($grantTypes as $key => $grantType) {
$this->addGrantType($grantType, $key);
}
- $this->accessTokenResponseType = $accessTokenResponseType;
- $this->scopeUtil = $scopeUtil;
- }
-
- public function getResourceController()
- {
- if (is_null($this->resourceController)) {
- if (is_null($this->config['token_type'])) {
- $this->config['token_type'] = 'bearer';
- }
- $tokenType = null;
- if ($this->config['token_type'] == 'bearer') {
- $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_param_name token_bearer_header_name')));
- $tokenType = new OAuth2_TokenType_Bearer($config);
- } elseif ($this->config['token_type'] == 'mac') {
- $tokenType = new OAuth2_TokenType_MAC();
- } else {
- throw new LogicException('unrecognized token type: '.$this->config['token_type']);
- }
- if (!isset($this->storages['access_token'])) {
- throw new LogicException("You must supply a storage object implementing OAuth2_Storage_AccessTokenInterface to use the access server");
- }
- $config = array_intersect_key($this->config, array('www_realm' => ''));
- $this->resourceController = new OAuth2_Controller_ResourceController($tokenType, $this->storages['access_token'], $config, $this->scopeUtil);
+ foreach ($responseTypes as $key => $responseType) {
+ $this->addResponseType($responseType, $key);
}
- return $this->resourceController;
+ $this->tokenType = $tokenType;
+ $this->scopeUtil = $scopeUtil;
+ $this->clientAssertionType = $clientAssertionType;
}
public function getAuthorizeController()
{
if (is_null($this->authorizeController)) {
- if (!isset($this->storages['client'])) {
- throw new LogicException("You must supply a storage object implementing OAuth2_Storage_ClientInterface to use the authorize server");
- }
- if (0 == count($this->responseTypes)) {
- $this->responseTypes = $this->getDefaultResponseTypes();
- }
- $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_implicit enforce_state')));
- $this->authorizeController = new OAuth2_Controller_AuthorizeController($this->storages['client'], $this->responseTypes, $config, $this->scopeUtil);
+ $this->authorizeController = $this->createDefaultAuthorizeController();
}
return $this->authorizeController;
}
@@ -137,81 +114,41 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
public function getTokenController()
{
if (is_null($this->tokenController)) {
- if (!isset($this->storages['client_credentials'])) {
- throw new LogicException("You must supply a storage object implementing OAuth2_Storage_ClientCredentialsInterface to use the grant server");
- }
- if (is_null($this->accessTokenResponseType)) {
- if (isset($this->responseTypes['access_token'])) {
- $this->accessTokenResponseType = $this->responseTypes['access_token'];
- } else {
- if (!isset($this->storages['access_token'])) {
- throw new LogicException("You must supply a storage object implementing OAuth2_Storage_AccessTokenInterface to use the grant server");
- }
- $refreshStorage = null;
- if (isset($this->storages['refresh_token'])) {
- $refreshStorage = $this->storages['refresh_token'];
- }
- $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_type access_lifetime refresh_token_lifetime')));
- $this->accessTokenResponseType = new OAuth2_ResponseType_AccessToken($this->storages['access_token'], $refreshStorage, $config);
- }
- }
- if (0 == count($this->grantTypes)) {
- $this->grantTypes = $this->getDefaultGrantTypes();
- }
- $this->tokenController = new OAuth2_Controller_TokenController($this->storages['client_credentials'], $this->accessTokenResponseType, $this->grantTypes, $this->scopeUtil);
+ $this->tokenController = $this->createDefaultTokenController();
}
return $this->tokenController;
}
- protected function getDefaultResponseTypes()
+ public function getResourceController()
{
- $responseTypes = array();
- if (isset($this->storages['access_token'])) {
- $refreshStorage = null;
- if (isset($this->storages['refresh_token'])) {
- $refreshStorage = $this->storages['refresh_token'];
- }
- $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_type access_lifetime refresh_token_lifetime')));
- $responseTypes['token'] = new OAuth2_ResponseType_AccessToken($this->storages['access_token'], $refreshStorage, $config);
- }
-
- if (isset($this->storages['authorization_code'])) {
- $config = array_intersect_key($this->config, array_flip(explode(' ', 'enforce_redirect auth_code_lifetime')));
- $responseTypes['code'] = new OAuth2_ResponseType_AuthorizationCode($this->storages['authorization_code'], $config);
- }
-
- if (count($responseTypes) == 0) {
- throw new LogicException("You must supply an array of response_types in the constructor or implement a OAuth2_Storage_AccessTokenInterface or OAuth2_Storage_AuthorizationCodeInterface storage object");
+ if (is_null($this->resourceController)) {
+ $this->resourceController = $this->createDefaultResourceController();
}
-
- return $responseTypes;
+ return $this->resourceController;
}
- protected function getDefaultGrantTypes()
+ /**
+ * every getter deserves a setter
+ */
+ public function setAuthorizeController(OAuth2_Controller_AuthorizeControllerInterface $authorizeController)
{
- $grantTypes = array();
-
- if (isset($this->storages['user_credentials'])) {
- $grantTypes['password'] = new OAuth2_GrantType_UserCredentials($this->storages['user_credentials']);
- }
-
- if (isset($this->storages['client_credentials'])) {
- $grantTypes['client_credentials'] = new OAuth2_GrantType_ClientCredentials($this->storages['client_credentials']);
- }
-
- if (isset($this->storages['refresh_token'])) {
- $grantTypes['refresh_token'] = new OAuth2_GrantType_RefreshToken($this->storages['refresh_token']);
- }
-
- if (isset($this->storages['authorization_code'])) {
- $grantTypes['authorization_code'] = new OAuth2_GrantType_AuthorizationCode($this->storages['authorization_code']);
- }
+ $this->authorizeController = $authorizeController;
+ }
- if (count($grantTypes) == 0) {
- throw new LogicException("Unable to build default grant types - You must supply an array of grant_types in the constructor");
- }
+ /**
+ * every getter deserves a setter
+ */
+ public function setTokenController(OAuth2_Controller_TokenControllerInterface $tokenController)
+ {
+ $this->tokenController = $tokenController;
+ }
- return $grantTypes;
+ /**
+ * every getter deserves a setter
+ */
+ public function setResourceController(OAuth2_Controller_ResourceControllerInterface $resourceController)
+ {
+ $this->resourceController = $resourceController;
}
/**
@@ -234,24 +171,17 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
*
* @ingroup oauth2_section_4
*/
- public function handleTokenRequest(OAuth2_RequestInterface $request)
- {
- $value = $this->getTokenController()->handleTokenRequest($request);
- $this->response = $this->tokenController->getResponse();
- return $value;
- }
-
- public function grantAccessToken(OAuth2_RequestInterface $request)
+ public function handleTokenRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- $value = $this->getTokenController()->grantAccessToken($request);
- $this->response = $this->tokenController->getResponse();
- return $value;
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $this->getTokenController()->handleTokenRequest($request, $this->response);
+ return $this->response;
}
- public function getClientCredentials(OAuth2_RequestInterface $request)
+ public function grantAccessToken(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- $value = $this->getTokenController()->getClientCredentials($request);
- $this->response = $this->tokenController->getResponse();
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $value = $this->getTokenController()->grantAccessToken($request, $this->response);
return $value;
}
@@ -285,11 +215,11 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
*
* @ingroup oauth2_section_4
*/
- public function handleAuthorizeRequest(OAuth2_RequestInterface $request, $is_authorized, $user_id = null)
+ public function handleAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $is_authorized, $user_id = null)
{
- $value = $this->getAuthorizeController()->handleAuthorizeRequest($request, $is_authorized, $user_id);
- $this->response = $this->authorizeController->getResponse();
- return $value;
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $this->getAuthorizeController()->handleAuthorizeRequest($request, $this->response, $is_authorized, $user_id);
+ return $this->response;
}
/**
@@ -311,24 +241,24 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
*
* @ingroup oauth2_section_3
*/
- public function validateAuthorizeRequest(OAuth2_RequestInterface $request)
+ public function validateAuthorizeRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- $value = $this->getAuthorizeController()->validateAuthorizeRequest($request);
- $this->response = $this->authorizeController->getResponse();
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $value = $this->getAuthorizeController()->validateAuthorizeRequest($request, $this->response);
return $value;
}
- public function verifyResourceRequest(OAuth2_RequestInterface $request, $scope = null)
+ public function verifyResourceRequest(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response, $scope = null)
{
- $value = $this->getResourceController()->verifyResourceRequest($request, $scope);
- $this->response = $this->resourceController->getResponse();
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $value = $this->getResourceController()->verifyResourceRequest($request, $this->response, $scope);
return $value;
}
- public function getAccessTokenData(OAuth2_RequestInterface $request, $scope = null)
+ public function getAccessTokenData(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
- $value = $this->getResourceController()->getAccessTokenData($request, $scope);
- $this->response = $this->resourceController->getResponse();
+ $this->response = is_null($response) ? new OAuth2_Response() : $response;
+ $value = $this->getResourceController()->getAccessTokenData($request, $this->response);
return $value;
}
@@ -346,6 +276,16 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
}
}
+ /**
+ * Set a storage object for the server
+ *
+ * @param $storage
+ * An object implementing one of the Storage interfaces
+ *
+ * @param $key
+ * If null, the storage is set to the key of each storage interface it implements
+ * @see storageMap
+ */
public function addStorage($storage, $key = null)
{
// if explicitly set to a valid key, do not "magically" set below
@@ -358,9 +298,6 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
throw new InvalidArgumentException(sprintf('unknown storage key "%s", must be one of [%s]', $key, implode(', ', array_keys($this->storageMap))));
} else {
$set = false;
- // set a storage object to each key for the interface it represents
- // this means if an object represents more than one storage type, it will be referenced by multiple storage keys
- // ex: OAuth2_Storage_Pdo will be set for all the $storageMap keys
foreach ($this->storageMap as $type => $interface) {
if ($storage instanceof $interface) {
$this->storages[$type] = $storage;
@@ -398,11 +335,140 @@ class OAuth2_Server implements OAuth2_Controller_ResourceControllerInterface,
}
}
+ public function getScopeUtil()
+ {
+ if (!$this->scopeUtil) {
+ $storage = isset($this->storages['scope']) ? $this->storages['scope'] : null;
+ $this->scopeUtil = new OAuth2_Scope($storage);
+ }
+ return $this->scopeUtil;
+ }
+
+ /**
+ * every getter deserves a setter
+ */
public function setScopeUtil($scopeUtil)
{
$this->scopeUtil = $scopeUtil;
}
+ protected function createDefaultAuthorizeController()
+ {
+ if (!isset($this->storages['client'])) {
+ throw new LogicException("You must supply a storage object implementing OAuth2_Storage_ClientInterface to use the authorize server");
+ }
+ if (0 == count($this->responseTypes)) {
+ $this->responseTypes = $this->getDefaultResponseTypes();
+ }
+ $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_implicit enforce_state')));
+ return new OAuth2_Controller_AuthorizeController($this->storages['client'], $this->responseTypes, $config, $this->getScopeUtil());
+ }
+
+ protected function createDefaultTokenController()
+ {
+ if (0 == count($this->grantTypes)) {
+ $this->grantTypes = $this->getDefaultGrantTypes();
+ }
+
+ if (is_null($this->clientAssertionType)) {
+ // see if HttpBasic assertion type is requred. If so, then create it from storage classes.
+ foreach ($this->grantTypes as $grantType) {
+ if (!$grantType instanceof OAuth2_ClientAssertionTypeInterface) {
+ if (!isset($this->storages['client_credentials'])) {
+ throw new LogicException("You must supply a storage object implementing OAuth2_Storage_ClientCredentialsInterface to use the token server");
+ }
+ $this->clientAssertionType = new OAuth2_ClientAssertionType_HttpBasic($this->storages['client_credentials']);
+ break;
+ }
+ }
+ }
+
+ return new OAuth2_Controller_TokenController($this->getAccessTokenResponseType(), $this->grantTypes, $this->clientAssertionType, $this->getScopeUtil());
+ }
+
+ protected function createDefaultResourceController()
+ {
+ if (!isset($this->storages['access_token'])) {
+ throw new LogicException("You must supply a storage object implementing OAuth2_Storage_AccessTokenInterface to use the resource server");
+ }
+ if (!$this->tokenType) {
+ $this->tokenType = $this->getDefaultTokenType();
+ }
+ $config = array_intersect_key($this->config, array('www_realm' => ''));
+ return new OAuth2_Controller_ResourceController($this->tokenType, $this->storages['access_token'], $config, $this->getScopeUtil());
+ }
+
+ protected function getDefaultTokenType()
+ {
+ $config = array_intersect_key($this->config, array_flip(explode(' ', 'token_param_name token_bearer_header_name')));
+ return new OAuth2_TokenType_Bearer($config);
+ }
+
+ protected function getDefaultResponseTypes()
+ {
+ $responseTypes = array();
+
+ if (isset($this->storages['access_token'])) {
+ $responseTypes['token'] = $this->getAccessTokenResponseType();
+ }
+
+ if (isset($this->storages['authorization_code'])) {
+ $config = array_intersect_key($this->config, array_flip(explode(' ', 'enforce_redirect auth_code_lifetime')));
+ $responseTypes['code'] = new OAuth2_ResponseType_AuthorizationCode($this->storages['authorization_code'], $config);
+ }
+
+ if (count($responseTypes) == 0) {
+ throw new LogicException("You must supply an array of response_types in the constructor or implement a OAuth2_Storage_AccessTokenInterface or OAuth2_Storage_AuthorizationCodeInterface storage object");
+ }
+
+ return $responseTypes;
+ }
+
+ protected function getDefaultGrantTypes()
+ {
+ $grantTypes = array();
+
+ if (isset($this->storages['user_credentials'])) {
+ $grantTypes['password'] = new OAuth2_GrantType_UserCredentials($this->storages['user_credentials']);
+ }
+
+ if (isset($this->storages['client_credentials'])) {
+ $grantTypes['client_credentials'] = new OAuth2_GrantType_ClientCredentials($this->storages['client_credentials']);
+ }
+
+ if (isset($this->storages['refresh_token'])) {
+ $grantTypes['refresh_token'] = new OAuth2_GrantType_RefreshToken($this->storages['refresh_token']);
+ }
+
+ if (isset($this->storages['authorization_code'])) {
+ $grantTypes['authorization_code'] = new OAuth2_GrantType_AuthorizationCode($this->storages['authorization_code']);
+ }
+
+ if (count($grantTypes) == 0) {
+ throw new LogicException("Unable to build default grant types - You must supply an array of grant_types in the constructor");
+ }
+
+ return $grantTypes;
+ }
+
+ protected function getAccessTokenResponseType()
+ {
+ if (isset($this->responseTypes['access_token'])) {
+ return $this->responseTypes['access_token'];
+ }
+ if (!isset($this->storages['access_token'])) {
+ throw new LogicException("You must supply a response type implementing OAuth2_ResponseType_AccessTokenInterface, or a storage object implementing OAuth2_Storage_AccessTokenInterface to use the token server");
+ }
+ $refreshStorage = null;
+ if (isset($this->storages['refresh_token'])) {
+ $refreshStorage = $this->storages['refresh_token'];
+ }
+ $config = array_intersect_key($this->config, array_flip(explode(' ', 'access_lifetime refresh_token_lifetime')));
+ $config['token_type'] = $this->tokenType ? $this->tokenType->getTokenType() : $this->getDefaultTokenType()->getTokenType();
+
+ return new OAuth2_ResponseType_AccessToken($this->storages['access_token'], $refreshStorage, $config);
+ }
+
public function getResponse()
{
return $this->response;
diff --git a/src/OAuth2/Storage/AccessTokenInterface.php b/src/OAuth2/Storage/AccessTokenInterface.php
index 4bbad84..6454ab1 100644
--- a/src/OAuth2/Storage/AccessTokenInterface.php
+++ b/src/OAuth2/Storage/AccessTokenInterface.php
@@ -4,7 +4,7 @@
* Implement this interface to specify where the OAuth2 Server
* should get/save access tokens
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_AccessTokenInterface
{
diff --git a/src/OAuth2/Storage/AuthorizationCodeInterface.php b/src/OAuth2/Storage/AuthorizationCodeInterface.php
index 71fa5a8..16a055d 100644
--- a/src/OAuth2/Storage/AuthorizationCodeInterface.php
+++ b/src/OAuth2/Storage/AuthorizationCodeInterface.php
@@ -5,7 +5,7 @@
* should get/save authorization codes for the "Authorization Code"
* grant type
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_AuthorizationCodeInterface
{
diff --git a/src/OAuth2/Storage/ClientCredentialsInterface.php b/src/OAuth2/Storage/ClientCredentialsInterface.php
index 45af123..d66da74 100644
--- a/src/OAuth2/Storage/ClientCredentialsInterface.php
+++ b/src/OAuth2/Storage/ClientCredentialsInterface.php
@@ -4,7 +4,7 @@
* Implement this interface to specify how the OAuth2 Server
* should verify client credentials
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_ClientCredentialsInterface extends OAuth2_Storage_ClientInterface
{
diff --git a/src/OAuth2/Storage/ClientInterface.php b/src/OAuth2/Storage/ClientInterface.php
index 9ca80fe..0f3def5 100644
--- a/src/OAuth2/Storage/ClientInterface.php
+++ b/src/OAuth2/Storage/ClientInterface.php
@@ -4,7 +4,7 @@
* Implement this interface to specify where the OAuth2 Server
* should retrieve client information
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_ClientInterface
{
@@ -18,8 +18,9 @@ interface OAuth2_Storage_ClientInterface
* Client identifier to be check with.
*
* @return array
- * Client details. Only mandatory item is the "registered redirect URI", and MUST
- * return FALSE if the given client does not exist or is invalid.
+ * Client details. The only mandatory key in the array is "redirect_uri".
+ * This function MUST return FALSE if the given client does not exist or is
+ * invalid. "redirect_uri" can be space-delimited to allow for multiple valid uris.
*
* @ingroup oauth2_section_4
*/
diff --git a/src/OAuth2/Storage/JWTBearerInterface.php b/src/OAuth2/Storage/JWTBearerInterface.php
index 65001d1..d1cc86f 100644
--- a/src/OAuth2/Storage/JWTBearerInterface.php
+++ b/src/OAuth2/Storage/JWTBearerInterface.php
@@ -8,11 +8,10 @@
* be the same storage as retrieving clientData
*
* @author F21
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_JWTBearerInterface
{
-
/**
* Get the public key associated with a client_id
*
diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php
index 32bf485..97acd12 100644
--- a/src/OAuth2/Storage/Memory.php
+++ b/src/OAuth2/Storage/Memory.php
@@ -6,7 +6,7 @@
* NOTE: This class should never be used in production, and is
* a stub class for example use only
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface,
OAuth2_Storage_UserCredentialsInterface, OAuth2_Storage_AccessTokenInterface,
@@ -20,6 +20,7 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
private $accessTokens;
private $jwt;
private $supportedScopes;
+ private $clientSupportedScopes;
private $defaultScope;
public function __construct($params = array())
@@ -32,6 +33,7 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
'access_tokens' => array(),
'jwt' => array(),
'default_scope' => null,
+ 'client_supported_scopes' => array(),
'supported_scopes' => array(),
), $params);
@@ -42,6 +44,7 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
$this->accessTokens = $params['access_tokens'];
$this->jwt = $params['jwt'];
$this->supportedScopes = $params['supported_scopes'];
+ $this->clientSupportedScopes = $params['client_supported_scopes'];
$this->defaultScope = $params['default_scope'];
}
@@ -76,14 +79,16 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
return isset($this->userCredentials[$username]) && $this->userCredentials[$username] === $password;
}
- public function setUserCredentials($user_credentials)
- {
- return isset($this->userCredentials[$username]) ? $this->userCredentials[$username] : null;
- }
-
public function getUserDetails($username)
{
- return $this->getUser($username);
+ if (!isset($this->userCredentials[$username])) {
+ return null;
+ }
+
+ return array(
+ 'user_id' => $username,
+ 'password' => $this->userCredentials[$username],
+ );
}
/* ClientCredentialsInterface */
@@ -151,7 +156,14 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
public function scopeExists($scope, $client_id = null)
{
$scope = explode(' ', trim($scope));
- return (count(array_diff($scope, $this->supportedScopes)) == 0);
+
+ if (!is_null($client_id) && array_key_exists($client_id, $this->clientSupportedScopes)) {
+ $allowedScopes = array_merge($this->supportedScopes, $this->clientSupportedScopes[$client_id]);
+ } else {
+ $allowedScopes = $this->supportedScopes;
+ }
+
+ return (count(array_diff($scope, $allowedScopes)) == 0);
}
public function getDefaultScope()
@@ -163,11 +175,8 @@ class OAuth2_Storage_Memory implements OAuth2_Storage_AuthorizationCodeInterface
public function getClientKey($client_id, $subject)
{
if (isset($this->jwt[$client_id])) {
-
$jwt = $this->jwt[$client_id];
-
if ($jwt) {
-
if ($jwt["subject"] == $subject) {
return $jwt["key"];
}
diff --git a/src/OAuth2/Storage/Mongo.php b/src/OAuth2/Storage/Mongo.php
index 6fd1182..2a2cdfc 100644
--- a/src/OAuth2/Storage/Mongo.php
+++ b/src/OAuth2/Storage/Mongo.php
@@ -169,9 +169,10 @@ class OAuth2_Storage_Mongo implements OAuth2_Storage_AuthorizationCodeInterface,
public function getUserDetails($username)
{
- $user = $this->getUser($username);
- // User details must have a 'user_id' field, to store in token data for example:
- $user['user_id'] = $user['username'];
+ if ($user = $this->getUser($username)) {
+ $user['user_id'] = $user['username'];
+ }
+
return $user;
}
diff --git a/src/OAuth2/Storage/Pdo.php b/src/OAuth2/Storage/Pdo.php
index ca8bcb5..d45d8e4 100644
--- a/src/OAuth2/Storage/Pdo.php
+++ b/src/OAuth2/Storage/Pdo.php
@@ -3,11 +3,14 @@
/**
* Simple PDO storage for all storage types
*
- * NOTE: This class is meant to get users started
- * quickly. If your application requires further
+ * NOTE: This class is meant to get users started
+ * quickly. If your application requires further
* customization, extend this class or create your own.
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * NOTE: Passwords are stored in plaintext, which is never
+ * a good idea. Be sure to override this for your application
+ *
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
OAuth2_Storage_AccessTokenInterface, OAuth2_Storage_ClientCredentialsInterface,
@@ -47,7 +50,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
), $config);
}
- /* ClientCredentialsInterface */
+ /* OAuth2_Storage_ClientCredentialsInterface */
public function checkClientCredentials($client_id, $client_secret = null)
{
$stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table']));
@@ -77,7 +80,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
return true;
}
- /* AccessTokenInterface */
+ /* OAuth2_Storage_AccessTokenInterface */
public function getAccessToken($access_token)
{
$stmt = $this->db->prepare(sprintf('SELECT * from %s where access_token = :access_token', $this->config['access_token_table']));
@@ -105,7 +108,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
return $stmt->execute(compact('access_token', 'client_id', 'user_id', 'expires', 'scope'));
}
- /* AuthorizationCodeInterface */
+ /* OAuth2_Storage_AuthorizationCodeInterface */
public function getAuthorizationCode($code)
{
$stmt = $this->db->prepare(sprintf('SELECT * from %s where authorization_code = :code', $this->config['code_table']));
@@ -140,7 +143,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
return $stmt->execute(compact('code'));
}
- /* UserCredentialsInterface */
+ /* OAuth2_Storage_UserCredentialsInterface */
public function checkUserCredentials($username, $password)
{
if ($user = $this->getUser($username)) {
@@ -154,7 +157,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
return $this->getUser($username);
}
- /* RefreshTokenInterface */
+ /* OAuth2_Storage_RefreshTokenInterface */
public function getRefreshToken($refresh_token)
{
$stmt = $this->db->prepare(sprintf('SELECT * FROM %s WHERE refresh_token = :refresh_token', $this->config['refresh_token_table']));
@@ -209,6 +212,7 @@ class OAuth2_Storage_Pdo implements OAuth2_Storage_AuthorizationCodeInterface,
return $stmt->execute(compact('username', 'password', 'firstName', 'lastName'));
}
+ /* OAuth2_Storage_JWTBearerInterface */
public function getClientKey($client_id, $subject)
{
$stmt = $this->db->prepare($sql = sprintf('SELECT public_key from %s where client_id=:client_id AND subject=:subject', $this->config['jwt_table']));
diff --git a/src/OAuth2/Storage/Redis.php b/src/OAuth2/Storage/Redis.php
new file mode 100644
index 0000000..bce438e
--- /dev/null
+++ b/src/OAuth2/Storage/Redis.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * redis storage for all storage types
+ *
+ * Register client:
+ * <code>
+ * $storage = new OAuth2_Storage_Redis($redis);
+ * $storage->registerClient($client_id, $client_secret, $redirect_uri);
+ * </code>
+ */
+class OAuth2_Storage_Redis implements OAuth2_Storage_AuthorizationCodeInterface,
+ OAuth2_Storage_AccessTokenInterface,
+ OAuth2_Storage_ClientCredentialsInterface,
+ OAuth2_Storage_UserCredentialsInterface,
+ OAuth2_Storage_RefreshTokenInterface,
+ OAuth2_Storage_JWTBearerInterface
+{
+ private $redis;
+ private $config;
+ private $cache;
+
+ /**
+ * Redis Storage!
+ *
+ * @param \Predis\Client $redis
+ * @param array $config
+ */
+ public function __construct($redis, $config=array())
+ {
+ $this->redis = $redis;
+ $this->config = array_merge(array(
+ 'client_key' => 'oauth_clients:',
+ 'access_token_key' => 'oauth_access_tokens:',
+ 'refresh_token_key' => 'oauth_refresh_tokens:',
+ 'code_key' => 'oauth_authorization_codes:',
+ 'user_key' => 'oauth_users:',
+ 'jwt_key' => 'oauth_jwt:',
+ ), $config);
+ }
+
+ protected function getValue($key)
+ {
+ if ( isset($this->cache[$key]) ) {
+ return $this->cache[$key];
+ }
+ $value = $this->redis->get($key);
+ if ( isset($value) ) {
+ return json_decode($value, true);
+ } else {
+ return false;
+ }
+ }
+
+ protected function setValue($key, $value, $expire=0)
+ {
+ $this->cache[$key] = $value;
+ $str = json_encode($value);
+ if ( $expire > 0 ) {
+ $seconds = $expire - time();
+ return $this->redis->setex($key, $seconds, $str);
+ } else {
+ return $this->redis->set($key, $str);
+ }
+ }
+
+ protected function expireValue($key)
+ {
+ unset($this->cache[$key]);
+ return $this->redis->expire($key);
+ }
+
+ /* AuthorizationCodeInterface */
+ public function getAuthorizationCode($code)
+ {
+ return $this->getValue($this->config['code_key'] . $code);
+ }
+
+ public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null)
+ {
+ return $this->setValue(
+ $this->config['code_key'] . $authorization_code,
+ compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope'),
+ $expires
+ );
+ }
+
+ public function expireAuthorizationCode($code)
+ {
+ $key = $this->config['code_key'] . $code;
+ unset($this->cache[$key]);
+ return $this->expireValue($key);
+ }
+
+ /* UserCredentialsInterface */
+ public function checkUserCredentials($username, $password)
+ {
+ $user = $this->getUserDetails($username);
+ return $user && $user['password'] === $password;
+ }
+
+ public function getUserDetails($username)
+ {
+ return $this->getUser($username);
+ }
+
+ public function getUser($username)
+ {
+ return $this->getValue($this->config['user_key'] . $username);
+ }
+
+ public function setUser($username, $password, $first_name = null, $last_name = null)
+ {
+ return $this->setValue(
+ $this->config['user_key'] . $username,
+ compact('username', 'password', 'first_name', 'last_name')
+ );
+ }
+
+ /* ClientCredentialsInterface */
+ public function checkClientCredentials($client_id, $client_secret = null)
+ {
+ $client = $this->getClientDetails($client_id);
+ return isset($client['client_secret'])
+ && $client['client_secret'] == $client_secret;
+ }
+
+ public function getClientDetails($client_id)
+ {
+ return $this->getValue($this->config['client_key'] . $client_id);
+ }
+
+ public function checkRestrictedGrantType($client_id, $grant_type)
+ {
+ $details = $this->getClientDetails($client_id);
+ if (isset($details['grant_types'])) {
+ return in_array($grant_type, (array) $details['grant_types']);
+ }
+
+ // if grant_types are not defined, then none are restricted
+ return true;
+ }
+
+ public function registerClient($client_id, $client_secret, $redirect_uri)
+ {
+ return $this->setValue(
+ $this->config['client_key'] . $client_id,
+ compact('client_id', 'client_secret', 'redirect_uri')
+ );
+ }
+
+ /* RefreshTokenInterface */
+ public function getRefreshToken($refresh_token)
+ {
+ return $this->getValue($this->config['refresh_token_key'] . $refresh_token);
+ }
+
+ public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null)
+ {
+ return $this->setValue(
+ $this->config['refresh_token_key'] . $refresh_token,
+ compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope'),
+ $expires
+ );
+ }
+
+ public function unsetRefreshToken($refresh_token)
+ {
+ return $this->expireValue($this->config['refresh_token_key'] . $refresh_token);
+ }
+
+ /* AccessTokenInterface */
+ public function getAccessToken($access_token)
+ {
+ return $this->getValue($this->config['access_token_key'].$access_token);
+ }
+
+ public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null)
+ {
+ return $this->setValue(
+ $this->config['access_token_key'].$access_token,
+ compact('access_token', 'client_id', 'user_id', 'expires', 'scope'),
+ $expires
+ );
+ }
+
+ /*JWTBearerInterface */
+ public function getClientKey($client_id, $subject)
+ {
+ $jwt = $this->getValue($this->config['jwt_key'] . $client_id);
+ if ( isset($jwt['subject']) && $jwt['subject'] == $subject ) {
+ return $jwt['key'];
+ }
+ return null;
+ }
+}
diff --git a/src/OAuth2/Storage/RefreshTokenInterface.php b/src/OAuth2/Storage/RefreshTokenInterface.php
index eaff39e..b148ecb 100644
--- a/src/OAuth2/Storage/RefreshTokenInterface.php
+++ b/src/OAuth2/Storage/RefreshTokenInterface.php
@@ -5,11 +5,10 @@
* should get/save refresh tokens for the "Refresh Token"
* grant type
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_RefreshTokenInterface
{
-
/**
* Grant refresh access tokens.
*
diff --git a/src/OAuth2/Storage/ScopeInterface.php b/src/OAuth2/Storage/ScopeInterface.php
index 720b74d..5b2caaa 100644
--- a/src/OAuth2/Storage/ScopeInterface.php
+++ b/src/OAuth2/Storage/ScopeInterface.php
@@ -5,7 +5,7 @@
* should retrieve data involving the relevent scopes associated
* with this implementation.
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_ScopeInterface
{
diff --git a/src/OAuth2/Storage/UserCredentialsInterface.php b/src/OAuth2/Storage/UserCredentialsInterface.php
index 5ae184a..ea414d3 100644
--- a/src/OAuth2/Storage/UserCredentialsInterface.php
+++ b/src/OAuth2/Storage/UserCredentialsInterface.php
@@ -5,11 +5,10 @@
* should retrieve user credentials for the
* "Resource Owner Password Credentials" grant type
*
- * @author Brent Shaffer <bshafs@gmail.com>
+ * @author Brent Shaffer <bshafs at gmail dot com>
*/
interface OAuth2_Storage_UserCredentialsInterface
{
-
/**
* Grant access tokens for basic user credentials.
*
@@ -47,7 +46,6 @@ interface OAuth2_Storage_UserCredentialsInterface
* @return
* ARRAY the associated "scope" or "user_id" values if applicable, or an empty array
* if this does not apply
- *
*/
public function getUserDetails($username);
}
diff --git a/src/OAuth2/TokenType/Bearer.php b/src/OAuth2/TokenType/Bearer.php
index ad5e344..838286b 100644
--- a/src/OAuth2/TokenType/Bearer.php
+++ b/src/OAuth2/TokenType/Bearer.php
@@ -3,9 +3,8 @@
/**
*
*/
-class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface, OAuth2_Response_ProviderInterface
+class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface
{
- private $response;
private $config;
public function __construct(array $config = array())
@@ -43,25 +42,25 @@ class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface, OAuth2_Respo
* @see http://code.google.com/p/android/issues/detail?id=6684
*
*/
- public function getAccessTokenParameter(OAuth2_RequestInterface $request)
+ public function getAccessTokenParameter(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
$headers = $request->headers('AUTHORIZATION');
// Check that exactly one method was used
$methodsUsed = !empty($headers) + !is_null($request->query($this->config['token_param_name'])) + !is_null($request->request($this->config['token_param_name']));
if ($methodsUsed > 1) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Only one method may be used to authenticate at a time (Auth header, GET or POST)');
+ $response->setError(400, 'invalid_request', 'Only one method may be used to authenticate at a time (Auth header, GET or POST)');
return null;
}
if ($methodsUsed == 0) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'The access token was not found');
+ $response->setError(400, 'invalid_request', 'The access token was not found');
return null;
}
// HEADER: Get the access token from the header
if (!empty($headers)) {
if (!preg_match('/' . $this->config['token_bearer_header_name'] . '\s(\S+)/', $headers, $matches)) {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'Malformed auth header');
+ $response->setError(400, 'invalid_request', 'Malformed auth header');
return null;
}
return $matches[1];
@@ -70,14 +69,14 @@ class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface, OAuth2_Respo
if ($request->request($this->config['token_param_name'])) {
// POST: Get the token from POST data
if (strtolower($request->server('REQUEST_METHOD')) != 'post') {
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'When putting the token in the body, the method must be POST');
+ $response->setError(400, 'invalid_request', 'When putting the token in the body, the method must be POST');
return null;
}
if ($request->server('CONTENT_TYPE') !== null && $request->server('CONTENT_TYPE') != 'application/x-www-form-urlencoded') {
// IETF specifies content-type. NB: Not all webservers populate this _SERVER variable
// @see http://tools.ietf.org/html/rfc6750#section-2.2
- $this->response = new OAuth2_Response_Error(400, 'invalid_request', 'The content type for POST requests must be "application/x-www-form-urlencoded"');
+ $response->setError(400, 'invalid_request', 'The content type for POST requests must be "application/x-www-form-urlencoded"');
return null;
}
@@ -87,9 +86,4 @@ class OAuth2_TokenType_Bearer implements OAuth2_TokenTypeInterface, OAuth2_Respo
// GET method
return $request->query($this->config['token_param_name']);
}
-
- public function getResponse()
- {
- return $this->response;
- }
}
diff --git a/src/OAuth2/TokenType/MAC.php b/src/OAuth2/TokenType/MAC.php
index c6c8fe3..60427e0 100644
--- a/src/OAuth2/TokenType/MAC.php
+++ b/src/OAuth2/TokenType/MAC.php
@@ -10,7 +10,7 @@ class OAuth2_TokenType_MAC implements OAuth2_TokenTypeInterface
return 'mac';
}
- public function getAccessTokenParameter(OAuth2_RequestInterface $request)
+ public function getAccessTokenParameter(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response)
{
throw new LogicException("Not supported");
diff --git a/src/OAuth2/TokenTypeInterface.php b/src/OAuth2/TokenTypeInterface.php
index 8960e9e..fe7cfdb 100644
--- a/src/OAuth2/TokenTypeInterface.php
+++ b/src/OAuth2/TokenTypeInterface.php
@@ -2,6 +2,15 @@
interface OAuth2_TokenTypeInterface
{
+ /**
+ * Token type identification string
+ *
+ * ex: "bearer" or "mac"
+ */
public function getTokenType();
- public function getAccessTokenParameter(OAuth2_RequestInterface $request);
+
+ /**
+ * Retrieves the token string from the request object
+ */
+ public function getAccessTokenParameter(OAuth2_RequestInterface $request, OAuth2_ResponseInterface $response);
}
diff --git a/test/OAuth2/Server/Authorize/BasicValidationTest.php b/test/OAuth2/Controller/Authorize/BasicValidationTest.php
index efbd95e..ce6a93a 100644
--- a/test/OAuth2/Server/Authorize/BasicValidationTest.php
+++ b/test/OAuth2/Controller/Authorize/BasicValidationTest.php
@@ -1,12 +1,12 @@
<?php
-class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_TestCase
+class OAuth2_Controller_Authorize_BasicValidationTest extends PHPUnit_Framework_TestCase
{
public function testNoClientIdResponse()
{
$server = $this->getTestServer();
$request = OAuth2_Request::createFromGlobals();
- $response = $server->handleAuthorizeRequest($request, false);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), false);
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_client');
@@ -18,7 +18,7 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test
$server = $this->getTestServer();
$request = OAuth2_Request::createFromGlobals();
$request->query['client_id'] = 'Fake Client ID'; // invalid client id
- $response = $server->handleAuthorizeRequest($request, false);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), false);
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_client');
@@ -30,7 +30,7 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test
$server = $this->getTestServer();
$request = OAuth2_Request::createFromGlobals();
$request->query['client_id'] = 'Test Client ID'; // valid client id
- $response = $server->handleAuthorizeRequest($request, false);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), false);
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_uri');
@@ -43,7 +43,7 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test
$request = OAuth2_Request::createFromGlobals();
$request->query['client_id'] = 'Test Client ID'; // valid client id
$request->query['redirect_uri'] = 'http://adobe.com'; // valid redirect URI
- $response = $server->handleAuthorizeRequest($request, false);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), false);
$this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location');
@@ -61,7 +61,7 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test
$request->query['client_id'] = 'Test Client ID'; // valid client id
$request->query['redirect_uri'] = 'http://adobe.com'; // valid redirect URI
$request->query['response_type'] = 'invalid'; // invalid response type
- $response = $server->handleAuthorizeRequest($request, false);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), false);
$this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location');
@@ -79,7 +79,7 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test
$request->query['client_id'] = 'Test Client ID'; // valid client id
$request->query['redirect_uri'] = 'http://adobe.com#fragment'; // valid redirect URI
$request->query['response_type'] = 'code'; // invalid response type
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_uri');
@@ -93,7 +93,7 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test
$request->query['client_id'] = 'Test Client ID'; // valid client id
$request->query['redirect_uri'] = 'http://adobe.com'; // valid redirect URI
$request->query['response_type'] = 'code';
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
$this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location');
@@ -114,7 +114,7 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test
$request->query['client_id'] = 'Test Client ID'; // valid client id
$request->query['redirect_uri'] = 'http://adobe.com'; // valid redirect URI
$request->query['response_type'] = 'code';
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
$this->assertEquals($response->getStatusCode(), 302);
$parts = parse_url($response->getHttpHeader('Location'));
@@ -124,7 +124,7 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test
$this->assertEquals($query['error_description'], 'This application requires you specify a scope parameter');
$request->query['scope'] = 'testscope';
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
$this->assertEquals($response->getStatusCode(), 302);
$parts = parse_url($response->getHttpHeader('Location'));
@@ -134,14 +134,14 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test
$this->assertFalse(isset($query['error']));
}
- public function testValidateRedirectUri()
+ public function testInvalidRedirectUri()
{
$server = $this->getTestServer();
$request = OAuth2_Request::createFromGlobals();
$request->query['client_id'] = 'Test Client ID with Redirect Uri'; // valid client id
$request->query['redirect_uri'] = 'http://adobe.com'; // invalid redirect URI
$request->query['response_type'] = 'code';
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
$this->assertEquals($response->getStatusCode(), 400);
@@ -149,6 +149,24 @@ class OAuth2_Server_Authorize_BasicValidationTest extends PHPUnit_Framework_Test
$this->assertEquals($response->getParameter('error_description'), 'The redirect URI provided is missing or does not match');
}
+ public function testMultipleRedirectUris()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request::createFromGlobals();
+ $request->query['client_id'] = 'Test Client ID with Multiple Redirect Uris'; // valid client id
+ $request->query['redirect_uri'] = 'http://brentertainment.com'; // valid redirect URI
+ $request->query['response_type'] = 'code';
+
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
+ $this->assertEquals($response->getStatusCode(), 302);
+
+ // call again with different (but still valid) redirect URI
+ $request->query['redirect_uri'] = 'http://morehazards.com';
+
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
+ $this->assertEquals($response->getStatusCode(), 302);
+ }
+
private function getTestServer($config = array())
{
$storage = OAuth2_Storage_Bootstrap::getInstance()->getMemoryStorage();
diff --git a/test/OAuth2/Server/Authorize/CodeTest.php b/test/OAuth2/Controller/Authorize/CodeTest.php
index c099298..3647c74 100644
--- a/test/OAuth2/Server/Authorize/CodeTest.php
+++ b/test/OAuth2/Controller/Authorize/CodeTest.php
@@ -1,6 +1,6 @@
<?php
-class OAuth2_Server_Authorize_CodeTest extends PHPUnit_Framework_TestCase
+class OAuth2_Controller_Authorize_CodeTest extends PHPUnit_Framework_TestCase
{
public function testUserDeniesAccessResponse()
{
@@ -9,7 +9,7 @@ class OAuth2_Server_Authorize_CodeTest extends PHPUnit_Framework_TestCase
$request->query['client_id'] = 'Test Client ID'; // valid client id
$request->query['redirect_uri'] = 'http://adobe.com'; // valid redirect URI
$request->query['response_type'] = 'code';
- $response = $server->handleAuthorizeRequest($request, false);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), false);
$this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location');
@@ -27,7 +27,7 @@ class OAuth2_Server_Authorize_CodeTest extends PHPUnit_Framework_TestCase
$request->query['client_id'] = 'Test Client ID'; // valid client id
$request->query['redirect_uri'] = 'http://adobe.com'; // valid redirect URI
$request->query['response_type'] = 'code';
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
$this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location');
@@ -60,7 +60,7 @@ class OAuth2_Server_Authorize_CodeTest extends PHPUnit_Framework_TestCase
$request->query['redirect_uri'] = 'http://adobe.com'; // valid redirect URI
$request->query['response_type'] = 'code';
$request->query['state'] = 'test'; // valid state string (just needs to be passed back to us)
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
$this->assertEquals($response->getStatusCode(), 302);
@@ -86,7 +86,7 @@ class OAuth2_Server_Authorize_CodeTest extends PHPUnit_Framework_TestCase
$request->query['response_type'] = 'code';
$request->query['state'] = 'test'; // valid state string (just needs to be passed back to us)
$request->query['fake'] = 'something'; // extra query param
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
$this->assertEquals($response->getStatusCode(), 302);
$this->assertNull($response->getParameter('error'));
diff --git a/test/OAuth2/Server/Authorize/ImplicitTest.php b/test/OAuth2/Controller/Authorize/ImplicitTest.php
index 30dcb39..b1ea03b 100644
--- a/test/OAuth2/Server/Authorize/ImplicitTest.php
+++ b/test/OAuth2/Controller/Authorize/ImplicitTest.php
@@ -1,6 +1,6 @@
<?php
-class OAuth2_Server_Authorize_ImplicitTest extends PHPUnit_Framework_TestCase
+class OAuth2_Controller_Authorize_ImplicitTest extends PHPUnit_Framework_TestCase
{
public function testImplicitNotAllowedResponse()
{
@@ -9,7 +9,7 @@ class OAuth2_Server_Authorize_ImplicitTest extends PHPUnit_Framework_TestCase
$request->query['client_id'] = 'Test Client ID'; // valid client id
$request->query['redirect_uri'] = 'http://adobe.com'; // valid redirect URI
$request->query['response_type'] = 'token'; // invalid response type
- $response = $server->handleAuthorizeRequest($request, false);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), false);
$this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location');
@@ -27,7 +27,7 @@ class OAuth2_Server_Authorize_ImplicitTest extends PHPUnit_Framework_TestCase
$request->query['client_id'] = 'Test Client ID'; // valid client id
$request->query['redirect_uri'] = 'http://adobe.com'; // valid redirect URI
$request->query['response_type'] = 'token'; // valid response type
- $response = $server->handleAuthorizeRequest($request, false);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), false);
$this->assertEquals($response->getStatusCode(), 302);
$location = $response->getHttpHeader('Location');
@@ -45,7 +45,7 @@ class OAuth2_Server_Authorize_ImplicitTest extends PHPUnit_Framework_TestCase
$request->query['client_id'] = 'Test Client ID'; // valid client id
$request->query['redirect_uri'] = 'http://adobe.com'; // valid redirect URI
$request->query['response_type'] = 'token'; // valid response type
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
$this->assertEquals($response->getStatusCode(), 302);
$this->assertNull($response->getParameter('error'));
@@ -75,7 +75,7 @@ class OAuth2_Server_Authorize_ImplicitTest extends PHPUnit_Framework_TestCase
$request->query['redirect_uri'] = 'http://adobe.com'; // valid redirect URI
$request->query['response_type'] = 'token'; // valid response type
$request->query['state'] = 'test'; // valid state string (just needs to be passed back to us)
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
$this->assertEquals($response->getStatusCode(), 302);
$this->assertNull($response->getParameter('error'));
@@ -99,7 +99,7 @@ class OAuth2_Server_Authorize_ImplicitTest extends PHPUnit_Framework_TestCase
$request->query['response_type'] = 'token'; // valid response type
$request->query['state'] = 'test'; // valid state string (just needs to be passed back to us)
$request->query['fake'] = 'something'; // add extra param to querystring
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
$this->assertEquals($response->getStatusCode(), 302);
$this->assertNull($response->getParameter('error'));
diff --git a/test/OAuth2/Server/Access/BasicValidationTest.php b/test/OAuth2/Controller/Resource/BasicValidationTest.php
index b20045f..faae4bb 100644
--- a/test/OAuth2/Server/Access/BasicValidationTest.php
+++ b/test/OAuth2/Controller/Resource/BasicValidationTest.php
@@ -1,15 +1,14 @@
<?php
-class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCase
+class OAuth2_Controller_Resource_BasicValidationTest extends PHPUnit_Framework_TestCase
{
public function testNoAccessToken()
{
$server = $this->getTestServer();
$request = OAuth2_Request::createFromGlobals();
- $allow = $server->verifyResourceRequest($request);
+ $allow = $server->verifyResourceRequest($request, $response = new OAuth2_Response());
$this->assertFalse($allow);
- $response = $server->getResponse();
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
$this->assertEquals($response->getParameter('error_description'), 'The access token was not found');
@@ -20,10 +19,9 @@ class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCas
$server = $this->getTestServer();
$request = OAuth2_Request::createFromGlobals();
$request->headers['AUTHORIZATION'] = 'tH1s i5 B0gU5';
- $allow = $server->verifyResourceRequest($request);
+ $allow = $server->verifyResourceRequest($request, $response = new OAuth2_Response());
$this->assertFalse($allow);
- $response = $server->getResponse();
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
$this->assertEquals($response->getParameter('error_description'), 'Malformed auth header');
@@ -35,10 +33,9 @@ class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCas
$request = OAuth2_Request::createFromGlobals();
$request->request['access_token'] = 'TEST';
$request->query['access_token'] = 'TEST';
- $allow = $server->verifyResourceRequest($request);
+ $allow = $server->verifyResourceRequest($request, $response = new OAuth2_Response());
$this->assertFalse($allow);
- $response = $server->getResponse();
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
$this->assertEquals($response->getParameter('error_description'), 'Only one method may be used to authenticate at a time (Auth header, GET or POST)');
@@ -50,10 +47,9 @@ class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCas
$request = OAuth2_Request::createFromGlobals();
$request->server['REQUEST_METHOD'] = 'GET';
$request->request['access_token'] = 'TEST';
- $allow = $server->verifyResourceRequest($request);
+ $allow = $server->verifyResourceRequest($request, $response = new OAuth2_Response());
$this->assertFalse($allow);
- $response = $server->getResponse();
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
$this->assertEquals($response->getParameter('error_description'), 'When putting the token in the body, the method must be POST');
@@ -66,10 +62,9 @@ class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCas
$request->server['REQUEST_METHOD'] = 'POST';
$request->server['CONTENT_TYPE'] = 'application/json';
$request->request['access_token'] = 'TEST';
- $allow = $server->verifyResourceRequest($request);
+ $allow = $server->verifyResourceRequest($request, $response = new OAuth2_Response());
$this->assertFalse($allow);
- $response = $server->getResponse();
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
$this->assertEquals($response->getParameter('error_description'), 'The content type for POST requests must be "application/x-www-form-urlencoded"');
@@ -80,10 +75,9 @@ class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCas
$server = $this->getTestServer();
$request = OAuth2_Request::createFromGlobals();
$request->headers['AUTHORIZATION'] = 'Bearer TESTTOKEN';
- $allow = $server->verifyResourceRequest($request);
+ $allow = $server->verifyResourceRequest($request, $response = new OAuth2_Response());
$this->assertFalse($allow);
- $response = $server->getResponse();
$this->assertEquals($response->getStatusCode(), 401);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
$this->assertEquals($response->getParameter('error_description'), 'The access token provided is invalid');
@@ -94,10 +88,9 @@ class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCas
$server = $this->getTestServer();
$request = OAuth2_Request::createFromGlobals();
$request->headers['AUTHORIZATION'] = 'Bearer accesstoken-expired';
- $allow = $server->verifyResourceRequest($request);
+ $allow = $server->verifyResourceRequest($request, $response = new OAuth2_Response());
$this->assertFalse($allow);
- $response = $server->getResponse();
$this->assertEquals($response->getStatusCode(), 401);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
$this->assertEquals($response->getParameter('error_description'), 'The access token provided has expired');
@@ -109,10 +102,9 @@ class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCas
$request = OAuth2_Request::createFromGlobals();
$request->headers['AUTHORIZATION'] = 'Bearer accesstoken-scope';
$scope = 'outofscope';
- $allow = $server->verifyResourceRequest($request, $scope);
+ $allow = $server->verifyResourceRequest($request, $response = new OAuth2_Response(), $scope);
$this->assertFalse($allow);
- $response = $server->getResponse();
$this->assertEquals($response->getStatusCode(), 401);
$this->assertEquals($response->getParameter('error'), 'insufficient_scope');
$this->assertEquals($response->getParameter('error_description'), 'The request requires higher privileges than provided by the access token');
@@ -123,10 +115,9 @@ class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCas
$server = $this->getTestServer();
$request = OAuth2_Request::createFromGlobals();
$request->headers['AUTHORIZATION'] = 'Bearer accesstoken-malformed';
- $allow = $server->verifyResourceRequest($request);
+ $allow = $server->verifyResourceRequest($request, $response = new OAuth2_Response());
$this->assertFalse($allow);
- $response = $server->getResponse();
$this->assertEquals($response->getStatusCode(), 401);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
$this->assertEquals($response->getParameter('error_description'), 'Malformed token (missing "expires" or "client_id")');
@@ -137,7 +128,7 @@ class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCas
$server = $this->getTestServer();
$request = OAuth2_Request::createFromGlobals();
$request->headers['AUTHORIZATION'] = 'Bearer accesstoken-scope';
- $allow = $server->verifyResourceRequest($request);
+ $allow = $server->verifyResourceRequest($request, $response = new OAuth2_Response());
$this->assertTrue($allow);
}
@@ -147,7 +138,7 @@ class OAuth2_Server_Access_BasicValidationTest extends PHPUnit_Framework_TestCas
$request = OAuth2_Request::createFromGlobals();
$request->headers['AUTHORIZATION'] = 'Bearer accesstoken-scope';
$request->query['scope'] = 'testscope';
- $allow = $server->verifyResourceRequest($request);
+ $allow = $server->verifyResourceRequest($request, $response = new OAuth2_Response());
$this->assertTrue($allow);
}
diff --git a/test/OAuth2/Server/Grant/BasicValidationTest.php b/test/OAuth2/Controller/Token/BasicValidationTest.php
index d6defba..d8a256b 100644
--- a/test/OAuth2/Server/Grant/BasicValidationTest.php
+++ b/test/OAuth2/Controller/Token/BasicValidationTest.php
@@ -1,12 +1,12 @@
<?php
-class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase
+class OAuth2_Controller_Token_BasicValidationTest extends PHPUnit_Framework_TestCase
{
public function testNoGrantType()
{
// add the test parameters in memory
$server = $this->getTestServer();
- $response = $server->handleTokenRequest(OAuth2_Request_TestRequest::createPost());
+ $server->handleTokenRequest(OAuth2_Request_TestRequest::createPost(), $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
@@ -20,7 +20,7 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase
$request = OAuth2_Request_TestRequest::createPost(array(
'grant_type' => 'invalid_grant_type', // invalid grant type
));
- $response = $server->handleTokenRequest($request);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'unsupported_grant_type');
@@ -33,8 +33,9 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase
$server = $this->getTestServer();
$request = OAuth2_Request_TestRequest::createPost(array(
'grant_type' => 'authorization_code', // valid grant type
+ 'code' => 'testcode',
));
- $response = $server->handleTokenRequest($request);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_client');
@@ -47,33 +48,44 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase
$server = $this->getTestServer();
$request = OAuth2_Request_TestRequest::createPost(array(
'grant_type' => 'authorization_code', // valid grant type
+ 'code' => 'testcode',
'client_id' => 'Test Client ID', // valid client id
));
- $response = $server->handleTokenRequest($request);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_client');
$this->assertEquals($response->getParameter('error_description'), 'Client credentials were not found in the headers or body');
}
- public function testInvalidClientCredentials()
+ public function testInvalidClientId()
{
// add the test parameters in memory
$server = $this->getTestServer();
$request = OAuth2_Request_TestRequest::createPost(array(
'grant_type' => 'authorization_code', // valid grant type
- 'client_id' => 'Fake Client ID', // invalid client id
- 'client_secret' => 'Fake Client Secret', // invalid client secret
+ 'code' => 'testcode',
+ 'client_id' => 'Fake Client ID', // invalid client id
+ 'client_secret' => 'TestSecret', // valid client secret
));
- $response = $server->handleTokenRequest($request);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_client');
$this->assertEquals($response->getParameter('error_description'), 'The client credentials are invalid');
+ }
- // try again with a real client ID, but an invalid secret
- $request->request['client_id'] = 'Test Client ID'; // valid client id
- $response = $server->handleTokenRequest($request);
+ public function testInvalidClientSecret()
+ {
+ // add the test parameters in memory
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'code' => 'testcode',
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'Fake Client Secret', // invalid client secret
+ ));
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_client');
@@ -90,7 +102,7 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase
'client_secret' => 'TestSecret', // valid client secret
'code' => 'testcode', // valid authorization code
));
- $response = $server->handleTokenRequest($request);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertTrue($response instanceof OAuth2_Response);
$this->assertEquals($response->getStatusCode(), 200);
@@ -101,6 +113,43 @@ class OAuth2_Server_Grant_BasicValidationTest extends PHPUnit_Framework_TestCase
$this->assertNotNUll($response->getParameter('token_type'));
}
+ public function testValidClientIdScope()
+ {
+ // add the test parameters in memory
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'code' => 'testcode',
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'scope' => 'clientscope1 clientscope2 scope1 scope2 scope3'
+ ));
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 200);
+ $this->assertNull($response->getParameter('error'));
+ $this->assertNull($response->getParameter('error_description'));
+ $this->assertEquals('clientscope1 clientscope2 scope1 scope2 scope3', $response->getParameter('scope'));
+ }
+
+ public function testInvalidClientIdScope()
+ {
+ // add the test parameters in memory
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'code' => 'testcode',
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'scope' => 'clientscope3 scope1'
+ ));
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 400);
+ $this->assertEquals($response->getParameter('error'), 'invalid_scope');
+ $this->assertEquals($response->getParameter('error_description'), 'An unsupported scope was requested.');
+ }
+
private function getTestServer()
{
$storage = OAuth2_Storage_Bootstrap::getInstance()->getMemoryStorage();
diff --git a/test/OAuth2/GrantType/AuthorizationCodeTest.php b/test/OAuth2/GrantType/AuthorizationCodeTest.php
index f853c23..5bd5831 100644
--- a/test/OAuth2/GrantType/AuthorizationCodeTest.php
+++ b/test/OAuth2/GrantType/AuthorizationCodeTest.php
@@ -10,7 +10,7 @@ class OAuth2_GrantType_AuthorizationCodeTest extends PHPUnit_Framework_TestCase
'client_id' => 'Test Client ID', // valid client id
'client_secret' => 'TestSecret', // valid client secret
));
- $response = $server->handleTokenRequest($request);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
@@ -21,12 +21,12 @@ class OAuth2_GrantType_AuthorizationCodeTest extends PHPUnit_Framework_TestCase
{
$server = $this->getTestServer();
$request = OAuth2_Request_TestRequest::createPost(array(
- 'grant_type' => 'authorization_code', // valid grant type
- 'client_id' => 'Test Client ID', // valid client id
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
'client_secret' => 'TestSecret', // valid client secret
- 'code' => 'InvalidCode', // invalid authorization code
+ 'code' => 'InvalidCode', // invalid authorization code
));
- $response = $server->handleTokenRequest($request);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
@@ -37,27 +37,161 @@ class OAuth2_GrantType_AuthorizationCodeTest extends PHPUnit_Framework_TestCase
{
$server = $this->getTestServer();
$request = OAuth2_Request_TestRequest::createPost(array(
- 'grant_type' => 'authorization_code', // valid grant type
- 'client_id' => 'Test Client ID', // valid client id
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
'client_secret' => 'TestSecret', // valid client secret
- 'code' => 'testcode', // valid code
+ 'code' => 'testcode', // valid code
));
- $response = $server->handleTokenRequest($request);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 200);
$this->assertNotNull($response->getParameter('access_token'));
// try to use the same code again
- $response = $server->handleTokenRequest($request);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
$this->assertEquals($response->getParameter('error_description'), 'Authorization code doesn\'t exist or is invalid for the client');
}
+ public function testExpiredCode()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'code' => 'testcode-expired', // expired authorization code
+ ));
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 400);
+ $this->assertEquals($response->getParameter('error'), 'invalid_grant');
+ $this->assertEquals($response->getParameter('error_description'), 'The authorization code has expired');
+ }
+
+ public function testValidCode()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'code' => 'testcode', // valid code
+ ));
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
+
+ $this->assertNotNull($token);
+ $this->assertArrayHasKey('access_token', $token);
+ }
+
+ public function testValidCodeNoScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'code' => 'testcode-with-scope', // valid code
+ ));
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
+
+ $this->assertNotNull($token);
+ $this->assertArrayHasKey('access_token', $token);
+ $this->assertArrayHasKey('scope', $token);
+ $this->assertEquals($token['scope'], 'scope1 scope2');
+ }
+
+ public function testValidCodeSameScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'code' => 'testcode-with-scope', // valid code
+ 'scope' => 'scope2 scope1',
+ ));
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
+
+ $this->assertNotNull($token);
+ $this->assertArrayHasKey('access_token', $token);
+ $this->assertArrayHasKey('scope', $token);
+ $this->assertEquals($token['scope'], 'scope2 scope1');
+ }
+
+ public function testValidCodeLessScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'code' => 'testcode-with-scope', // valid code
+ 'scope' => 'scope1',
+ ));
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
+
+ $this->assertNotNull($token);
+ $this->assertArrayHasKey('access_token', $token);
+ $this->assertArrayHasKey('scope', $token);
+ $this->assertEquals($token['scope'], 'scope1');
+ }
+
+ public function testValidCodeDifferentScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'code' => 'testcode-with-scope', // valid code
+ 'scope' => 'scope3',
+ ));
+ $token = $server->grantAccessToken($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 400);
+ $this->assertEquals($response->getParameter('error'), 'invalid_scope');
+ $this->assertEquals($response->getParameter('error_description'), 'An unsupported scope was requested.');
+ }
+
+ public function testValidCodeInvalidScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'code' => 'testcode-with-scope', // valid code
+ 'scope' => 'invalid-scope',
+ ));
+ $token = $server->grantAccessToken($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 400);
+ $this->assertEquals($response->getParameter('error'), 'invalid_scope');
+ $this->assertEquals($response->getParameter('error_description'), 'An unsupported scope was requested.');
+ }
+
+ public function testValidClientDifferentCode()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code', // valid grant type
+ 'client_id' => 'Test Some Other Client', // valid client id
+ 'client_secret' => 'TestSecret3', // valid client secret
+ 'code' => 'testcode', // valid code
+ ));
+ $token = $server->grantAccessToken($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 400);
+ $this->assertEquals($response->getParameter('error'), 'invalid_grant');
+ $this->assertEquals($response->getParameter('error_description'), 'authorization_code doesn\'t exist or is invalid for the client');
+ }
+
private function getTestServer()
{
- $storage = new OAuth2_Storage_Memory(json_decode(file_get_contents(dirname(__FILE__).'/../../config/storage.json'), true));
+ $storage = OAuth2_Storage_Bootstrap::getInstance()->getMemoryStorage();
$server = new OAuth2_Server($storage);
$server->addGrantType(new OAuth2_GrantType_AuthorizationCode($storage));
diff --git a/test/OAuth2/GrantType/ClientCredentialsTest.php b/test/OAuth2/GrantType/ClientCredentialsTest.php
index b184e2d..0fcbe18 100644
--- a/test/OAuth2/GrantType/ClientCredentialsTest.php
+++ b/test/OAuth2/GrantType/ClientCredentialsTest.php
@@ -10,43 +10,59 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase
'client_id' => 'Test Client ID', // valid client id
'client_secret' => 'FakeSecret', // valid client secret
));
- $response = $server->handleTokenRequest($request);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_client');
$this->assertEquals($response->getParameter('error_description'), 'The client credentials are invalid');
}
- public function testValidCredentialsWithScope()
+ public function testValidCredentials()
{
$server = $this->getTestServer();
$request = OAuth2_Request_TestRequest::createPost(array(
'grant_type' => 'client_credentials', // valid grant type
'client_id' => 'Test Client ID', // valid client id
'client_secret' => 'TestSecret', // valid client secret
- 'scope' => 'scope',
));
- $token = $server->grantAccessToken($request);
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
$this->assertNotNull($token);
- $this->assertArrayHasKey('access_token', $token);
$this->assertArrayHasKey('scope', $token);
- $this->assertEquals($token['scope'], 'scope');
+ $this->assertNull($token['scope']);
}
- public function testValidCredentialsNoScope()
+ public function testValidCredentialsWithScope()
{
$server = $this->getTestServer();
$request = OAuth2_Request_TestRequest::createPost(array(
'grant_type' => 'client_credentials', // valid grant type
'client_id' => 'Test Client ID', // valid client id
'client_secret' => 'TestSecret', // valid client secret
+ 'scope' => 'scope1',
));
- $token = $server->grantAccessToken($request);
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
$this->assertNotNull($token);
+ $this->assertArrayHasKey('access_token', $token);
$this->assertArrayHasKey('scope', $token);
- $this->assertNull($token['scope']);
+ $this->assertEquals($token['scope'], 'scope1');
+ }
+
+ public function testValidCredentialsInvalidScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'client_credentials', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'scope' => 'invalid-scope',
+ ));
+ $token = $server->grantAccessToken($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 400);
+ $this->assertEquals($response->getParameter('error'), 'invalid_scope');
+ $this->assertEquals($response->getParameter('error_description'), 'An unsupported scope was requested.');
}
public function testValidCredentialsInHeader()
@@ -56,7 +72,7 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase
$headers = array('HTTP_AUTHORIZATION' => 'Basic '.base64_encode('Test Client ID:TestSecret'), 'REQUEST_METHOD' => 'POST');
$params = array('grant_type' => 'client_credentials');
$request = new OAuth2_Request(array(), $params, array(), array(), array(), $headers);
- $token = $server->grantAccessToken($request);
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
$this->assertNotNull($token);
$this->assertArrayHasKey('access_token', $token);
@@ -66,7 +82,7 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase
$headers = array('PHP_AUTH_USER' => 'Test Client ID', 'PHP_AUTH_PW' => 'TestSecret', 'REQUEST_METHOD' => 'POST');
$params = array('grant_type' => 'client_credentials');
$request = new OAuth2_Request(array(), $params, array(), array(), array(), $headers);
- $token = $server->grantAccessToken($request);
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
$this->assertNotNull($token);
$this->assertArrayHasKey('access_token', $token);
@@ -81,7 +97,7 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase
'client_id' => 'Test Client ID', // valid client id
'client_secret' => 'TestSecret', // valid client secret
));
- $token = $server->grantAccessToken($request);
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
$this->assertNotNull($token);
$this->assertArrayHasKey('access_token', $token);
@@ -96,7 +112,7 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase
'client_id' => 'Test Client ID', // valid client id
'client_secret' => 'TestSecret', // valid client secret
));
- $token = $server->grantAccessToken($request);
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
$this->assertNotNull($token);
$this->assertArrayHasKey('access_token', $token);
@@ -105,7 +121,7 @@ class OAuth2_GrantType_ClientCredentialsTest extends PHPUnit_Framework_TestCase
private function getTestServer()
{
- $storage = new OAuth2_Storage_Memory(json_decode(file_get_contents(dirname(__FILE__).'/../../config/storage.json'), true));
+ $storage = OAuth2_Storage_Bootstrap::getInstance()->getMemoryStorage();
$server = new OAuth2_Server($storage);
$server->addGrantType(new OAuth2_GrantType_ClientCredentials($storage));
diff --git a/test/OAuth2/GrantType/JWTBearerTest.php b/test/OAuth2/GrantType/JWTBearerTest.php
index 4d13145..a50b229 100644
--- a/test/OAuth2/GrantType/JWTBearerTest.php
+++ b/test/OAuth2/GrantType/JWTBearerTest.php
@@ -2,6 +2,29 @@
class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
{
+ private $privateKey;
+
+ public function setUp()
+ {
+ $this->privateKey = <<<EOD
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC5/SxVlE8gnpFqCxgl2wjhzY7ucEi00s0kUg3xp7lVEvgLgYcA
+nHiWp+gtSjOFfH2zsvpiWm6Lz5f743j/FEzHIO1owR0p4d9pOaJK07d01+RzoQLO
+IQAgXrr4T1CCWUesncwwPBVCyy2Mw3Nmhmr9MrF8UlvdRKBxriRnlP3qJQIDAQAB
+AoGAVgJJVU4fhYMu1e5JfYAcTGfF+Gf+h3iQm4JCpoUcxMXf5VpB9ztk3K7LRN5y
+kwFuFALpnUAarRcUPs0D8FoP4qBluKksbAtgHkO7bMSH9emN+mH4le4qpFlR7+P1
+3fLE2Y19IBwPwEfClC+TpJvuog6xqUYGPlg6XLq/MxQUB4ECQQDgovP1v+ONSeGS
+R+NgJTR47noTkQT3M2izlce/OG7a+O0yw6BOZjNXqH2wx3DshqMcPUFrTjibIClP
+l/tEQ3ShAkEA0/TdBYDtXpNNjqg0R9GVH2pw7Kh68ne6mZTuj0kCgFYpUF6L6iMm
+zXamIJ51rTDsTyKTAZ1JuAhAsK/M2BbDBQJAKQ5fXEkIA+i+64dsDUR/hKLBeRYG
+PFAPENONQGvGBwt7/s02XV3cgGbxIgAxqWkqIp0neb9AJUoJgtyaNe3GQQJANoL4
+QQ0af0NVJAZgg8QEHTNL3aGrFSbzx8IE5Lb7PLRsJa5bP5lQxnDoYuU+EI/Phr62
+niisp/b/ZDGidkTMXQJBALeRsH1I+LmICAvWXpLKa9Gv0zGCwkuIJLiUbV9c6CVh
+suocCAteQwL5iW2gA4AnYr5OGeHFsEl7NCQcwfPZpJ0=
+-----END RSA PRIVATE KEY-----
+EOD;
+ }
+
public function testMalformedJWT()
{
$server = $this->getTestServer();
@@ -15,8 +38,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
$request->request['assertion'] = $jwt;
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
@@ -34,8 +56,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
$jwt = $this->getJWT() . 'notSupposeToBeHere';
$request->request['assertion'] = $jwt;
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
@@ -53,8 +74,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
$jwt = $this->getJWT(1234);
$request->request['assertion'] = $jwt;
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
@@ -72,8 +92,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
$jwt = $this->getJWT('badtimestamp');
$request->request['assertion'] = $jwt;
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
@@ -89,8 +108,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
//Do not pass the assert (JWT)
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
@@ -108,8 +126,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
$jwt = $this->getJWT(null, time() + 10000);
$request->request['assertion'] = $jwt;
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
@@ -127,8 +144,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
$jwt = $this->getJWT(null, 'notatimestamp');
$request->request['assertion'] = $jwt;
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
@@ -143,8 +159,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
'assertion' => $this->getJWT(),
));
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
@@ -159,8 +174,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
'assertion' => $this->getJWT(null, null, null, 'bad_client_id'),
));
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
@@ -175,8 +189,7 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
'assertion' => $this->getJWT(null, null, 'anotheruser@ourdomain,com'),
));
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
@@ -191,14 +204,75 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
'assertion' => $this->getJWT(null, null, null, 'Missing Key Cli,nt'),
));
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
$this->assertEquals($response->getParameter('error_description'), 'Invalid issuer (iss) or subject (sub) provided');
}
+ public function testValidJwt()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', // valid grant type
+ 'assertion' => $this->getJWT(), // valid assertion
+ ));
+
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
+ $this->assertNotNull($token);
+ $this->assertArrayHasKey('access_token', $token);
+ }
+
+ public function testValidJwtWithScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', // valid grant type
+ 'assertion' => $this->getJWT(null, null, null, 'Test Client ID', 'scope1'), // valid assertion
+ ));
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
+
+ $this->assertNotNull($token);
+ $this->assertArrayHasKey('access_token', $token);
+ $this->assertArrayHasKey('scope', $token);
+ $this->assertEquals($token['scope'], 'scope1');
+ }
+
+ public function testValidJwtInvalidScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', // valid grant type
+ 'assertion' => $this->getJWT(null, null, null, 'Test Client ID', 'invalid-scope'), // valid assertion with invalid scope
+ ));
+ $token = $server->grantAccessToken($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 400);
+ $this->assertEquals($response->getParameter('error'), 'invalid_scope');
+ $this->assertEquals($response->getParameter('error_description'), 'An unsupported scope was requested.');
+ }
+
+ public function testJwtUtil()
+ {
+ $storage = OAuth2_Storage_Bootstrap::getInstance()->getMemoryStorage();
+ $jwtUtil = new OAuth2_Encryption_JWT();
+ $client_id = 'Test Client ID';
+ $params = $this->getJWTParams(null, null, null, $client_id);
+
+
+ if (version_compare(PHP_VERSION, '5.3.3') <= 0) {
+ $encoded = $jwtUtil->encode($params, 'mysecretkey', 'HS256');
+ $client_id .= ' PHP-5.2';
+ } else {
+ $encoded = $jwtUtil->encode($params, $this->privateKey, 'RS256');
+ }
+
+ $payload = $jwtUtil->decode($encoded, $storage->getClientKey($client_id, "testuser@ourdomain.com"));
+
+ $this->assertEquals($params, $payload);
+ }
+
/**
* Generates a JWT
* @param $exp The expiration date. If the current time is greater than the exp, the JWT is invalid.
@@ -207,83 +281,55 @@ class OAuth2_GrantType_JWTBearerTest extends PHPUnit_Framework_TestCase
* @param $iss The issuer, usually the client_id.
* @return string
*/
- private function getJWT($exp = null, $nbf = null, $sub = null, $iss = 'Test Client ID')
+ private function getJWTParams($exp = null, $nbf = null, $sub = null, $iss = 'Test Client ID', $scope = null)
{
- $privateKey = <<<EOD
------BEGIN PRIVATE KEY-----
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC/yMgbkPnOnrXt
-rcbxZVtTUXdYedy28V6Td04F2h6Meh+XAQrpifoxTTF5c3QgcHq9+3ZcQH/tBnSA
-NY+aXa37mo0e325I0v9cR40g/fbZLV0qp+4vVboWYfSlppiDYritM6JAaLEjzmQ0
-OXGgI7gXGUZwHSXNa0DGfHnKo0hJ2PoEcJYLxVDLHcMcZ4rJ9yh3y3hCQQnAfN4U
-dhQg8SJlWr2ziwofyDFOeuai7M0jburTAx7z93JmWn//Q14pLr4855csGKLq5gv8
-dajXopD++2ez6X1Y0W3g9owdwWyFmsHv7BUcD09zBmRDVRm+jD9PPlZc6j1k9sBN
-6rA9VhdTAgMBAAECggEARJi7QmjjgQzxg5bhjpwzQyTjbCkCUgkZS+Oja6JtxM81
-l1xPG+M3MwUqSgeKpMO2k1drKWoEl7H0X5tIxuz0+wZ3rXA7UR7vKYGKMB/GeD2q
-kkRIhHPEQJc+2zUwXeYkqgbK5QDNQ0JiQu+8/YtpIBHxSM6UI9pV6+i/40Pt6kNr
-/v1nGqu2KfwVEJ7O5moFdyLaR6QHFc9hxUxeAdcC6QOEoAp59+8UBkXFe5sK6VNO
-iRBgnAIrppopkDG2XTb2zA1dZFYnUmgeLtL9lDIBv2KOxUCI5KZe5wTaRQzLRVrG
-2DFvE4HrBIl3hAoy/wDhVPkw/mEfk9Y+8lKhdYC5gQKBgQDexfU1z03yV4DA5sxB
-8ZLEiJBkfPPNwhgY85/Thb4Xd4bDq7mPIkqWtIXSrWBKC9+uF3cN3Aq65rU/hZ8k
-n9CqZdkYfGyBburwhTfd/9n/LNXjDDsrmvBqBf2yxOReLKNU+Xp8/Opov/bMrwS5
-BBMCVSnkXqke6GJuUEd+fHI6wQKBgQDcY5ZWxFCn2u8+VTw7rA2465cxDjfVBWOP
-99lI+vzSjeoxqsfnD27yxiVlMlct09X2oF2Z8NRr1Ekb+SFzPoANfqj1B6Knsp0w
-3FivvCABCVPEXtLIdlfibsewtgTs3Kx2IvzS1XL1gN+awHiIut47deAAuZIZWqvm
-skESO+F7EwKBgELOzxjSEKgqwbwX+w2TqtYxtkvMhTkhiiSBe17t7vIOsGWh7EYW
-nJPRk4h44jWSlgQZmWSYpsciRRzFr9JF80gGvzJurgOrBd4XtYdoITI7efFbS6tq
-Dctd/JVOIsZzUJA9ORSerJW5bAQ6QIpTxHegnq2UDdftDQfUH3y5SXCBAoGAI+9h
-DhjIhfYe2G0RezFcs3BGoMsOs39DcrAZD9tM6hAVxa9xFO3hS1iftFZ2/JPz6VjQ
-qk8oc8STfyoqGB5yxsCGUB3Emc937gWfuFfWaBTbQcOsXt9dCSDDEr87IlwBCuo9
-iyseqYUv8I29mZ3OqqdXtQaNGfE84kribVSyOV0CgYAbsee2ZZ1ufzSC5olW9nQh
-5dGELbq4DhlpJg8etWc6GIn87ITOjx4xDSDh75YS8PHDZVjctCWUhgtBzvcYVlpQ
-xZwD+bNYZXcbzzExGIh+YIkfpvY5rqCNRgN84YRYB585949H+rYIXB04Pou5UVuK
-Fnh+zeEVijg18pMvVScrBw==
------END PRIVATE KEY-----
-EOD;
-
//Since PHP 5.2 does not have OpenSSL support on Travis CI, we will test it using the HS256 algorithm
//We also provided PHP 5.2 specific data for it in storage.json
- $newPHP = true;
-
if (version_compare(PHP_VERSION, '5.3.3') <= 0) {
+ // add "5.2" identifier onto the client name
$iss .= ' PHP-5.2';
- $newPHP = false;
- $privateKey = 'mysecretkey';
}
if (!$exp) {
$exp = time() + 1000;
}
- if(!$sub){
+ if (!$sub) {
$sub = "testuser@ourdomain.com";
}
$params = array(
- 'iss' => $iss,
- 'exp' => $exp,
- 'iat' => time(),
- 'sub' => $sub,
- 'aud' => 'http://myapp.com/oauth/auth',
- 'scope' => 'view_friends_list'
- );
+ 'iss' => $iss,
+ 'exp' => $exp,
+ 'iat' => time(),
+ 'sub' => $sub,
+ 'aud' => 'http://myapp.com/oauth/auth',
+ 'scope' => $scope,
+ );
if ($nbf) {
$params['nbf'] = $nbf;
}
+ return $params;
+ }
+
+ private function getJWT($exp = null, $nbf = null, $sub = null, $iss = 'Test Client ID', $scope = null)
+ {
+ $params = $this->getJWTParams($exp, $nbf, $sub, $iss, $scope);
+
$jwtUtil = new OAuth2_Encryption_JWT();
- if ($newPHP){
- return $jwtUtil->encode($params, $privateKey, 'RS256');
- }else{
- return $jwtUtil->encode($params, $privateKey, 'HS256');
+ if (version_compare(PHP_VERSION, '5.3.3') <= 0) {
+ return $jwtUtil->encode($params, 'mysecretkey', 'HS256');
}
+ return $jwtUtil->encode($params, $this->privateKey, 'RS256');
}
private function getTestServer($audience = 'http://myapp.com/oauth/auth')
{
- $storage = new OAuth2_Storage_Memory(json_decode(file_get_contents(dirname(__FILE__).'/../../config/storage.json'), true));
+ $storage = OAuth2_Storage_Bootstrap::getInstance()->getMemoryStorage();
$server = new OAuth2_Server($storage);
$server->addGrantType(new OAuth2_GrantType_JWTBearer($storage, $audience));
diff --git a/test/OAuth2/GrantType/RefreshTokenTest.php b/test/OAuth2/GrantType/RefreshTokenTest.php
index 56af171..e2c818c 100644
--- a/test/OAuth2/GrantType/RefreshTokenTest.php
+++ b/test/OAuth2/GrantType/RefreshTokenTest.php
@@ -14,8 +14,7 @@ class OAuth2_GrantType_RefreshTokenTest extends PHPUnit_Framework_TestCase
'client_id' => 'Test Client ID', // valid client id
'client_secret' => 'TestSecret', // valid client secret
));
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
@@ -33,8 +32,7 @@ class OAuth2_GrantType_RefreshTokenTest extends PHPUnit_Framework_TestCase
'client_secret' => 'TestSecret', // valid client secret
'refresh_token' => 'fake-token', // invalid refresh token
));
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
@@ -52,7 +50,7 @@ class OAuth2_GrantType_RefreshTokenTest extends PHPUnit_Framework_TestCase
'client_secret' => 'TestSecret', // valid client secret
'refresh_token' => 'test-refreshtoken', // valid refresh token
));
- $token = $server->grantAccessToken($request);
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
$this->assertTrue(isset($token['refresh_token']), 'refresh token should always refresh');
$refresh_token = $this->storage->getRefreshToken($token['refresh_token']);
@@ -75,16 +73,102 @@ class OAuth2_GrantType_RefreshTokenTest extends PHPUnit_Framework_TestCase
'client_secret' => 'TestSecret', // valid client secret
'refresh_token' => 'test-refreshtoken', // valid refresh token
));
- $token = $server->grantAccessToken($request);
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
$this->assertFalse(isset($token['refresh_token']), 'refresh token should not be returned');
$used_token = $this->storage->getRefreshToken('test-refreshtoken');
$this->assertNotNull($used_token, 'the refresh token used is still valid');
}
+ public function testValidRefreshTokenSameScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'refresh_token', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'refresh_token' => 'test-refreshtoken-with-scope', // valid refresh token (with scope)
+ 'scope' => 'scope2 scope1',
+ ));
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
+
+ $this->assertNotNull($token);
+ $this->assertArrayHasKey('access_token', $token);
+ $this->assertArrayHasKey('scope', $token);
+ $this->assertEquals($token['scope'], 'scope2 scope1');
+ }
+
+ public function testValidRefreshTokenLessScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'refresh_token', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'refresh_token' => 'test-refreshtoken-with-scope', // valid refresh token (with scope)
+ 'scope' => 'scope1',
+ ));
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
+
+ $this->assertNotNull($token);
+ $this->assertArrayHasKey('access_token', $token);
+ $this->assertArrayHasKey('scope', $token);
+ $this->assertEquals($token['scope'], 'scope1');
+ }
+
+ public function testValidRefreshTokenDifferentScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'refresh_token', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'refresh_token' => 'test-refreshtoken-with-scope', // valid refresh token (with scope)
+ 'scope' => 'scope3',
+ ));
+ $token = $server->grantAccessToken($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 400);
+ $this->assertEquals($response->getParameter('error'), 'invalid_scope');
+ $this->assertEquals($response->getParameter('error_description'), 'An unsupported scope was requested.');
+ }
+
+ public function testValidRefreshTokenInvalidScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'refresh_token', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'refresh_token' => 'test-refreshtoken-with-scope', // valid refresh token (with scope)
+ 'scope' => 'invalid-scope',
+ ));
+ $token = $server->grantAccessToken($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 400);
+ $this->assertEquals($response->getParameter('error'), 'invalid_scope');
+ $this->assertEquals($response->getParameter('error_description'), 'An unsupported scope was requested.');
+ }
+
+ public function testValidClientDifferentRefreshToken()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'refresh_token', // valid grant type
+ 'client_id' => 'Test Some Other Client', // valid client id
+ 'client_secret' => 'TestSecret3', // valid client secret
+ 'refresh_token' => 'test-refreshtoken', // valid refresh token
+ ));
+ $token = $server->grantAccessToken($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 400);
+ $this->assertEquals($response->getParameter('error'), 'invalid_grant');
+ $this->assertEquals($response->getParameter('error_description'), 'refresh_token doesn\'t exist or is invalid for the client');
+ }
+
private function getTestServer()
{
- $this->storage = new OAuth2_Storage_Memory(json_decode(file_get_contents(dirname(__FILE__).'/../../config/storage.json'), true));
+ $this->storage = OAuth2_Storage_Bootstrap::getInstance()->getMemoryStorage();
$server = new OAuth2_Server($this->storage);
return $server;
diff --git a/test/OAuth2/GrantType/UserCredentialsTest.php b/test/OAuth2/GrantType/UserCredentialsTest.php
index 74ad222..ac3234d 100644
--- a/test/OAuth2/GrantType/UserCredentialsTest.php
+++ b/test/OAuth2/GrantType/UserCredentialsTest.php
@@ -11,8 +11,7 @@ class OAuth2_GrantType_UserCredentialsTest extends PHPUnit_Framework_TestCase
'client_secret' => 'TestSecret', // valid client secret
'password' => 'testpass', // valid password
));
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
@@ -28,8 +27,7 @@ class OAuth2_GrantType_UserCredentialsTest extends PHPUnit_Framework_TestCase
'client_secret' => 'TestSecret', // valid client secret
'username' => 'test-username', // valid username
));
- $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_request');
@@ -46,8 +44,7 @@ class OAuth2_GrantType_UserCredentialsTest extends PHPUnit_Framework_TestCase
'username' => 'fake-username', // valid username
'password' => 'testpass', // valid password
));
- $ret = $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $token = $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
@@ -62,19 +59,71 @@ class OAuth2_GrantType_UserCredentialsTest extends PHPUnit_Framework_TestCase
'client_id' => 'Test Client ID', // valid client id
'client_secret' => 'TestSecret', // valid client secret
'username' => 'test-username', // valid username
- 'password' => 'fakepass', // valid password
+ 'password' => 'fakepass', // invalid password
));
- $ret = $server->grantAccessToken($request);
- $response = $server->getResponse();
+ $token = $server->grantAccessToken($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 400);
$this->assertEquals($response->getParameter('error'), 'invalid_grant');
$this->assertEquals($response->getParameter('error_description'), 'Invalid username and password combination');
}
+ public function testValidCredentials()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'password', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'username' => 'test-username', // valid username
+ 'password' => 'testpass', // valid password
+ ));
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
+
+ $this->assertNotNull($token);
+ $this->assertArrayHasKey('access_token', $token);
+ }
+
+ public function testValidCredentialsWithScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'password', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'username' => 'test-username', // valid username
+ 'password' => 'testpass', // valid password
+ 'scope' => 'scope1', // valid scope
+ ));
+ $token = $server->grantAccessToken($request, new OAuth2_Response());
+
+ $this->assertNotNull($token);
+ $this->assertArrayHasKey('access_token', $token);
+ $this->assertArrayHasKey('scope', $token);
+ $this->assertEquals($token['scope'], 'scope1');
+ }
+
+ public function testValidCredentialsInvalidScope()
+ {
+ $server = $this->getTestServer();
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'password', // valid grant type
+ 'client_id' => 'Test Client ID', // valid client id
+ 'client_secret' => 'TestSecret', // valid client secret
+ 'username' => 'test-username', // valid username
+ 'password' => 'testpass', // valid password
+ 'scope' => 'invalid-scope',
+ ));
+ $token = $server->grantAccessToken($request, $response = new OAuth2_Response());
+
+ $this->assertEquals($response->getStatusCode(), 400);
+ $this->assertEquals($response->getParameter('error'), 'invalid_scope');
+ $this->assertEquals($response->getParameter('error_description'), 'An unsupported scope was requested.');
+ }
+
private function getTestServer()
{
- $storage = new OAuth2_Storage_Memory(json_decode(file_get_contents(dirname(__FILE__).'/../../config/storage.json'), true));
+ $storage = OAuth2_Storage_Bootstrap::getInstance()->getMemoryStorage();
$server = new OAuth2_Server($storage);
$server->addGrantType(new OAuth2_GrantType_UserCredentials($storage));
diff --git a/test/OAuth2/RequestTest.php b/test/OAuth2/RequestTest.php
index 36c510e..3d8319a 100644
--- a/test/OAuth2/RequestTest.php
+++ b/test/OAuth2/RequestTest.php
@@ -8,11 +8,11 @@ class OAuth2_RequestTest extends PHPUnit_Framework_TestCase
$server = $this->getTestServer();
// Smoke test for override request class
- // $response = $server->handleTokenRequest($request);
+ // $server->handleTokenRequest($request, $response = new OAuth2_Response());
// $this->assertInstanceOf('OAuth2_Response', $response);
- // $response = $server->handleAuthorizeRequest($request, true);
+ // $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
// $this->assertInstanceOf('OAuth2_Response', $response);
- // $response = $server->verifyResourceRequest($request);
+ // $response = $server->verifyResourceRequest($request, $response = new OAuth2_Response());
// $this->assertTrue(is_bool($response));
/*** make some valid requests ***/
@@ -24,7 +24,7 @@ class OAuth2_RequestTest extends PHPUnit_Framework_TestCase
'client_secret' => 'TestSecret',
'code' => 'testcode',
));
- $response = $server->handleTokenRequest($request);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
$this->assertEquals($response->getStatusCode(), 200);
$this->assertNull($response->getParameter('error'));
$this->assertNotNUll($response->getParameter('access_token'));
diff --git a/test/OAuth2/Util/ScopeTest.php b/test/OAuth2/ScopeTest.php
index 36d0c68..36d0c68 100644
--- a/test/OAuth2/Util/ScopeTest.php
+++ b/test/OAuth2/ScopeTest.php
diff --git a/test/OAuth2/ServerTest.php b/test/OAuth2/ServerTest.php
index b83cf7e..7d8e35a 100644
--- a/test/OAuth2/ServerTest.php
+++ b/test/OAuth2/ServerTest.php
@@ -5,7 +5,7 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
/**
* @expectedException LogicException OAuth2_Storage_ClientInterface
**/
- public function testGetAuthorizeServerWithNoClientStorageThrowsException()
+ public function testGetAuthorizeControllerWithNoClientStorageThrowsException()
{
// must set Client Storage
$server = new OAuth2_Server();
@@ -15,7 +15,7 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
/**
* @expectedException LogicException OAuth2_Storage_AccessTokenInterface
**/
- public function testGetAuthorizeServerWithNoAccessTokenStorageThrowsException()
+ public function testGetAuthorizeControllerWithNoAccessTokenStorageThrowsException()
{
// must set AccessToken or AuthorizationCode
$server = new OAuth2_Server();
@@ -23,7 +23,7 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
$server->getAuthorizeController();
}
- public function testGetAuthorizeServerWithClientStorageAndAccessTokenResponseType()
+ public function testGetAuthorizeControllerWithClientStorageAndAccessTokenResponseType()
{
// must set AccessToken or AuthorizationCode
$server = new OAuth2_Server();
@@ -33,7 +33,7 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
$this->assertNotNull($server->getAuthorizeController());
}
- public function testGetAuthorizeServerWithClientStorageAndAuthorizationCodeResponseType()
+ public function testGetAuthorizeControllerWithClientStorageAndAuthorizationCodeResponseType()
{
// must set AccessToken or AuthorizationCode
$server = new OAuth2_Server();
@@ -43,7 +43,7 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
$this->assertNotNull($server->getAuthorizeController());
}
- public function testGetAuthorizeServerWithClientStorageAndAccessTokenStorage()
+ public function testGetAuthorizeControllerWithClientStorageAndAccessTokenStorage()
{
// must set AccessToken or AuthorizationCode
$server = new OAuth2_Server();
@@ -53,7 +53,7 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
$this->assertNotNull($server->getAuthorizeController());
}
- public function testGetAuthorizeServerWithClientStorageAndAuthorizationCodeStorage()
+ public function testGetAuthorizeControllerWithClientStorageAndAuthorizationCodeStorage()
{
// must set AccessToken or AuthorizationCode
$server = new OAuth2_Server();
@@ -64,25 +64,35 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
}
/**
+ * @expectedException LogicException grant_types
+ **/
+ public function testGetTokenControllerWithGrantTypeStorageThrowsException()
+ {
+ $server = new OAuth2_Server();
+ $server->getTokenController();
+ }
+
+ /**
* @expectedException LogicException OAuth2_Storage_ClientCredentialsInterface
**/
- public function testGetGrantServerWithNoClientCredentialsStorageThrowsException()
+ public function testGetTokenControllerWithNoClientCredentialsStorageThrowsException()
{
$server = new OAuth2_Server();
+ $server->addStorage($this->getMock('OAuth2_Storage_UserCredentialsInterface'));
$server->getTokenController();
}
/**
* @expectedException LogicException OAuth2_Storage_AccessTokenInterface
**/
- public function testGetGrantServerWithNoAccessTokenStorageThrowsException()
+ public function testGetTokenControllerWithNoAccessTokenStorageThrowsException()
{
$server = new OAuth2_Server();
$server->addStorage($this->getMock('OAuth2_Storage_ClientCredentialsInterface'));
$server->getTokenController();
}
- public function testGetGrantServerWithAccessTokenAndClientCredentialsStorage()
+ public function testGetTokenControllerWithAccessTokenAndClientCredentialsStorage()
{
$server = new OAuth2_Server();
$server->addStorage($this->getMock('OAuth2_Storage_AccessTokenInterface'));
@@ -90,7 +100,7 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
$server->getTokenController();
}
- public function testGetGrantServerAccessTokenStorageAndClientCredentialsStorageAndGrantTypes()
+ public function testGetTokenControllerAccessTokenStorageAndClientCredentialsStorageAndGrantTypes()
{
$server = new OAuth2_Server();
$server->addStorage($this->getMock('OAuth2_Storage_AccessTokenInterface'));
@@ -102,13 +112,13 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
/**
* @expectedException LogicException OAuth2_Storage_AccessTokenInterface
**/
- public function testGetAccessServerWithNoAccessTokenStorageThrowsException()
+ public function testGetResourceControllerWithNoAccessTokenStorageThrowsException()
{
$server = new OAuth2_Server();
$server->getResourceController();
}
- public function testGetAccessServerWithAccessTokenStorage()
+ public function testGetResourceControllerWithAccessTokenStorage()
{
$server = new OAuth2_Server();
$server->addStorage($this->getMock('OAuth2_Storage_AccessTokenInterface'));
@@ -121,7 +131,7 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
public function testAddingStorageWithInvalidClass()
{
$server = new OAuth2_Server();
- $server->addStorage($this->getMock('OAuth2_ScopeInterface'));
+ $server->addStorage(new StdClass());
}
/**
@@ -173,7 +183,7 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
$storage
->expects($this->any())
->method('checkRestrictedGrantType')
- ->will($this->returnValue(TRUE));
+ ->will($this->returnValue(true));
// add with the "code" key explicitly set
$codeType = new OAuth2_ResponseType_AuthorizationCode($storage);
@@ -181,7 +191,7 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
$server->addStorage($storage);
$server->addResponseType($codeType);
$request = new OAuth2_Request(array('response_type' => 'code', 'client_id' => 'some_client', 'redirect_uri' => 'http://example.com'));
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
// the response is successful
$this->assertEquals($response->getStatusCode(), 302);
@@ -194,7 +204,7 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
$codeType = new OAuth2_ResponseType_AuthorizationCode($storage);
$server = new OAuth2_Server(array($storage), array(), array(), array($codeType));
$request = new OAuth2_Request(array('response_type' => 'code', 'client_id' => 'some_client', 'redirect_uri' => 'http://example.com'));
- $response = $server->handleAuthorizeRequest($request, true);
+ $server->handleAuthorizeRequest($request, $response = new OAuth2_Response(), true);
// the response is successful
$this->assertEquals($response->getStatusCode(), 302);
@@ -204,6 +214,30 @@ class OAuth2_ServerTest extends PHPUnit_Framework_TestCase
$this->assertFalse(isset($query['error']));
}
+ public function testCustomClientAssertionType()
+ {
+ $request = OAuth2_Request_TestRequest::createPost(array(
+ 'grant_type' => 'authorization_code',
+ 'client_id' =>'Test Client ID',
+ 'code' => 'testcode',
+ ));
+ // verify the mock clientAssertionType was called as expected
+ $clientAssertionType = $this->getMock('OAuth2_ClientAssertionTypeInterface', array('validateRequest', 'getClientId'));
+ $clientAssertionType
+ ->expects($this->once())
+ ->method('validateRequest')
+ ->will($this->returnValue(true));
+ $clientAssertionType
+ ->expects($this->once())
+ ->method('getClientId')
+ ->will($this->returnValue('Test Client ID'));
+
+ // create mock storage
+ $storage = OAuth2_Storage_Bootstrap::getInstance()->getMemoryStorage();
+ $server = new OAuth2_Server(array($storage), array(), array(), array(), null, null, $clientAssertionType);
+ $server->handleTokenRequest($request, $response = new OAuth2_Response());
+ }
+
/**
* @expectedException InvalidArgumentException OAuth2_ResponseType_AuthorizationCodeInterface
**/
diff --git a/test/OAuth2/StorageTest.php b/test/OAuth2/StorageTest.php
index 0f18cff..26d0886 100644
--- a/test/OAuth2/StorageTest.php
+++ b/test/OAuth2/StorageTest.php
@@ -212,7 +212,7 @@ class OAuth2_StorageTest extends PHPUnit_Framework_TestCase
$this->assertFalse($storage->checkUserCredentials('fakeusername', 'testpass'));
// invalid username
- $this->assertFalse($storage->getUser('fakeusername'));
+ $this->assertFalse($storage->getUserDetails('fakeusername'));
// ensure all properties are set
$user = $storage->getUser('testusername');
@@ -227,15 +227,18 @@ class OAuth2_StorageTest extends PHPUnit_Framework_TestCase
public function provideStorage()
{
+ $memory = OAuth2_Storage_Bootstrap::getInstance()->getMemoryStorage();
$mysql = OAuth2_Storage_Bootstrap::getInstance()->getMysqlPdo();
$sqlite = OAuth2_Storage_Bootstrap::getInstance()->getSqlitePdo();
$mongo = OAuth2_Storage_Bootstrap::getInstance()->getMongo();
+ $redis = OAuth2_Storage_Bootstrap::getInstance()->getRedisStorage();
// will add multiple storage types later
return array(
array($sqlite),
array($mysql),
- array($mongo)
+ array($mongo),
+ array($redis)
);
}
}
diff --git a/test/config/storage.json b/test/config/storage.json
index 967991f..439dcd0 100644
--- a/test/config/storage.json
+++ b/test/config/storage.json
@@ -4,8 +4,20 @@
"client_id": "Test Client ID",
"user_id": "",
"redirect_uri": "",
+ "expires": "9999999999"
+ },
+ "testcode-with-scope": {
+ "client_id": "Test Client ID",
+ "user_id": "",
+ "redirect_uri": "",
"expires": "9999999999",
- "scope": ""
+ "scope": "scope1 scope2"
+ },
+ "testcode-expired": {
+ "client_id": "Test Client ID",
+ "user_id": "",
+ "redirect_uri": "",
+ "expires": "1356998400"
}
},
"client_credentials" : {
@@ -15,6 +27,13 @@
"Test Client ID with Redirect Uri": {
"secret": "TestSecret2",
"redirect_uri": "http://brentertainment.com"
+ },
+ "Test Client ID with Multiple Redirect Uris": {
+ "secret": "TestSecret2",
+ "redirect_uri": "http://brentertainment.com http://morehazards.com"
+ },
+ "Test Some Other Client": {
+ "secret": "TestSecret3"
}
},
"user_credentials" : {
@@ -27,6 +46,13 @@
"user_id": "test-username",
"expires": 99999999900,
"scope": null
+ },
+ "test-refreshtoken-with-scope": {
+ "refresh_token": "test-refreshtoken",
+ "client_id": "Test Client ID",
+ "user_id": "test-username",
+ "expires": 99999999900,
+ "scope": "scope1 scope2"
}
},
"access_tokens" : {
@@ -48,26 +74,31 @@
"scope": "testscope"
}
},
-
"jwt": {
"Test Client ID": {
- "key": "-----BEGIN PUBLIC KEY-----\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAstgzbq1Tyozg6Wn/ugyw\\nng4J2wPOaSJwPWlFFff92K60rQrrCDDUSVeuhC0+Tjn7Lq35gMZrsWazl3oj4KAH BBFZqBPejER0Z/t1wgZka97uooW2BclxUMjL6sFW92BLm2f2Po/SDjKdGRwt0+kd\\nj2MgmdqIDu7KTNLPVS/3zk0Ln+Ett92pbODhUliYHkVFy/ZZtoX66lggLjZXGdBP\\n2pLx/cAR09B0m1QDBvkzGewmF6Hl0gXqKSBQel1vnR8Pp2c8Fjp3sDB65hiStXmf\\na6NpMZBAb4vBp39SyRrbXiNH7GbYoIiBBtgnhVg4zHAG6btsEyAtmVM0aMhn01TK\\nHQIDAQAB\\n-----END PUBLIC KEY-----",
+ "key": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5/SxVlE8gnpFqCxgl2wjhzY7u\ncEi00s0kUg3xp7lVEvgLgYcAnHiWp+gtSjOFfH2zsvpiWm6Lz5f743j/FEzHIO1o\nwR0p4d9pOaJK07d01+RzoQLOIQAgXrr4T1CCWUesncwwPBVCyy2Mw3Nmhmr9MrF8\nUlvdRKBxriRnlP3qJQIDAQAB\n-----END PUBLIC KEY-----",
"subject": "testuser@ourdomain.com"
},
-
"Test Client ID PHP-5.2": {
"key": "mysecretkey",
"subject": "testuser@ourdomain.com"
},
-
"Missing Key Client": {
"key": null,
"subject": "testuser@ourdomain.com"
},
-
"Missing Key Client PHP-5.2": {
"key": null,
"subject": "testuser@ourdomain.com"
}
- }
+ },
+ "client_supported_scopes" : {
+ "Test Client ID" : ["clientscope1", "clientscope2"],
+ "Test Client ID 2" : ["clientscope3"]
+ },
+ "supported_scopes" : [
+ "scope1",
+ "scope2",
+ "scope3"
+ ]
} \ No newline at end of file
diff --git a/test/lib/OAuth2/Storage/Bootstrap.php b/test/lib/OAuth2/Storage/Bootstrap.php
index 92ea4b6..be02a60 100644
--- a/test/lib/OAuth2/Storage/Bootstrap.php
+++ b/test/lib/OAuth2/Storage/Bootstrap.php
@@ -33,6 +33,11 @@ class OAuth2_Storage_Bootstrap
return new OAuth2_Storage_Memory(json_decode(file_get_contents(dirname(__FILE__).'/../../../config/storage.json'), true));
}
+ public function getRedisStorage()
+ {
+ return new OAuth2_Storage_Redis(new OAuth2_Storage_MockRedisClient());
+ }
+
public function getMysqlPdo()
{
if (!$this->mysql) {
diff --git a/test/lib/OAuth2/Storage/MockRedisClient.php b/test/lib/OAuth2/Storage/MockRedisClient.php
new file mode 100644
index 0000000..9ecaa20
--- /dev/null
+++ b/test/lib/OAuth2/Storage/MockRedisClient.php
@@ -0,0 +1,63 @@
+<?php
+class OAuth2_Storage_MockRedisClient
+{
+ public function __construct()
+ {
+ $data = array(
+ 'oauth_clients:oauth_test_client' => array(
+ 'client_id' => 'oauth_test_client',
+ 'client_secret' => "testpass",
+ 'redirect_uri' => '',
+ ),
+ 'oauth_access_tokens:testtoken' => array(
+ 'access_token' => 'testtoken',
+ 'client_id' => "Some Client",
+ 'user_id' => '',
+ 'expires' => 0,
+ 'scope' => ''
+ ),
+ 'oauth_authorization_codes:testcode' => array(
+ 'client_id' => "Some Client",
+ 'authorization_code' => 'testcode',
+ 'user_id' => '',
+ 'redirect_uri' => '',
+ 'expires' => 0,
+ 'scope' => ''
+ ),
+ 'oauth_users:testuser' => array(
+ 'username' => 'testuser',
+ 'password' => "password",
+ 'first_name' => '',
+ 'last_name' => ''
+ )
+ );
+ foreach ( $data as $name => $val ) {
+ $data[$name] = json_encode($val);
+ }
+ $this->data = $data;
+ }
+
+ function get($key)
+ {
+ return isset($this->data[$key]) ? $this->data[$key] : null;
+ }
+
+ function set ($key, $value)
+ {
+ $this->data[$key] = $value;
+ return true;
+ }
+
+ function setex($key, $expires, $value)
+ {
+
+ $this->data[$key] = $value;
+ return true;
+ }
+
+ function expire($key)
+ {
+ unset($this->data[$key]);
+ return true;
+ }
+}