Compare commits

..

28 Commits
4.1.0 ... 4.1.3

Author SHA1 Message Date
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
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
17 changed files with 199 additions and 59 deletions

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

View File

@@ -1,5 +1,20 @@
# Changelog
## 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

@@ -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
@@ -53,6 +55,10 @@ Please see [CONTRIBUTING](https://github.com/thephpleague/oauth2-server/blob/mas
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",
"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

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

View File

@@ -148,7 +148,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 +157,10 @@ class AuthCodeGrant extends AbstractGrant
foreach ($authParams['scopes'] as $scope) {
$authCode->associateScope($scope);
$session->associateScope($scope);
}
$session->save();
$authCode->setSession($session);
$authCode->save();

View File

@@ -35,6 +35,13 @@ class RefreshTokenGrant extends AbstractGrant
*/
protected $refreshTokenTTL = 604800;
/**
* Rotate token (default = true)
*
* @var integer
*/
protected $refreshTokenRotate = true;
/**
* Set the TTL of the refresh token
*
@@ -57,6 +64,25 @@ 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;
}
/**
* {@inheritdoc}
*/
@@ -146,17 +172,21 @@ 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,6 +12,8 @@
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;
@@ -40,10 +42,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 +95,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 +129,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) {
$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

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

@@ -128,22 +128,18 @@ class MAC extends AbstractTokenType implements TokenTypeInterface
*/
private function hash_equals($knownString, $userString)
{
if (!function_exists('hash_equals')) {
function 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 (function_exists('\hash_equals')) {
return \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;
}
}

View File

@@ -421,4 +421,81 @@ 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']);
}
}