mirror of
https://github.com/elyby/oauth2-server.git
synced 2025-05-31 14:12:07 +05:30
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
34a6b66b8c | ||
|
61738a7fe2 | ||
|
51184259d1 | ||
|
b21de11429 | ||
|
cf6e86c9d4 | ||
|
f6fdbc7142 | ||
|
7f7f45662a | ||
|
f92a68cc72 | ||
|
295d8ffa24 | ||
|
3d08140651 | ||
|
ec8a8393ee | ||
|
3869b8f406 | ||
|
7da7484008 | ||
|
b42ba4af17 | ||
|
dd795a82f4 | ||
|
166362d3cd | ||
|
d43391564c | ||
|
ea6edf572a | ||
|
19b64c2e65 | ||
|
612775466c | ||
|
740ea24e08 | ||
|
e1c14abf6c | ||
|
d1aae27359 | ||
|
80aeaf9200 | ||
|
282bb20cc8 | ||
|
b727be55a2 | ||
|
cf80a2d6ce | ||
|
851c7c0eb1 |
12
.travis.yml
12
.travis.yml
@@ -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
|
||||
|
15
CHANGELOG.md
15
CHANGELOG.md
@@ -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)
|
||||
|
@@ -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.
|
||||
|
@@ -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.*",
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace League\OAuth2\Server\Entity;
|
||||
|
||||
/**
|
||||
* Access token entity class
|
||||
* Auth Code entity class
|
||||
*/
|
||||
class AuthCodeEntity extends AbstractTokenEntity
|
||||
{
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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']);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user