Compare commits

...

62 Commits
4.1.1 ... 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
30 changed files with 296 additions and 76 deletions

View File

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

View File

@@ -1,10 +1,22 @@
language: php language: php
sudo: false
cache:
directories:
- vendor
php: php:
- 5.4 - 5.4
- 5.5 - 5.5
- 5.6 - 5.6
- 7.0
- hhvm - hhvm
matrix:
allow_failures:
- php: 7.0
fast_finish: true
install: install:
- travis_retry composer install --no-interaction --prefer-source - travis_retry composer install --no-interaction --prefer-source
@@ -14,8 +26,8 @@ script:
- phpunit --coverage-text --verbose --coverage-clover=coverage.clover --coverage-html coverage - phpunit --coverage-text --verbose --coverage-clover=coverage.clover --coverage-html coverage
after_script: after_script:
- wget https://scrutinizer-ci.com/ocular.phar - bash -c 'if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi;
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover - 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.email "travis@travis-ci.org"
- git config --global user.name "TravisCI" - git config --global user.name "TravisCI"
- cp -R coverage ${HOME}/coverage - cp -R coverage ${HOME}/coverage

View File

@@ -1,5 +1,40 @@
# Changelog # 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) ## 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.*` * Changed `symfony/http-foundation` dependency version to `~2.4` so package can be installed in Laravel `4.1.*`

View File

@@ -51,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. 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 ## Support
Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague/oauth2-server/issues) 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 ## 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. 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", "license": "MIT",
"require": { "require": {
"php": ">=5.4.0", "php": ">=5.4.0",
"symfony/http-foundation": "~2.4", "symfony/http-foundation": "~2.4|~3.0",
"league/event": "1.0.*" "league/event": "~2.1"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "4.3.*", "phpunit/phpunit": "4.3.*",

View File

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

View File

@@ -9,11 +9,6 @@ use RelationalExample\Storage;
include __DIR__.'/vendor/autoload.php'; 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 // Set up the OAuth 2.0 resource server
$sessionStorage = new Storage\SessionStorage(); $sessionStorage = new Storage\SessionStorage();
$accessTokenStorage = new Storage\AccessTokenStorage(); $accessTokenStorage = new Storage\AccessTokenStorage();

View File

@@ -122,10 +122,11 @@ abstract class AbstractServer
* *
* @param string $eventName Event name * @param string $eventName Event name
* @param callable $listener Callable function or method * @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);
} }
/** /**

View File

@@ -20,7 +20,7 @@ use League\OAuth2\Server\TokenType\Bearer;
class AuthorizationServer extends AbstractServer 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 * The OAuth 2 specification states it should be a space but most use a comma
* *
* @var string * @var string

View File

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

View File

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

View File

@@ -36,6 +36,11 @@ class OAuthException extends \Exception
*/ */
public $errorType = ''; public $errorType = '';
/**
* Parameter eventually passed to Exception
*/
public $parameter = '';
/** /**
* Throw a new exception * 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 * 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) 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; $parameter = is_null($parameter) ? 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.' : $parameter;
parent::__construct($parameter); parent::__construct($parameter);
} }
} }

View File

@@ -32,6 +32,7 @@ class UnsupportedGrantTypeException extends OAuthException
public function __construct($parameter) public function __construct($parameter)
{ {
$this->parameter = $parameter;
parent::__construct( parent::__construct(
sprintf( sprintf(
'The authorization grant type "%s" is not supported by the authorization server.', '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) public function __construct($parameter, $redirectUri = null)
{ {
$this->parameter = $parameter;
parent::__construct('The authorization server does not support obtaining an access token using this method.'); parent::__construct('The authorization server does not support obtaining an access token using this method.');
$this->redirectUri = $redirectUri; $this->redirectUri = $redirectUri;
} }

View File

@@ -60,6 +60,14 @@ class AuthCodeGrant extends AbstractGrant
*/ */
protected $authTokenTTL = 600; 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 * Override the default access token expire time
* *
@@ -72,6 +80,27 @@ class AuthCodeGrant extends AbstractGrant
$this->authTokenTTL = $authTokenTTL; $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 * Check authorize parameters
* *
@@ -148,7 +177,6 @@ class AuthCodeGrant extends AbstractGrant
$session = new SessionEntity($this->server); $session = new SessionEntity($this->server);
$session->setOwner($type, $typeId); $session->setOwner($type, $typeId);
$session->associateClient($authParams['client']); $session->associateClient($authParams['client']);
$session->save();
// Create a new auth code // Create a new auth code
$authCode = new AuthCodeEntity($this->server); $authCode = new AuthCodeEntity($this->server);
@@ -158,8 +186,10 @@ class AuthCodeGrant extends AbstractGrant
foreach ($authParams['scopes'] as $scope) { foreach ($authParams['scopes'] as $scope) {
$authCode->associateScope($scope); $authCode->associateScope($scope);
$session->associateScope($scope);
} }
$session->save();
$authCode->setSession($session); $authCode->setSession($session);
$authCode->save(); $authCode->save();
@@ -183,7 +213,7 @@ class AuthCodeGrant extends AbstractGrant
$clientSecret = $this->server->getRequest()->request->get('client_secret', $clientSecret = $this->server->getRequest()->request->get('client_secret',
$this->server->getRequest()->getPassword()); $this->server->getRequest()->getPassword());
if (is_null($clientSecret)) { if ($this->shouldRequireClientSecret() && is_null($clientSecret)) {
throw new Exception\InvalidRequestException('client_secret'); throw new Exception\InvalidRequestException('client_secret');
} }
@@ -270,4 +300,4 @@ class AuthCodeGrant extends AbstractGrant
return $this->server->getTokenType()->generateResponse(); return $this->server->getTokenType()->generateResponse();
} }
} }

View File

@@ -19,7 +19,7 @@ use League\OAuth2\Server\Exception;
use League\OAuth2\Server\Util\SecureKey; use League\OAuth2\Server\Util\SecureKey;
/** /**
* Referesh token grant * Refresh token grant
*/ */
class RefreshTokenGrant extends AbstractGrant class RefreshTokenGrant extends AbstractGrant
{ {
@@ -42,6 +42,14 @@ class RefreshTokenGrant extends AbstractGrant
*/ */
protected $refreshTokenRotate = true; 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 * Set the TTL of the refresh token
* *
@@ -83,6 +91,28 @@ class RefreshTokenGrant extends AbstractGrant
return $this->refreshTokenRotate; 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} * {@inheritdoc}
*/ */
@@ -95,7 +125,7 @@ class RefreshTokenGrant extends AbstractGrant
$clientSecret = $this->server->getRequest()->request->get('client_secret', $clientSecret = $this->server->getRequest()->request->get('client_secret',
$this->server->getRequest()->getPassword()); $this->server->getRequest()->getPassword());
if (is_null($clientSecret)) { if ($this->shouldRequireClientSecret() && is_null($clientSecret)) {
throw new Exception\InvalidRequestException('client_secret'); throw new Exception\InvalidRequestException('client_secret');
} }
@@ -190,4 +220,4 @@ class RefreshTokenGrant extends AbstractGrant
return $this->server->getTokenType()->generateResponse(); return $this->server->getTokenType()->generateResponse();
} }
} }

View File

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

View File

@@ -24,7 +24,7 @@ interface AccessTokenInterface extends StorageInterface
* *
* @param string $token The access token * @param string $token The access token
* *
* @return \League\OAuth2\Server\Entity\AccessTokenEntity * @return \League\OAuth2\Server\Entity\AccessTokenEntity | null
*/ */
public function get($token); public function get($token);
@@ -33,7 +33,7 @@ interface AccessTokenInterface extends StorageInterface
* *
* @param \League\OAuth2\Server\Entity\AccessTokenEntity $token The access token * @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); public function getScopes(AccessTokenEntity $token);

View File

@@ -24,7 +24,7 @@ interface AuthCodeInterface extends StorageInterface
* *
* @param string $code * @param string $code
* *
* @return \League\OAuth2\Server\Entity\AuthCodeEntity * @return \League\OAuth2\Server\Entity\AuthCodeEntity | null
*/ */
public function get($code); public function get($code);
@@ -45,7 +45,7 @@ interface AuthCodeInterface extends StorageInterface
* *
* @param \League\OAuth2\Server\Entity\AuthCodeEntity $token The auth code * @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); 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 $redirectUri The client's redirect URI (default = "null")
* @param string $grantType The grant type used (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); 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 * @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); public function getBySession(SessionEntity $session);
} }

View File

@@ -23,7 +23,7 @@ interface RefreshTokenInterface extends StorageInterface
* *
* @param string $token * @param string $token
* *
* @return \League\OAuth2\Server\Entity\RefreshTokenEntity * @return \League\OAuth2\Server\Entity\RefreshTokenEntity | null
*/ */
public function get($token); 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 $grantType The grant type used in the request (default = "null")
* @param string $clientId The client sending 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); 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 * @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); public function getByAccessToken(AccessTokenEntity $accessToken);
@@ -35,7 +35,7 @@ interface SessionInterface extends StorageInterface
* *
* @param \League\OAuth2\Server\Entity\AuthCodeEntity $authCode The auth code * @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); public function getByAuthCode(AuthCodeEntity $authCode);
@@ -44,7 +44,7 @@ interface SessionInterface extends StorageInterface
* *
* @param \League\OAuth2\Server\Entity\SessionEntity * @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); public function getScopes(SessionEntity $session);

View File

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

View File

@@ -36,6 +36,10 @@ class MAC extends AbstractTokenType implements TokenTypeInterface
'mac_algorithm' => 'hmac-sha-256', 'mac_algorithm' => 'hmac-sha-256',
]; ];
if (!is_null($this->getParam('refresh_token'))) {
$response['refresh_token'] = $this->getParam('refresh_token');
}
return $response; return $response;
} }
@@ -61,7 +65,7 @@ class MAC extends AbstractTokenType implements TokenTypeInterface
array_map(function ($param) use (&$params) { array_map(function ($param) use (&$params) {
$param = trim($param); $param = trim($param);
preg_match_all('/([a-zA-Z]*)="([\w=]*)"/', $param, $matches); preg_match_all('/([a-zA-Z]*)="([\w=\/+]*)"/', $param, $matches);
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
if (count($matches) !== 3) { if (count($matches) !== 3) {
@@ -84,7 +88,7 @@ class MAC extends AbstractTokenType implements TokenTypeInterface
return; return;
} }
if ((int) $params->get('ts') !== time()) { if (abs($params->get('ts') - time()) > 300) {
return; return;
} }
@@ -105,7 +109,7 @@ class MAC extends AbstractTokenType implements TokenTypeInterface
$timestamp, $timestamp,
$nonce, $nonce,
strtoupper($request->getMethod()), strtoupper($request->getMethod()),
$request->getUri(), $request->getRequestUri(),
$request->getHost(), $request->getHost(),
$request->getPort(), $request->getPort(),
]; ];
@@ -114,7 +118,14 @@ class MAC extends AbstractTokenType implements TokenTypeInterface
$calculatedSignatureParts[] = $params->get('ext'); $calculatedSignatureParts[] = $params->get('ext');
} }
$calculatedSignature = base64_encode(hash_hmac('sha256', implode("\n", $calculatedSignatureParts), $macKey)); $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 the access token if the signature matches
return ($this->hash_equals($calculatedSignature, $signature)) ? $accessToken : null; return ($this->hash_equals($calculatedSignature, $signature)) ? $accessToken : null;
@@ -128,22 +139,18 @@ class MAC extends AbstractTokenType implements TokenTypeInterface
*/ */
private function hash_equals($knownString, $userString) private function hash_equals($knownString, $userString)
{ {
if (!function_exists('hash_equals')) { if (function_exists('\hash_equals')) {
function hash_equals($knownString, $userString) 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;
}
} }
if (strlen($knownString) !== strlen($userString)) {
return hash_equals($knownString, $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

@@ -498,4 +498,73 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase
$this->assertTrue(array_key_exists('expires_in', $response)); $this->assertTrue(array_key_exists('expires_in', $response));
$this->assertEquals($response['refresh_token'], $_POST['refresh_token']); $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

@@ -52,12 +52,12 @@ class MacTest extends \PHPUnit_Framework_TestCase
$ts, $ts,
'foo', 'foo',
strtoupper($request->getMethod()), strtoupper($request->getMethod()),
$request->getUri(), $request->getRequestUri(),
$request->getHost(), $request->getHost(),
$request->getPort(), $request->getPort(),
'ext' 'ext'
]; ];
$calculatedSignature = base64_encode(hash_hmac('sha256', implode("\n", $calculatedSignatureParts), 'abcdef')); $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)); $request->headers->set('Authorization', sprintf('MAC id="foo", nonce="foo", ts="%s", mac="%s", ext="ext"', $ts, $calculatedSignature));