diff --git a/.travis.yml b/.travis.yml index f900228a..22b7fc4f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,3 +30,4 @@ after_script: branches: only: - master + - 8.0.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 20a54fb0..cfdfbcad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Changed +- Replace `convertToJWT()` interface with a more generic `__toString()` to improve extensibility (PR #874) - The `invalidClient()` function accepts a PSR-7 compliant `$serverRequest` argument to avoid accessing the `$_SERVER` global variable and improve testing (PR #899) ## [7.1.1] - released 2018-05-21 diff --git a/src/Entities/AccessTokenEntityInterface.php b/src/Entities/AccessTokenEntityInterface.php index 4da7600e..fd459914 100644 --- a/src/Entities/AccessTokenEntityInterface.php +++ b/src/Entities/AccessTokenEntityInterface.php @@ -9,17 +9,17 @@ namespace League\OAuth2\Server\Entities; -use Lcobucci\JWT\Token; use League\OAuth2\Server\CryptKey; interface AccessTokenEntityInterface extends TokenInterface { /** - * Generate a JWT from the access token - * - * @param CryptKey $privateKey - * - * @return Token + * Set a private key used to encrypt the access token. */ - public function convertToJWT(CryptKey $privateKey); + public function setPrivateKey(CryptKey $privateKey); + + /** + * Generate a string representation of the access token. + */ + public function __toString(); } diff --git a/src/Entities/Traits/AccessTokenTrait.php b/src/Entities/Traits/AccessTokenTrait.php index 81fc1bfd..501233e9 100644 --- a/src/Entities/Traits/AccessTokenTrait.php +++ b/src/Entities/Traits/AccessTokenTrait.php @@ -19,6 +19,19 @@ use League\OAuth2\Server\Entities\ScopeEntityInterface; trait AccessTokenTrait { + /** + * @var CryptKey + */ + private $privateKey; + + /** + * Set the private key used to encrypt this access token. + */ + public function setPrivateKey(CryptKey $privateKey) + { + $this->privateKey = $privateKey; + } + /** * Generate a JWT from the access token * @@ -26,7 +39,7 @@ trait AccessTokenTrait * * @return Token */ - public function convertToJWT(CryptKey $privateKey) + private function convertToJWT(CryptKey $privateKey) { return (new Builder()) ->setAudience($this->getClient()->getIdentifier()) @@ -40,6 +53,14 @@ trait AccessTokenTrait ->getToken(); } + /** + * Generate a string representation from the access token + */ + public function __toString() + { + return (string) $this->convertToJWT($this->privateKey); + } + /** * @return ClientEntityInterface */ diff --git a/src/Grant/AbstractGrant.php b/src/Grant/AbstractGrant.php index 05b73faa..d020c6ad 100644 --- a/src/Grant/AbstractGrant.php +++ b/src/Grant/AbstractGrant.php @@ -361,6 +361,7 @@ abstract class AbstractGrant implements GrantTypeInterface $accessToken->setClient($client); $accessToken->setUserIdentifier($userIdentifier); $accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL)); + $accessToken->setPrivateKey($this->privateKey); foreach ($scopes as $scope) { $accessToken->addScope($scope); diff --git a/src/Grant/ImplicitGrant.php b/src/Grant/ImplicitGrant.php index c740f75c..5d6035e4 100644 --- a/src/Grant/ImplicitGrant.php +++ b/src/Grant/ImplicitGrant.php @@ -216,7 +216,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant $this->makeRedirectUri( $finalRedirectUri, [ - 'access_token' => (string) $accessToken->convertToJWT($this->privateKey), + 'access_token' => (string) $accessToken, 'token_type' => 'Bearer', 'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(), 'state' => $authorizationRequest->getState(), diff --git a/src/ResponseTypes/BearerTokenResponse.php b/src/ResponseTypes/BearerTokenResponse.php index a57573a0..2e658215 100644 --- a/src/ResponseTypes/BearerTokenResponse.php +++ b/src/ResponseTypes/BearerTokenResponse.php @@ -24,12 +24,10 @@ class BearerTokenResponse extends AbstractResponseType { $expireDateTime = $this->accessToken->getExpiryDateTime()->getTimestamp(); - $jwtAccessToken = $this->accessToken->convertToJWT($this->privateKey); - $responseParams = [ 'token_type' => 'Bearer', 'expires_in' => $expireDateTime - (new \DateTime())->getTimestamp(), - 'access_token' => (string) $jwtAccessToken, + 'access_token' => (string) $this->accessToken, ]; if ($this->refreshToken instanceof RefreshTokenEntityInterface) { diff --git a/tests/Grant/AbstractGrantTest.php b/tests/Grant/AbstractGrantTest.php index 5da2776e..a5916de7 100644 --- a/tests/Grant/AbstractGrantTest.php +++ b/tests/Grant/AbstractGrantTest.php @@ -3,6 +3,7 @@ namespace LeagueTests\Grant; use League\Event\Emitter; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; @@ -353,6 +354,7 @@ class AbstractGrantTest extends TestCase /** @var AbstractGrant $grantMock */ $grantMock = $this->getMockForAbstractClass(AbstractGrant::class); + $grantMock->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $grantMock->setAccessTokenRepository($accessTokenRepoMock); $abstractGrantReflection = new \ReflectionClass($grantMock); diff --git a/tests/Grant/AuthCodeGrantTest.php b/tests/Grant/AuthCodeGrantTest.php index 6a319234..589e488c 100644 --- a/tests/Grant/AuthCodeGrantTest.php +++ b/tests/Grant/AuthCodeGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use League\OAuth2\Server\Exception\OAuthServerException; @@ -40,7 +41,7 @@ class AuthCodeGrantTest extends TestCase public function setUp() { - $this->cryptStub = new CryptTraitStub; + $this->cryptStub = new CryptTraitStub(); } public function testGetIdentifier() @@ -608,6 +609,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], @@ -676,6 +678,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], @@ -747,6 +750,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], @@ -1537,6 +1541,7 @@ class AuthCodeGrantTest extends TestCase new \DateInterval('PT10M') ); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $this->assertInstanceOf(RedirectResponse::class, $grant->completeAuthorizationRequest($authRequest)); } @@ -1624,6 +1629,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], @@ -1695,6 +1701,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], @@ -1766,6 +1773,7 @@ class AuthCodeGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setRefreshTokenRepository($refreshTokenRepositoryMock); $grant->setEncryptionKey($this->cryptStub->getKey()); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $request = new ServerRequest( [], diff --git a/tests/Grant/ClientCredentialsGrantTest.php b/tests/Grant/ClientCredentialsGrantTest.php index 6c7b5a36..dfd78b41 100644 --- a/tests/Grant/ClientCredentialsGrantTest.php +++ b/tests/Grant/ClientCredentialsGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Grant\ClientCredentialsGrant; use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; @@ -44,6 +45,7 @@ class ClientCredentialsGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( diff --git a/tests/Grant/PasswordGrantTest.php b/tests/Grant/PasswordGrantTest.php index 2ee700f8..c90a83db 100644 --- a/tests/Grant/PasswordGrantTest.php +++ b/tests/Grant/PasswordGrantTest.php @@ -2,6 +2,7 @@ namespace LeagueTests\Grant; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\Entities\AccessTokenEntityInterface; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; use League\OAuth2\Server\Grant\PasswordGrant; @@ -60,6 +61,7 @@ class PasswordGrantTest extends TestCase $grant->setAccessTokenRepository($accessTokenRepositoryMock); $grant->setScopeRepository($scopeRepositoryMock); $grant->setDefaultScope(self::DEFAULT_SCOPE); + $grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $serverRequest = new ServerRequest(); $serverRequest = $serverRequest->withParsedBody( diff --git a/tests/Middleware/ResourceServerMiddlewareTest.php b/tests/Middleware/ResourceServerMiddlewareTest.php index 2269c45a..d1a96042 100644 --- a/tests/Middleware/ResourceServerMiddlewareTest.php +++ b/tests/Middleware/ResourceServerMiddlewareTest.php @@ -29,8 +29,9 @@ class ResourceServerMiddlewareTest extends TestCase $accessToken->setUserIdentifier(123); $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $token = $accessToken->convertToJWT(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $token = (string) $accessToken; $request = new ServerRequest(); $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); @@ -64,8 +65,9 @@ class ResourceServerMiddlewareTest extends TestCase $accessToken->setUserIdentifier(123); $accessToken->setExpiryDateTime((new \DateTime())->sub(new \DateInterval('PT1H'))); $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); - $token = $accessToken->convertToJWT(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); + $token = (string) $accessToken; $request = new ServerRequest(); $request = $request->withHeader('authorization', sprintf('Bearer %s', $token)); diff --git a/tests/ResponseTypes/BearerResponseTypeTest.php b/tests/ResponseTypes/BearerResponseTypeTest.php index 31245b07..2eb87238 100644 --- a/tests/ResponseTypes/BearerResponseTypeTest.php +++ b/tests/ResponseTypes/BearerResponseTypeTest.php @@ -35,6 +35,7 @@ class BearerResponseTypeTest extends TestCase $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); $accessToken->addScope($scope); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); @@ -77,6 +78,7 @@ class BearerResponseTypeTest extends TestCase $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); $accessToken->addScope($scope); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); @@ -119,6 +121,7 @@ class BearerResponseTypeTest extends TestCase $accessToken->setUserIdentifier(123); $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); @@ -164,6 +167,7 @@ class BearerResponseTypeTest extends TestCase $accessToken->setUserIdentifier(123); $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef'); @@ -206,6 +210,7 @@ class BearerResponseTypeTest extends TestCase $accessToken->setUserIdentifier(123); $accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H'))); $accessToken->setClient($client); + $accessToken->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key')); $refreshToken = new RefreshTokenEntity(); $refreshToken->setIdentifier('abcdef');