Compare commits

...

86 Commits
4.0.5 ... 4.1.5

Author SHA1 Message Date
Alex Bilbie
c5db707e69 Updated changelog 2016-01-04 19:56:12 +00:00
Alex Bilbie
ed7f78179a Merge pull request #412 from derrabus/symfony-3
Allow Symfony 3.0
2015-12-20 20:38:02 +00:00
Alexander M. Turek
6e92239dd7 Allow Symfony 3.0. 2015-12-11 15:24:13 +01:00
Alex Bilbie
f5d731def9 Updated changelog 2015-11-13 17:52:27 +00:00
Alex Bilbie
03815cec6d Merge pull request #388 from m4tthumphrey/master
Added priority argument
2015-10-13 11:21:52 +01:00
Matt Humphrey
c71dc47459 Added priority argument 2015-10-13 11:16:49 +01:00
Alex Bilbie
3bcd8fc3f8 Removed runs and increased timeout 2015-09-26 11:26:17 +01:00
Alex Bilbie
db6d4e0dc6 Only send data to Scrutinizer only for PHP 5.6 2015-09-26 11:25:26 +01:00
Alex Bilbie
f19189a999 Merge pull request #345 from mpipet/master
Expose parameter passed to exceptions
2015-09-04 08:38:35 +01:00
Alex Bilbie
ec9c91cc11 Update .scrutinizer.yml 2015-09-04 08:37:12 +01:00
Alex Bilbie
c3457107ee Merge pull request #370 from michaelhogg/fix-bug-hmac-encoding
Fix bug: hash_hmac() should output raw binary data, not hexits
2015-09-04 08:36:33 +01:00
Alex Bilbie
a9f61fd3ed Merge pull request #377 from starJammer/master
AuthCodeGrant and RefreshTokenGrant don't require client_secret
2015-09-04 08:29:39 +01:00
Alex Bilbie
b78d8ca1d8 Merge pull request #364 from apollopy/master
Too idealistic. Should allow the client and server have some time difference.
2015-09-04 08:28:14 +01:00
Jerry Saravia
8f82e8ef86 Added test for setRequireClientSecret 2015-09-03 23:16:09 -04:00
Jerry Saravia
d88e01c7dd Making client secret optional during refresh and access token requsets. 2015-09-03 22:50:35 -04:00
Michael Hogg
d21374fb0b Merge remote-tracking branch 'thephpleague/master' into fix-bug-hmac-encoding 2015-09-02 09:50:46 +01:00
Alex Bilbie
31e5f4d33c Merge pull request #368 from apollopy/mac_token_only_header
Mac token only get to header
2015-09-01 14:33:58 +01:00
Alex Bilbie
a773405adf Merge pull request #369 from joaopramos/mac-refresh-tokens
Mac refresh tokens
2015-09-01 14:32:45 +01:00
Alex Bilbie
ccc845b195 Merge pull request #371 from michaelhogg/fix-bug-base64-regex
Fix bug: regex doesn't match all Base64 characters
2015-09-01 14:30:38 +01:00
Alex Bilbie
21cd917892 Merge pull request #372 from michaelhogg/fix-bug-request-uri
Fix bug: incorrect signature parameter
2015-09-01 14:29:41 +01:00
Michael Hogg
a2c418ee07 Fix bug: incorrect signature parameter 2015-08-28 16:41:12 +01:00
Michael Hogg
b220368583 Fix bug: regex doesn't match all Base64 characters 2015-08-28 14:01:22 +01:00
Michael Hogg
2d26c38d6c Update unit test: testDetermineAccessTokenInHeaderValid() 2015-08-28 13:11:20 +01:00
Michael Hogg
eeaa68400f Fix bug: hash_hmac() should output raw binary data, not hexits 2015-08-28 12:46:53 +01:00
joao
56c73d2427 ISSUE #356: added the refresh token to the mac token type response 2015-08-28 10:40:13 +00:00
joao
f632fcc997 ISSUE #356: added the refresh token to the mac token type response 2015-08-28 10:38:45 +00:00
ApolloPY
618d84ddcf Mac token only get to header 2015-08-22 01:47:59 +08:00
apollopy
ace42e89e0 change to 300 seconds 2015-08-21 20:02:42 +08:00
ApolloPY
c496df98e4 Too idealistic. Should allow the client and server have some time difference. 2015-08-21 17:17:51 +08:00
Alex Bilbie
2496653968 Merge pull request #342 from gaomd/master
Fix #328, strict check Bearer token
2015-08-21 09:00:02 +01:00
Alex Bilbie
abf66ef9c8 Merge pull request #346 from Korri/Korri-patch-removed-duplicate-routing
Removed duplicate routing setup code
2015-08-21 08:59:35 +01:00
Alex Bilbie
4b9ec488f4 Merge pull request #352 from daveblake/master
Fix typo in docblock
2015-07-13 21:55:43 +01:00
DavidBlake
726d879607 Fix typo in docblock 2015-06-18 13:27:58 +01:00
Mathieu Pipet
b256195421 Expose parameter passed to exceptions 2015-06-09 17:42:25 +02:00
Mathieu Pipet
c84ea1ea62 Expose parameter passed to exceptions 2015-06-09 17:30:13 +02:00
Hugo Vacher
16685ccde4 Removed duplicate routing setup code 2015-06-08 15:50:55 -04:00
Mengdi Gao
7934c7bb53 Fix #328, strict check Bearer token 2015-06-01 21:36:44 +08:00
Alex Bilbie
c174b6fc65 Merge pull request #341 from thephpleague/philsturgeon-patch-1
Added integration list to readme
2015-05-20 13:22:42 +01:00
Phil Sturgeon
75ced70248 Added integration list to readme
I figure it's a good idea to let people know where they can find their bridge packages to save em looking or building their own framework specific nonsense.
2015-05-17 13:33:50 -04:00
Alex Bilbie
5b7fdaeece Merge pull request #330 from jakeasmith/patch-1
Just a typo fix
2015-04-16 17:48:00 +01:00
Jake A. Smith
430a752315 Just a typo fix 2015-04-16 10:41:37 -05:00
Alex Bilbie
810544ec0a Changelog update 2015-03-22 23:32:45 +00:00
Alex Bilbie
34a6b66b8c More .travis.yml updates 2015-03-22 23:19:36 +00:00
Alex Bilbie
61738a7fe2 Added fast_finish: true to .travis.yml 2015-03-22 23:13:41 +00:00
Alex Bilbie
51184259d1 Merge pull request #323 from rdohms/interface-docs
Updated Interface Docs
2015-03-20 11:43:47 +00:00
rdohms
b21de11429 Updated Interface Docs
Made phpdocs match expectations like null when not found and using array notation for indicating array of <object>
2015-03-20 11:33:03 +01:00
Alex Bilbie
cf6e86c9d4 Merge pull request #319 from Fuxy22/patch-1
Fixed missing session scope
2015-03-13 11:03:05 +00:00
Alex Bilbie
f6fdbc7142 Added PHP 7.0 testing 2015-03-03 22:00:47 +00:00
Norbert Fuksz
7f7f45662a Fixed missing session scope
Close #297
2015-03-02 17:47:48 +00:00
Alex Bilbie
f92a68cc72 Merge branch 'master' of github.com:thephpleague/oauth2-server 2015-02-22 19:47:44 +00:00
Alex Bilbie
295d8ffa24 Updated league/event to ~2.1. Fixes #311 2015-02-22 19:47:27 +00:00
Alex Bilbie
3d08140651 Merge pull request #300 from vvllaadd/patch-2
Probable bug
2015-02-22 19:45:14 +00:00
Alex Bilbie
ec8a8393ee Merge pull request #310 from ismailbaskin/master
typo
2015-02-10 10:03:53 +00:00
Ismail BASKIN
3869b8f406 typo 2015-02-10 10:28:57 +02:00
Alex Bilbie
7da7484008 Added security section 2015-02-05 16:14:59 +00:00
Alex Bilbie
b42ba4af17 Merge pull request #303 from hannesvdvreken/fix/consistent-use-and-fqcn
Boyscouting the php docs to always use FQCNs
2015-01-23 10:47:26 +00:00
Hannes Van De Vreken
dd795a82f4 Changed the order and added missing throws 2015-01-23 11:21:12 +01:00
Hannes Van De Vreken
166362d3cd Boyscouting the php docs to always use FQCNs 2015-01-23 11:17:19 +01:00
Vlad
d43391564c Probable bug
AccessTokenStorage::delete should delete the token, not the scope associated with the token
2015-01-15 14:20:54 +01:00
Alex Bilbie
ea6edf572a Changelog update 2015-01-01 12:56:20 +00:00
Alex Bilbie
19b64c2e65 Merge pull request #290 from sarciszewski/patch-1
Remove side-effects in hash_equals()
2015-01-01 12:52:03 +00:00
Scott Arciszewski
612775466c Remove side-effects in hash_equals()
This is functionally identical, but without the side-effect of defining a function in the current namespace.

Also, it uses absolute function reference (`\hash_equals` instead of `hash_equals`) because if someone defined `League\OAuth2\Server\TokenType\hash_equals()` elsewhere, it would try that first.

Kudos for using `hash_equals()` in your original design for this feature. Many OAuth2 implementations neglect this nuance :)
2015-01-01 01:34:22 -05:00
Alex Bilbie
740ea24e08 Changelog update 2014-12-31 16:03:26 +00:00
Alex Bilbie
e1c14abf6c Lowered symfony/http-foundation to ~2.4 so Laravel can use it 2014-12-31 15:51:52 +00:00
Alex Bilbie
d1aae27359 Version bump 2014-12-27 23:01:11 +00:00
Alex Bilbie
80aeaf9200 Merge branch 'Symplicity-master' into release/4.1.0 2014-12-27 23:00:17 +00:00
Alex Bilbie
282bb20cc8 Fix docblocks + method name 2014-12-27 23:00:11 +00:00
Alex Bilbie
b727be55a2 Merge branch 'master' of https://github.com/Symplicity/oauth2-server into Symplicity-master 2014-12-27 22:57:08 +00:00
Alex Bilbie
cf80a2d6ce README update 2014-12-27 22:55:30 +00:00
Alex Bilbie
72a5c1794a Remove unused namespace 2014-12-27 22:50:13 +00:00
Alex Bilbie
707c85b0d6 Fixes and tests 2014-12-27 22:26:31 +00:00
Alex Bilbie
c56562b0b8 PSR fixes 2014-12-27 21:38:01 +00:00
Alex Bilbie
d0b2498b43 Ignore PHPStorm 2014-12-27 21:35:45 +00:00
Alex Bilbie
17be6f4549 Added MacTokenInterface 2014-12-27 21:35:45 +00:00
Alex Bilbie
b50fbff1e3 Update docblock 2014-12-27 21:35:45 +00:00
Alex Bilbie
7375a348c6 PHP code fix 2014-12-27 21:35:45 +00:00
Alex Bilbie
ae5dd9ce65 Added MAC TokenType 2014-12-27 21:35:45 +00:00
Alex Bilbie
f9e56ff62a Added MAC storage getter and setter 2014-12-27 21:35:45 +00:00
Alex Bilbie
1bcf7ee20f Update .travis.yml 2014-12-26 17:03:35 +00:00
Alex Bilbie
bee9c6a51d Added Gitter.im 2014-12-26 16:59:09 +00:00
Dave Walker
851c7c0eb1 Per the spec:
The authorization server MAY issue a new refresh token, in which case
   the client MUST discard the old refresh token and replace it with the
   new refresh token.  The authorization server MAY revoke the old
   refresh token after issuing a new refresh token to the client.  If a
   new refresh token is issued, the refresh token scope MUST be
   identical to that of the refresh token included by the client in the
   request.

This commit allows users to specifiy the time before the Refresh Token
expire time to issue a new Refresh Token.

alter method names, naming convention(?)
2014-12-21 18:51:52 -05:00
Alex Bilbie
7fff4a8fe8 Merge pull request #280 from danprime/master
Fix Example Init Code
2014-12-17 10:10:50 +00:00
Alex Bilbie
44ac01ee0e Merge pull request #284 from mortenhauberg/fix-misspelling
Changed "paremter" to "parameter"
2014-12-16 19:48:40 +00:00
mortenhauberg
60bd334b46 Changed "paremter" to "parameter" 2014-12-16 19:04:03 +01:00
Daniel Tse
2653a174bb Update init.php 2014-12-12 10:25:52 -07:00
Daniel Tse
676fb4c06a Fix column declarations and references so that foreign keys and references work. 2014-12-11 15:50:42 -07:00
33 changed files with 795 additions and 76 deletions

3
.gitignore vendored
View File

@@ -11,4 +11,5 @@
/tests/codecept/tests/_log
oauth2-server.paw
/output_*/
/_site
/_site
.idea

View File

@@ -21,8 +21,7 @@ checks:
fix_doc_comments: true
tools:
external_code_coverage:
timeout: 600
runs: 3
timeout: 1800
php_code_coverage: false
php_code_sniffer:
config:
@@ -34,4 +33,4 @@ tools:
excluded_dirs: [vendor, tests, examples]
php_cpd:
enabled: true
excluded_dirs: [vendor, tests, examples]
excluded_dirs: [vendor, tests, examples]

View File

@@ -1,10 +1,22 @@
language: php
sudo: false
cache:
directories:
- vendor
php:
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
allow_failures:
- php: 7.0
fast_finish: true
install:
- travis_retry composer install --no-interaction --prefer-source
@@ -14,8 +26,8 @@ script:
- phpunit --coverage-text --verbose --coverage-clover=coverage.clover --coverage-html coverage
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover
- bash -c 'if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi;
- bash -c 'if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi;
- git config --global user.email "travis@travis-ci.org"
- git config --global user.name "TravisCI"
- cp -R coverage ${HOME}/coverage
@@ -33,4 +45,12 @@ branches:
- master
env:
global:
secure: "C4wD/BQefKSu9W594iyLp+IBCjlM8kKlmp+nXKXnZGi0L8IkV3m4mmNOb8PExxGMhZ3mlev5DnU4Uoh4oJaUxnkR1FpX4dSEpyzU3VknUzSE2yZOlL+bdCw3o85TGoCcp/+ReJCOw5sncxTskJKHlW1YMa33FznaXwLNoImpjTg="
secure: "C4wD/BQefKSu9W594iyLp+IBCjlM8kKlmp+nXKXnZGi0L8IkV3m4mmNOb8PExxGMhZ3mlev5DnU4Uoh4oJaUxnkR1FpX4dSEpyzU3VknUzSE2yZOlL+bdCw3o85TGoCcp/+ReJCOw5sncxTskJKHlW1YMa33FznaXwLNoImpjTg="
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/7de0ca12596cd5268f30
on_success: always # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false

View File

@@ -1,5 +1,51 @@
# Changelog
## 4.1.5 (released 2016-01-04)
* Enable Symfony 3.0 support (#412)
## 4.1.4 (released 2015-11-13)
* Fix for determining access token in header (Issue #328)
* Refresh tokens are now returned for MAC responses (Issue #356)
* Added integration list to readme (Issue #341)
* Expose parameter passed to exceptions (Issue #345)
* Removed duplicate routing setup code (Issue #346)
* Docs fix (Issues #347, #360, #380)
* Examples fix (Issues #348, #358)
* Fix typo in docblock (Issue #352)
* Improved timeouts for MAC tokens (Issue #364)
* `hash_hmac()` should output raw binary data, not hexits (Issue #370)
* Improved regex for matching all Base64 characters (Issue #371)
* Fix incorrect signature parameter (Issue #372)
* AuthCodeGrant and RefreshTokenGrant don't require client_secret (Issue #377)
* Added priority argument to event listener (Issue #388)
## 4.1.3 (released 2015-03-22)
* Docblock, namespace and inconsistency fixes (Issue #303)
* Docblock type fix (Issue #310)
* Example bug fix (Issue #300)
* Updated league/event to ~2.1 (Issue #311)
* Fixed missing session scope (Issue #319)
* Updated interface docs (Issue #323)
* `.travis.yml` updates
## 4.1.2 (released 2015-01-01)
* Remove side-effects in hash_equals() implementation (Issue #290)
## 4.1.1 (released 2014-12-31)
* Changed `symfony/http-foundation` dependency version to `~2.4` so package can be installed in Laravel `4.1.*`
## 4.1.0 (released 2014-12-27)
* Added MAC token support (Issue #158)
* Fixed example init code (Issue #280)
* Toggle refresh token rotation (Issue #286)
* Docblock fixes
## 4.0.5 (released 2014-12-15)
* Prevent duplicate session in auth code grant (Issue #282)

View File

@@ -5,7 +5,7 @@
[![Build Status](https://img.shields.io/travis/thephpleague/oauth2-server/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/oauth2-server)
[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/oauth2-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-server/code-structure)
[![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/oauth2-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-server)
[![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-server.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-server)
[![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-server.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-server) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/thephpleague/oauth2-server?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
A standards compliant [OAuth 2.0](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-v2/) authorization server and resource server written in PHP which makes working with OAuth 2.0 trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them.
@@ -22,9 +22,11 @@ You can also define your own grants.
In addition it supports the following token types:
* Bearer tokens
* MAC tokens (coming soon)
* MAC tokens
* JSON web tokens (coming soon)
You can also create you own tokens.
## Requirements
@@ -49,10 +51,19 @@ Contribute to this documentation in the [gh-pages branch](https://github.com/the
Please see [CONTRIBUTING](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) for details.
## Integration
- [CakePHP 3](https://github.com/uafrica/oauth-server)
- [Laravel](https://github.com/lucadegasperi/oauth2-server-laravel)
## Support
Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague/oauth2-server/issues)
## Security
If you discover any security related issues, please email hello@alexbilbie.com instead of using the issue tracker.
## License
This package is released under the MIT License. See the bundled [LICENSE](https://github.com/thephpleague/oauth2-server/blob/master/LICENSE) file for details.

View File

@@ -5,8 +5,8 @@
"license": "MIT",
"require": {
"php": ">=5.4.0",
"symfony/http-foundation": "~2.5",
"league/event": "1.0.*"
"symfony/http-foundation": "~2.4|~3.0",
"league/event": "~2.1"
},
"require-dev": {
"phpunit/phpunit": "4.3.*",

View File

@@ -86,7 +86,7 @@ class AccessTokenStorage extends AbstractStorage implements AccessTokenInterface
*/
public function delete(AccessTokenEntity $token)
{
Capsule::table('oauth_access_token_scopes')
Capsule::table('oauth_access_tokens')
->where('access_token', $token->getId())
->delete();
}

View File

@@ -9,11 +9,6 @@ use RelationalExample\Storage;
include __DIR__.'/vendor/autoload.php';
// Routing setup
$request = (new Request())->createFromGlobals();
$router = new \Orno\Route\RouteCollection();
$router->setStrategy(\Orno\Route\RouteStrategyInterface::RESTFUL_STRATEGY);
// Set up the OAuth 2.0 resource server
$sessionStorage = new Storage\SessionStorage();
$accessTokenStorage = new Storage\AccessTokenStorage();

View File

@@ -102,7 +102,7 @@ Capsule::table('oauth_scopes')->insert([
print 'Creating sessions table'.PHP_EOL;
Capsule::schema()->create('oauth_sessions', function ($table) {
$table->increments('id');
$table->increments('id')->unsigned();
$table->string('owner_type');
$table->string('owner_id');
$table->string('client_id');
@@ -135,7 +135,7 @@ print 'Creating access tokens table'.PHP_EOL;
Capsule::schema()->create('oauth_access_tokens', function ($table) {
$table->string('access_token')->primary();
$table->integer('session_id');
$table->integer('session_id')->unsigned();
$table->integer('expire_time');
$table->foreign('session_id')->references('id')->on('oauth_sessions')->onDelete('cascade');
@@ -168,7 +168,7 @@ Capsule::schema()->create('oauth_refresh_tokens', function ($table) {
$table->integer('expire_time');
$table->string('access_token');
$table->foreign('access_token')->references('id')->on('oauth_access_tokens')->onDelete('cascade');
$table->foreign('access_token')->references('access_token')->on('oauth_access_tokens')->onDelete('cascade');
});
/******************************************************************************/
@@ -177,7 +177,7 @@ print 'Creating auth codes table'.PHP_EOL;
Capsule::schema()->create('oauth_auth_codes', function ($table) {
$table->string('auth_code')->primary();
$table->integer('session_id');
$table->integer('session_id')->unsigned();
$table->integer('expire_time');
$table->string('client_redirect_uri');
@@ -189,7 +189,7 @@ Capsule::schema()->create('oauth_auth_codes', function ($table) {
print 'Creating oauth access token scopes table'.PHP_EOL;
Capsule::schema()->create('oauth_access_token_scopes', function ($table) {
$table->increments('id');
$table->increments('id')->unsigned();
$table->string('access_token');
$table->string('scope');
@@ -240,8 +240,8 @@ Capsule::schema()->create('oauth_auth_code_scopes', function ($table) {
print 'Creating oauth session scopes table'.PHP_EOL;
Capsule::schema()->create('oauth_session_scopes', function ($table) {
$table->increments('id');
$table->string('session_id');
$table->increments('id')->unsigned();
$table->integer('session_id')->unsigned();
$table->string('scope');
$table->foreign('session_id')->references('id')->on('oauth_sessions')->onDelete('cascade');

View File

@@ -15,6 +15,7 @@ use League\Event\Emitter;
use League\OAuth2\Server\Storage\AccessTokenInterface;
use League\OAuth2\Server\Storage\AuthCodeInterface;
use League\OAuth2\Server\Storage\ClientInterface;
use League\OAuth2\Server\Storage\MacTokenInterface;
use League\OAuth2\Server\Storage\RefreshTokenInterface;
use League\OAuth2\Server\Storage\ScopeInterface;
use League\OAuth2\Server\Storage\SessionInterface;
@@ -75,6 +76,11 @@ abstract class AbstractServer
*/
protected $clientStorage;
/**
* @var \League\OAuth2\Server\Storage\MacTokenInterface
*/
protected $macStorage;
/**
* Token type
*
@@ -116,10 +122,11 @@ abstract class AbstractServer
*
* @param string $eventName Event name
* @param callable $listener Callable function or method
* @param int $priority Priority of event listener
*/
public function addEventListener($eventName, callable $listener)
public function addEventListener($eventName, callable $listener, $priority = Emitter::P_NORMAL)
{
$this->eventEmitter->addListener($eventName, $listener);
$this->eventEmitter->addListener($eventName, $listener, $priority);
}
/**
@@ -332,4 +339,20 @@ abstract class AbstractServer
{
return $this->tokenType;
}
/**
* @return MacTokenInterface
*/
public function getMacStorage()
{
return $this->macStorage;
}
/**
* @param MacTokenInterface $macStorage
*/
public function setMacStorage(MacTokenInterface $macStorage)
{
$this->macStorage = $macStorage;
}
}

View File

@@ -20,7 +20,7 @@ use League\OAuth2\Server\TokenType\Bearer;
class AuthorizationServer extends AbstractServer
{
/**
* The delimeter between scopes specified in the scope query string parameter
* The delimiter between scopes specified in the scope query string parameter
* The OAuth 2 specification states it should be a space but most use a comma
*
* @var string
@@ -181,7 +181,7 @@ class AuthorizationServer extends AbstractServer
}
/**
* Require the "state" paremter in checkAuthoriseParams()
* Require the "state" parameter in checkAuthoriseParams()
*
* @return bool
*/
@@ -191,7 +191,7 @@ class AuthorizationServer extends AbstractServer
}
/**
* Require the "state" paremter in checkAuthoriseParams()
* Require the "state" parameter in checkAuthoriseParams()
*
* @param boolean $require
*

View File

@@ -12,7 +12,7 @@
namespace League\OAuth2\Server\Entity;
/**
* Access token entity class
* Auth Code entity class
*/
class AuthCodeEntity extends AbstractTokenEntity
{

View File

@@ -32,6 +32,7 @@ class InvalidGrantException extends OAuthException
public function __construct($parameter)
{
$this->parameter = $parameter;
parent::__construct(
sprintf(
'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. Check the "%s" parameter.',

View File

@@ -32,6 +32,7 @@ class InvalidRequestException extends OAuthException
public function __construct($parameter, $redirectUri = null)
{
$this->parameter = $parameter;
parent::__construct(
sprintf(
'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "%s" parameter.',

View File

@@ -32,6 +32,7 @@ class InvalidScopeException extends OAuthException
public function __construct($parameter, $redirectUri = null)
{
$this->parameter = $parameter;
parent::__construct(
sprintf(
'The requested scope is invalid, unknown, or malformed. Check the "%s" scope.',

View File

@@ -36,6 +36,11 @@ class OAuthException extends \Exception
*/
public $errorType = '';
/**
* Parameter eventually passed to Exception
*/
public $parameter = '';
/**
* Throw a new exception
*
@@ -72,6 +77,16 @@ class OAuthException extends \Exception
);
}
/**
* Return parameter if set
*
* @return string
*/
public function getParameter()
{
return $this->parameter;
}
/**
* Get all headers that have to be send with the error response
*

View File

@@ -31,7 +31,9 @@ class ServerErrorException extends OAuthException
*/
public function __construct($parameter = null)
{
$this->parameter = $parameter;
$parameter = is_null($parameter) ? 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.' : $parameter;
parent::__construct($parameter);
}
}

View File

@@ -32,6 +32,7 @@ class UnsupportedGrantTypeException extends OAuthException
public function __construct($parameter)
{
$this->parameter = $parameter;
parent::__construct(
sprintf(
'The authorization grant type "%s" is not supported by the authorization server.',

View File

@@ -31,6 +31,7 @@ class UnsupportedResponseTypeException extends OAuthException
*/
public function __construct($parameter, $redirectUri = null)
{
$this->parameter = $parameter;
parent::__construct('The authorization server does not support obtaining an access token using this method.');
$this->redirectUri = $redirectUri;
}

View File

@@ -60,6 +60,14 @@ class AuthCodeGrant extends AbstractGrant
*/
protected $authTokenTTL = 600;
/**
* Whether to require the client secret when
* completing the flow.
*
* @var boolean
*/
protected $requireClientSecret = true;
/**
* Override the default access token expire time
*
@@ -72,6 +80,27 @@ class AuthCodeGrant extends AbstractGrant
$this->authTokenTTL = $authTokenTTL;
}
/**
*
* @param bool $required True to require client secret during access
* token request. False if not. Default = true
*/
public function setRequireClientSecret($required)
{
$this->requireClientSecret = $required;
}
/**
* True if client secret is required during
* access token request. False if it isn't.
*
* @return bool
*/
public function shouldRequireClientSecret()
{
return $this->requireClientSecret;
}
/**
* Check authorize parameters
*
@@ -148,7 +177,6 @@ class AuthCodeGrant extends AbstractGrant
$session = new SessionEntity($this->server);
$session->setOwner($type, $typeId);
$session->associateClient($authParams['client']);
$session->save();
// Create a new auth code
$authCode = new AuthCodeEntity($this->server);
@@ -158,8 +186,10 @@ class AuthCodeGrant extends AbstractGrant
foreach ($authParams['scopes'] as $scope) {
$authCode->associateScope($scope);
$session->associateScope($scope);
}
$session->save();
$authCode->setSession($session);
$authCode->save();
@@ -183,7 +213,7 @@ class AuthCodeGrant extends AbstractGrant
$clientSecret = $this->server->getRequest()->request->get('client_secret',
$this->server->getRequest()->getPassword());
if (is_null($clientSecret)) {
if ($this->shouldRequireClientSecret() && is_null($clientSecret)) {
throw new Exception\InvalidRequestException('client_secret');
}
@@ -270,4 +300,4 @@ class AuthCodeGrant extends AbstractGrant
return $this->server->getTokenType()->generateResponse();
}
}
}

View File

@@ -19,7 +19,7 @@ use League\OAuth2\Server\Exception;
use League\OAuth2\Server\Util\SecureKey;
/**
* Referesh token grant
* Refresh token grant
*/
class RefreshTokenGrant extends AbstractGrant
{
@@ -35,6 +35,21 @@ class RefreshTokenGrant extends AbstractGrant
*/
protected $refreshTokenTTL = 604800;
/**
* Rotate token (default = true)
*
* @var integer
*/
protected $refreshTokenRotate = true;
/**
* Whether to require the client secret when
* completing the flow.
*
* @var boolean
*/
protected $requireClientSecret = true;
/**
* Set the TTL of the refresh token
*
@@ -57,6 +72,47 @@ class RefreshTokenGrant extends AbstractGrant
return $this->refreshTokenTTL;
}
/**
* Set the rotation boolean of the refresh token
* @param bool $refreshTokenRotate
*/
public function setRefreshTokenRotation($refreshTokenRotate = true)
{
$this->refreshTokenRotate = $refreshTokenRotate;
}
/**
* Get rotation boolean of the refresh token
*
* @return bool
*/
public function shouldRotateRefreshTokens()
{
return $this->refreshTokenRotate;
}
/**
*
* @param bool $required True to require client secret during access
* token request. False if not. Default = true
*/
public function setRequireClientSecret($required)
{
$this->requireClientSecret = $required;
}
/**
* True if client secret is required during
* access token request. False if it isn't.
*
* @return bool
*/
public function shouldRequireClientSecret()
{
return $this->requireClientSecret;
}
/**
* {@inheritdoc}
*/
@@ -69,7 +125,7 @@ class RefreshTokenGrant extends AbstractGrant
$clientSecret = $this->server->getRequest()->request->get('client_secret',
$this->server->getRequest()->getPassword());
if (is_null($clientSecret)) {
if ($this->shouldRequireClientSecret() && is_null($clientSecret)) {
throw new Exception\InvalidRequestException('client_secret');
}
@@ -146,18 +202,22 @@ class RefreshTokenGrant extends AbstractGrant
$this->server->getTokenType()->setParam('access_token', $newAccessToken->getId());
$this->server->getTokenType()->setParam('expires_in', $this->getAccessTokenTTL());
// Expire the old refresh token
$oldRefreshToken->expire();
if ($this->shouldRotateRefreshTokens()) {
// Expire the old refresh token
$oldRefreshToken->expire();
// Generate a new refresh token
$newRefreshToken = new RefreshTokenEntity($this->server);
$newRefreshToken->setId(SecureKey::generate());
$newRefreshToken->setExpireTime($this->getRefreshTokenTTL() + time());
$newRefreshToken->setAccessToken($newAccessToken);
$newRefreshToken->save();
// Generate a new refresh token
$newRefreshToken = new RefreshTokenEntity($this->server);
$newRefreshToken->setId(SecureKey::generate());
$newRefreshToken->setExpireTime($this->getRefreshTokenTTL() + time());
$newRefreshToken->setAccessToken($newAccessToken);
$newRefreshToken->save();
$this->server->getTokenType()->setParam('refresh_token', $newRefreshToken->getId());
$this->server->getTokenType()->setParam('refresh_token', $newRefreshToken->getId());
} else {
$this->server->getTokenType()->setParam('refresh_token', $oldRefreshToken->getId());
}
return $this->server->getTokenType()->generateResponse();
}
}
}

View File

@@ -12,11 +12,14 @@
namespace League\OAuth2\Server;
use League\OAuth2\Server\Entity\AccessTokenEntity;
use League\OAuth2\Server\Exception\AccessDeniedException;
use League\OAuth2\Server\Exception\InvalidRequestException;
use League\OAuth2\Server\Storage\AccessTokenInterface;
use League\OAuth2\Server\Storage\ClientInterface;
use League\OAuth2\Server\Storage\ScopeInterface;
use League\OAuth2\Server\Storage\SessionInterface;
use League\OAuth2\Server\TokenType\Bearer;
use League\OAuth2\Server\TokenType\MAC;
/**
* OAuth 2.0 Resource Server
@@ -40,10 +43,10 @@ class ResourceServer extends AbstractServer
/**
* Initialise the resource server
*
* @param SessionInterface $sessionStorage
* @param AccessTokenInterface $accessTokenStorage
* @param ClientInterface $clientStorage
* @param ScopeInterface $scopeStorage
* @param \League\OAuth2\Server\Storage\SessionInterface $sessionStorage
* @param \League\OAuth2\Server\Storage\AccessTokenInterface $accessTokenStorage
* @param \League\OAuth2\Server\Storage\ClientInterface $clientStorage
* @param \League\OAuth2\Server\Storage\ScopeInterface $scopeStorage
*
* @return self
*/
@@ -93,31 +96,32 @@ class ResourceServer extends AbstractServer
/**
* Checks if the access token is valid or not
*
* @param bool $headersOnly Limit Access Token to Authorization header only
* @param AccessTokenEntity|null $accessToken Access Token
* @param bool $headerOnly Limit Access Token to Authorization header
* @param \League\OAuth2\Server\Entity\AccessTokenEntity|null $accessToken Access Token
*
* @throws \League\OAuth2\Server\Exception\AccessDeniedException
* @throws \League\OAuth2\Server\Exception\InvalidRequestException
*
* @return bool
*
* @throws
*/
public function isValidRequest($headersOnly = true, $accessToken = null)
public function isValidRequest($headerOnly = true, $accessToken = null)
{
$accessTokenString = ($accessToken !== null)
? $accessToken
: $this->determineAccessToken($headersOnly);
: $this->determineAccessToken($headerOnly);
// Set the access token
$this->accessToken = $this->getAccessTokenStorage()->get($accessTokenString);
// Ensure the access token exists
if (!$this->accessToken instanceof AccessTokenEntity) {
throw new Exception\AccessDeniedException();
throw new AccessDeniedException();
}
// Check the access token hasn't expired
// Ensure the auth code hasn't expired
if ($this->accessToken->isExpired() === true) {
throw new Exception\AccessDeniedException();
throw new AccessDeniedException();
}
return true;
@@ -126,24 +130,24 @@ class ResourceServer extends AbstractServer
/**
* Reads in the access token from the headers
*
* @param bool $headersOnly Limit Access Token to Authorization header only
* @param bool $headerOnly Limit Access Token to Authorization header
*
* @throws Exception\InvalidRequestException Thrown if there is no access token presented
* @throws \League\OAuth2\Server\Exception\InvalidRequestException Thrown if there is no access token presented
*
* @return string
*/
public function determineAccessToken($headersOnly = false)
public function determineAccessToken($headerOnly = false)
{
if ($this->getRequest()->headers->get('Authorization') !== null) {
$accessToken = $this->getTokenType()->determineAccessTokenInHeader($this->getRequest());
} elseif ($headersOnly === false) {
} elseif ($headerOnly === false && (! $this->getTokenType() instanceof MAC)) {
$accessToken = ($this->getRequest()->server->get('REQUEST_METHOD') === 'GET')
? $this->getRequest()->query->get($this->tokenKey)
: $this->getRequest()->request->get($this->tokenKey);
}
if (empty($accessToken)) {
throw new Exception\InvalidRequestException('access token');
throw new InvalidRequestException('access token');
}
return $accessToken;

View File

@@ -24,7 +24,7 @@ interface AccessTokenInterface extends StorageInterface
*
* @param string $token The access token
*
* @return \League\OAuth2\Server\Entity\AccessTokenEntity
* @return \League\OAuth2\Server\Entity\AccessTokenEntity | null
*/
public function get($token);
@@ -33,7 +33,7 @@ interface AccessTokenInterface extends StorageInterface
*
* @param \League\OAuth2\Server\Entity\AccessTokenEntity $token The access token
*
* @return array Array of \League\OAuth2\Server\Entity\ScopeEntity
* @return \League\OAuth2\Server\Entity\ScopeEntity[] Array of \League\OAuth2\Server\Entity\ScopeEntity
*/
public function getScopes(AccessTokenEntity $token);

View File

@@ -24,7 +24,7 @@ interface AuthCodeInterface extends StorageInterface
*
* @param string $code
*
* @return \League\OAuth2\Server\Entity\AuthCodeEntity
* @return \League\OAuth2\Server\Entity\AuthCodeEntity | null
*/
public function get($code);
@@ -45,7 +45,7 @@ interface AuthCodeInterface extends StorageInterface
*
* @param \League\OAuth2\Server\Entity\AuthCodeEntity $token The auth code
*
* @return array Array of \League\OAuth2\Server\Entity\ScopeEntity
* @return \League\OAuth2\Server\Entity\ScopeEntity[] Array of \League\OAuth2\Server\Entity\ScopeEntity
*/
public function getScopes(AuthCodeEntity $token);

View File

@@ -26,7 +26,7 @@ interface ClientInterface extends StorageInterface
* @param string $redirectUri The client's redirect URI (default = "null")
* @param string $grantType The grant type used (default = "null")
*
* @return \League\OAuth2\Server\Entity\ClientEntity
* @return \League\OAuth2\Server\Entity\ClientEntity | null
*/
public function get($clientId, $clientSecret = null, $redirectUri = null, $grantType = null);
@@ -35,7 +35,7 @@ interface ClientInterface extends StorageInterface
*
* @param \League\OAuth2\Server\Entity\SessionEntity $session The session
*
* @return \League\OAuth2\Server\Entity\ClientEntity
* @return \League\OAuth2\Server\Entity\ClientEntity | null
*/
public function getBySession(SessionEntity $session);
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* OAuth 2.0 MAC Token Interface
*
* @package league/oauth2-server
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Storage;
/**
* MacTokenInterface
*/
interface MacTokenInterface extends StorageInterface
{
/**
* Create a MAC key linked to an access token
* @param string $macKey
* @param string $accessToken
* @return void
*/
public function create($macKey, $accessToken);
/**
* Get a MAC key by access token
* @param string $accessToken
* @return string
*/
public function getByAccessToken($accessToken);
}

View File

@@ -23,7 +23,7 @@ interface RefreshTokenInterface extends StorageInterface
*
* @param string $token
*
* @return \League\OAuth2\Server\Entity\RefreshTokenEntity
* @return \League\OAuth2\Server\Entity\RefreshTokenEntity | null
*/
public function get($token);

View File

@@ -23,7 +23,7 @@ interface ScopeInterface extends StorageInterface
* @param string $grantType The grant type used in the request (default = "null")
* @param string $clientId The client sending the request (default = "null")
*
* @return \League\OAuth2\Server\Entity\ScopeEntity
* @return \League\OAuth2\Server\Entity\ScopeEntity | null
*/
public function get($scope, $grantType = null, $clientId = null);
}

View File

@@ -26,7 +26,7 @@ interface SessionInterface extends StorageInterface
*
* @param \League\OAuth2\Server\Entity\AccessTokenEntity $accessToken The access token
*
* @return \League\OAuth2\Server\Entity\SessionEntity
* @return \League\OAuth2\Server\Entity\SessionEntity | null
*/
public function getByAccessToken(AccessTokenEntity $accessToken);
@@ -35,7 +35,7 @@ interface SessionInterface extends StorageInterface
*
* @param \League\OAuth2\Server\Entity\AuthCodeEntity $authCode The auth code
*
* @return \League\OAuth2\Server\Entity\SessionEntity
* @return \League\OAuth2\Server\Entity\SessionEntity | null
*/
public function getByAuthCode(AuthCodeEntity $authCode);
@@ -44,7 +44,7 @@ interface SessionInterface extends StorageInterface
*
* @param \League\OAuth2\Server\Entity\SessionEntity
*
* @return array Array of \League\OAuth2\Server\Entity\ScopeEntity
* @return \League\OAuth2\Server\Entity\ScopeEntity[] Array of \League\OAuth2\Server\Entity\ScopeEntity
*/
public function getScopes(SessionEntity $session);

View File

@@ -38,9 +38,16 @@ class Bearer extends AbstractTokenType implements TokenTypeInterface
*/
public function determineAccessTokenInHeader(Request $request)
{
$header = $request->headers->get('Authorization');
$accessToken = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header));
if ($request->headers->has('Authorization') === false) {
return;
}
return ($accessToken === 'Bearer') ? '' : $accessToken;
$header = $request->headers->get('Authorization');
if (substr($header, 0, 7) !== 'Bearer ') {
return;
}
return trim(substr($header, 7));
}
}

156
src/TokenType/MAC.php Normal file
View File

@@ -0,0 +1,156 @@
<?php
/**
* OAuth 2.0 MAC Token Type
*
* @package league/oauth2-server
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\TokenType;
use League\OAuth2\Server\Util\SecureKey;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
/**
* MAC Token Type
*/
class MAC extends AbstractTokenType implements TokenTypeInterface
{
/**
* {@inheritdoc}
*/
public function generateResponse()
{
$macKey = SecureKey::generate();
$this->server->getMacStorage()->create($macKey, $this->getParam('access_token'));
$response = [
'access_token' => $this->getParam('access_token'),
'token_type' => 'mac',
'expires_in' => $this->getParam('expires_in'),
'mac_key' => $macKey,
'mac_algorithm' => 'hmac-sha-256',
];
if (!is_null($this->getParam('refresh_token'))) {
$response['refresh_token'] = $this->getParam('refresh_token');
}
return $response;
}
/**
* {@inheritdoc}
*/
public function determineAccessTokenInHeader(Request $request)
{
if ($request->headers->has('Authorization') === false) {
return;
}
$header = $request->headers->get('Authorization');
if (substr($header, 0, 4) !== 'MAC ') {
return;
}
// Find all the parameters expressed in the header
$paramsRaw = explode(',', substr($header, 4));
$params = new ParameterBag();
array_map(function ($param) use (&$params) {
$param = trim($param);
preg_match_all('/([a-zA-Z]*)="([\w=\/+]*)"/', $param, $matches);
// @codeCoverageIgnoreStart
if (count($matches) !== 3) {
return;
}
// @codeCoverageIgnoreEnd
$key = reset($matches[1]);
$value = trim(reset($matches[2]));
if (empty($value)) {
return;
}
$params->set($key, $value);
}, $paramsRaw);
// Validate parameters
if ($params->has('id') === false || $params->has('ts') === false || $params->has('nonce') === false || $params->has('mac') === false) {
return;
}
if (abs($params->get('ts') - time()) > 300) {
return;
}
$accessToken = $params->get('id');
$timestamp = (int) $params->get('ts');
$nonce = $params->get('nonce');
$signature = $params->get('mac');
// Try to find the MAC key for the access token
$macKey = $this->server->getMacStorage()->getByAccessToken($accessToken);
if ($macKey === null) {
return;
}
// Calculate and compare the signature
$calculatedSignatureParts = [
$timestamp,
$nonce,
strtoupper($request->getMethod()),
$request->getRequestUri(),
$request->getHost(),
$request->getPort(),
];
if ($params->has('ext')) {
$calculatedSignatureParts[] = $params->get('ext');
}
$calculatedSignature = base64_encode(
hash_hmac(
'sha256',
implode("\n", $calculatedSignatureParts),
$macKey,
true // raw_output: outputs raw binary data
)
);
// Return the access token if the signature matches
return ($this->hash_equals($calculatedSignature, $signature)) ? $accessToken : null;
}
/**
* Prevent timing attack
* @param string $knownString
* @param string $userString
* @return bool
*/
private function hash_equals($knownString, $userString)
{
if (function_exists('\hash_equals')) {
return \hash_equals($knownString, $userString);
}
if (strlen($knownString) !== strlen($userString)) {
return false;
}
$len = strlen($knownString);
$result = 0;
for ($i = 0; $i < $len; $i++) {
$result |= (ord($knownString[$i]) ^ ord($userString[$i]));
}
// They are only identical strings if $result is exactly 0...
return 0 === $result;
}
}

View File

@@ -421,4 +421,150 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase
$server->issueAccessToken();
}
public function testCompleteFlowRotateRefreshToken()
{
$_POST = [
'grant_type' => 'refresh_token',
'client_id' => 'testapp',
'client_secret' => 'foobar',
'refresh_token' => 'refresh_token',
];
$server = new AuthorizationServer();
$grant = new RefreshTokenGrant();
$clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface');
$clientStorage->shouldReceive('setServer');
$clientStorage->shouldReceive('get')->andReturn(
(new ClientEntity($server))->hydrate(['id' => 'testapp'])
);
$sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface');
$sessionStorage->shouldReceive('setServer');
$sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([]);
$sessionStorage->shouldReceive('associateScope');
$sessionStorage->shouldReceive('getByAccessToken')->andReturn(
(new SessionEntity($server))
);
$accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface');
$accessTokenStorage->shouldReceive('setServer');
$accessTokenStorage->shouldReceive('get')->andReturn(
(new AccessTokenEntity($server))
);
$accessTokenStorage->shouldReceive('delete');
$accessTokenStorage->shouldReceive('create');
$accessTokenStorage->shouldReceive('getScopes')->andReturn([
(new ScopeEntity($server))->hydrate(['id' => 'foo']),
]);
$accessTokenStorage->shouldReceive('associateScope');
$refreshTokenStorage = M::mock('League\OAuth2\Server\Storage\RefreshTokenInterface');
$refreshTokenStorage->shouldReceive('setServer');
$refreshTokenStorage->shouldReceive('associateScope');
$refreshTokenStorage->shouldReceive('delete');
$refreshTokenStorage->shouldReceive('create');
$refreshTokenStorage->shouldReceive('get')->andReturn(
(new RefreshTokenEntity($server))->setId('refresh_token')->setExpireTime(time() + 86400)
);
$scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface');
$scopeStorage->shouldReceive('setServer');
$scopeStorage->shouldReceive('get')->andReturn(
(new ScopeEntity($server))->hydrate(['id' => 'foo'])
);
$server->setClientStorage($clientStorage);
$server->setScopeStorage($scopeStorage);
$server->setSessionStorage($sessionStorage);
$server->setAccessTokenStorage($accessTokenStorage);
$server->setRefreshTokenStorage($refreshTokenStorage);
$server->addGrantType($grant);
$response = $server->issueAccessToken();
$this->assertTrue(array_key_exists('access_token', $response));
$this->assertTrue(array_key_exists('refresh_token', $response));
$this->assertTrue(array_key_exists('token_type', $response));
$this->assertTrue(array_key_exists('expires_in', $response));
$this->assertNotEquals($response['refresh_token'], $_POST['refresh_token']);
$grant->setRefreshTokenRotation(false);
$response = $server->issueAccessToken();
$this->assertTrue(array_key_exists('access_token', $response));
$this->assertTrue(array_key_exists('refresh_token', $response));
$this->assertTrue(array_key_exists('token_type', $response));
$this->assertTrue(array_key_exists('expires_in', $response));
$this->assertEquals($response['refresh_token'], $_POST['refresh_token']);
}
public function testCompleteFlowShouldRequireClientSecret()
{
$_POST = [
'grant_type' => 'refresh_token',
'client_id' => 'testapp',
'refresh_token' => 'refresh_token',
];
$server = new AuthorizationServer();
$grant = new RefreshTokenGrant();
$grant->setRequireClientSecret(false);
$clientStorage = M::mock('League\OAuth2\Server\Storage\ClientInterface');
$clientStorage->shouldReceive('setServer');
$clientStorage->shouldReceive('get')->andReturn(
(new ClientEntity($server))->hydrate(['id' => 'testapp'])
);
$sessionStorage = M::mock('League\OAuth2\Server\Storage\SessionInterface');
$sessionStorage->shouldReceive('setServer');
$sessionStorage->shouldReceive('getScopes')->shouldReceive('getScopes')->andReturn([]);
$sessionStorage->shouldReceive('associateScope');
$sessionStorage->shouldReceive('getByAccessToken')->andReturn(
(new SessionEntity($server))
);
$accessTokenStorage = M::mock('League\OAuth2\Server\Storage\AccessTokenInterface');
$accessTokenStorage->shouldReceive('setServer');
$accessTokenStorage->shouldReceive('get')->andReturn(
(new AccessTokenEntity($server))
);
$accessTokenStorage->shouldReceive('delete');
$accessTokenStorage->shouldReceive('create');
$accessTokenStorage->shouldReceive('getScopes')->andReturn([
(new ScopeEntity($server))->hydrate(['id' => 'foo']),
]);
$accessTokenStorage->shouldReceive('associateScope');
$refreshTokenStorage = M::mock('League\OAuth2\Server\Storage\RefreshTokenInterface');
$refreshTokenStorage->shouldReceive('setServer');
$refreshTokenStorage->shouldReceive('associateScope');
$refreshTokenStorage->shouldReceive('delete');
$refreshTokenStorage->shouldReceive('create');
$refreshTokenStorage->shouldReceive('get')->andReturn(
(new RefreshTokenEntity($server))->setId('refresh_token')->setExpireTime(time() + 86400)
);
$scopeStorage = M::mock('League\OAuth2\Server\Storage\ScopeInterface');
$scopeStorage->shouldReceive('setServer');
$scopeStorage->shouldReceive('get')->andReturn(
(new ScopeEntity($server))->hydrate(['id' => 'foo'])
);
$server->setClientStorage($clientStorage);
$server->setScopeStorage($scopeStorage);
$server->setSessionStorage($sessionStorage);
$server->setAccessTokenStorage($accessTokenStorage);
$server->setRefreshTokenStorage($refreshTokenStorage);
$server->addGrantType($grant);
$response = $server->issueAccessToken();
$this->assertTrue(array_key_exists('access_token', $response));
$this->assertTrue(array_key_exists('refresh_token', $response));
$this->assertTrue(array_key_exists('token_type', $response));
$this->assertTrue(array_key_exists('expires_in', $response));
}
}

View File

@@ -0,0 +1,165 @@
<?php
namespace LeagueTests\TokenType;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Entity\AccessTokenEntity;
use League\OAuth2\Server\TokenType\MAC;
use Mockery as M;
use Symfony\Component\HttpFoundation\Request;
class MacTest extends \PHPUnit_Framework_TestCase
{
public function testGenerateResponse()
{
$macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface');
$macStorage->shouldReceive('create');
$server = new AuthorizationServer();
$server->setMacStorage($macStorage);
$tokenType = new MAC();
$tokenType->setServer($server);
$accessToken = new AccessTokenEntity($server);
$accessToken->setId(uniqid());
$accessToken->setExpireTime(time());
$tokenType->setParam('access_token', $accessToken->getId());
$tokenType->setParam('expires_in', 3600);
$response = $tokenType->generateResponse();
$this->assertEquals($accessToken->getId(), $response['access_token']);
$this->assertEquals('mac', $response['token_type']);
$this->assertEquals(3600, $response['expires_in']);
$this->assertEquals('hmac-sha-256', $response['mac_algorithm']);
$this->assertArrayHasKey('mac_key', $response);
}
public function testDetermineAccessTokenInHeaderValid()
{
$macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface');
$macStorage->shouldReceive('getByAccessToken')->andReturn('abcdef');
$server = new AuthorizationServer();
$server->setMacStorage($macStorage);
$ts = time();
$request = Request::createFromGlobals();
$calculatedSignatureParts = [
$ts,
'foo',
strtoupper($request->getMethod()),
$request->getRequestUri(),
$request->getHost(),
$request->getPort(),
'ext'
];
$calculatedSignature = base64_encode(hash_hmac('sha256', implode("\n", $calculatedSignatureParts), 'abcdef', true));
$request->headers->set('Authorization', sprintf('MAC id="foo", nonce="foo", ts="%s", mac="%s", ext="ext"', $ts, $calculatedSignature));
$tokenType = new MAC();
$tokenType->setServer($server);
$response = $tokenType->determineAccessTokenInHeader($request);
$this->assertEquals('foo', $response);
}
public function testDetermineAccessTokenInHeaderMissingHeader()
{
$macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface');
$macStorage->shouldReceive('getByAccessToken')->andReturn('abcdef');
$server = new AuthorizationServer();
$server->setMacStorage($macStorage);
$request = Request::createFromGlobals();
$tokenType = new MAC();
$tokenType->setServer($server);
$response = $tokenType->determineAccessTokenInHeader($request);
$this->assertEquals(null, $response);
}
public function testDetermineAccessTokenInHeaderMissingAuthMac()
{
$macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface');
$macStorage->shouldReceive('getByAccessToken')->andReturn('abcdef');
$server = new AuthorizationServer();
$server->setMacStorage($macStorage);
$request = Request::createFromGlobals();
$request->headers->set('Authorization', '');
$tokenType = new MAC();
$tokenType->setServer($server);
$response = $tokenType->determineAccessTokenInHeader($request);
$this->assertEquals(null, $response);
}
public function testDetermineAccessTokenInHeaderInvalidParam()
{
$macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface');
$macStorage->shouldReceive('getByAccessToken')->andReturn('abcdef');
$server = new AuthorizationServer();
$server->setMacStorage($macStorage);
$request = Request::createFromGlobals();
$request->headers->set('Authorization', 'MAC ');
$tokenType = new MAC();
$tokenType->setServer($server);
$response = $tokenType->determineAccessTokenInHeader($request);
$this->assertEquals(null, $response);
}
public function testDetermineAccessTokenInHeaderMismatchTimestamp()
{
$macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface');
$macStorage->shouldReceive('getByAccessToken')->andReturn('abcdef');
$server = new AuthorizationServer();
$server->setMacStorage($macStorage);
$ts = time() - 100;
$request = Request::createFromGlobals();
$request->headers->set('Authorization', sprintf('MAC id="foo", nonce="foo", ts="%s", mac="%s", ext="ext"', $ts, 'foo'));
$tokenType = new MAC();
$tokenType->setServer($server);
$response = $tokenType->determineAccessTokenInHeader($request);
$this->assertEquals(null, $response);
}
public function testDetermineAccessTokenInHeaderMissingMacKey()
{
$macStorage = M::mock('\League\OAuth2\Server\Storage\MacTokenInterface');
$macStorage->shouldReceive('getByAccessToken')->andReturn(null);
$server = new AuthorizationServer();
$server->setMacStorage($macStorage);
$ts = time();
$request = Request::createFromGlobals();
$request->headers->set('Authorization', sprintf('MAC id="foo", nonce="foo", ts="%s", mac="%s", ext="ext"', $ts, 'foo'));
$tokenType = new MAC();
$tokenType->setServer($server);
$response = $tokenType->determineAccessTokenInHeader($request);
$this->assertEquals(null, $response);
}
}