Compare commits

..

38 Commits
5.0.2 ... 5.1.0

Author SHA1 Message Date
Alex Bilbie
68e4b1d390 Updated changelog 2016-06-28 09:03:41 +01:00
Alex Bilbie
5ee1583c5b Ensure state is in access denied redirect. Fixes #597 2016-06-28 09:03:01 +01:00
Alex Bilbie
66de05a395 Merge pull request #605 from jfilla/master
Added catch Runtime exception when parsing JWT string
2016-06-28 08:49:29 +01:00
Alex Bilbie
df20da1235 Merge pull request #601 from zerkms/ISSUE-596_UNIQUE_ACCESS_TOKEN
Added a check for unique access token constraint violation
2016-06-28 08:48:38 +01:00
Alex Bilbie
7321622104 Merge pull request #606 from GrahamCampbell/patch-2
Allow random compat 2.x
2016-06-28 08:46:57 +01:00
Graham Campbell
84187041bd Allow random compat 2.x 2016-06-27 19:31:35 +01:00
Jakub Filla
9eccc40eb6 Added catch Runtime exception when parsing JWT string 2016-06-22 12:38:03 +02:00
Alex Bilbie
8b865cc523 Merge pull request #604 from iansltx/http-basic-from-header
Look at Authorization header directly for HTTP Basic auth checks
2016-06-22 08:42:30 +01:00
Ian Littman
9775c0076b Look at Authorization header directly for HTTP Basic auth check
Should allow for better compatibility with server implementations that aren't sitting on top of a standard SAPI (e.g. persistent web servers building a PSR-7 compatible request from a socket-received message).

One catch here is that I've seen Apache hijack the HTTP Authorization header in the past, though that would probably impact the other aspects of the server just as much as it would this, so I think that risk is manageable.

Added tests to cover all paths through the new code, so the AbstractGrant type still has 100% coverage :)

Did notice that, as of the latest versions of PHPUnit, the mock creation method is deprecated. Maybe that needs to be updated? Haven't checked to see whether the replacements are PHPUnit 4.8 compatible though, so maybe they need to stay in order to test on older PHP versions?
2016-06-21 21:08:38 -05:00
Ivan Kurnosov
b68ef973df Added a check for unique access token constraint violation 2016-06-20 20:19:03 +12:00
Alex Bilbie
c6e5f12a7c Merge pull request #600 from zerkms/ISSUE-598_REDUNDANT_IS_EXPIRED
Removed isExpired() from interfaces and traits
2016-06-17 09:14:38 +01:00
Ivan Kurnosov
6b88cbeb13 Removed isExpired() from interfaces and traits 2016-06-17 19:50:04 +12:00
Alex Bilbie
64a0fcb3a6 Updated examples. Fixes #589 2016-06-02 09:35:27 +01:00
Alex Bilbie
78dbb267ed Merge pull request #578 from juliangut/master
unify middleware exception responses
2016-05-12 09:53:42 +01:00
Julián Gutiérrez
22e6a350dd unify middleware exception responses 2016-05-11 14:13:58 +02:00
Alex Bilbie
c0936cc320 Updated commercial support statement 2016-05-10 13:23:56 +01:00
Alex Bilbie
bb82651bec First commit of update changelog 2016-05-10 08:10:50 +01:00
Alex Bilbie
599c9aba75 Added indigophp/hash-compat to suggest and require dev for PHP 5.5 support 2016-05-06 15:23:57 +01:00
Alex Bilbie
4c6c189dff Added a list of supported RFCs 2016-05-06 15:23:25 +01:00
Alex Bilbie
8e8aed1a50 Implemented RFC7636. Fixes #574 2016-05-06 15:23:16 +01:00
Alex Bilbie
4a4f4fe2d7 Added commercial support section to README 2016-05-04 09:17:38 +01:00
Alex Bilbie
6b18a9441a Updated changelog 2016-05-04 09:13:20 +01:00
Alex Bilbie
44ff7b33a1 Merge branch 'master' of github.com:thephpleague/oauth2-server 2016-05-04 09:10:11 +01:00
Alex Bilbie
db055f790d Revert "Remove redundant parameters in example" #553
This reverts commit 9a93dca05c.
2016-05-04 09:10:05 +01:00
Alex Bilbie
d1bc4848c8 Revert "Remove redundant parameters in example"
This reverts commit 9a93dca05c.
2016-05-04 09:07:50 +01:00
Alex Bilbie
cf63403585 Merge branch 'master' of github.com:thephpleague/oauth2-server 2016-05-04 08:56:04 +01:00
Alex Bilbie
cdf43e498e Use constant for event name instead of explicit string. Fixes #563 2016-05-04 08:55:57 +01:00
Alex Bilbie
a12fc98b0d Merge pull request #569 from ismailbaskin/patch-2
Correct wrong phpdoc
2016-05-04 08:45:58 +01:00
Alex Bilbie
019d285235 Merge pull request #570 from Themodem/master
Fixed typo in exception string
2016-05-04 08:45:37 +01:00
Lee
0bb968f413 Fixed typo in exception string 2016-05-04 15:13:48 +08:00
ismail BASKIN
88b19ad2d0 Correct wrong phpdoc 2016-05-04 00:54:36 +03:00
Alex Bilbie
6856699cab Merge pull request #564 from ismailbaskin/patch-1
Remove unused request property
2016-04-30 12:16:12 +01:00
ismail BASKIN
72cd9a62e1 Remove unused request property 2016-04-30 05:08:28 +03:00
Alex Bilbie
acf262f879 Merge pull request #553 from markinjapan/patch-1
Remove redundant parameters in getNewToken()
2016-04-27 20:58:29 +01:00
Alex Bilbie
5241309bdb Fixes #560 2016-04-27 20:53:12 +01:00
Mark
9a93dca05c Remove redundant parameters in example 2016-04-20 16:52:54 +09:00
Mark
a6b7a5cedc Remove use of redundant parameters 2016-04-20 16:52:36 +09:00
Mark
78b6bddc4d Remove redundant parameters 2016-04-20 16:29:37 +09:00
26 changed files with 1260 additions and 100 deletions

View File

@@ -1,5 +1,27 @@
# Changelog
## 5.1.0 (released 2016-06-28)
* Implemented RFC7636 (Issue #574)
* Unify middleware exception responses (Issue #578)
* Updated examples (Issue #589)
* Ensure state is in access denied redirect (Issue #597)
* Remove redundant `isExpired()` method from entity interfaces and traits (Issue #600)
* Added a check for unique access token constraint violation (Issue #601)
* Look at Authorization header directly for HTTP Basic auth checks (Issue #604)
* Added catch Runtime exception when parsing JWT string (Issue #605)
* Allow `paragonie/random_compat` 2.x (Issue #606)
* Added `indigophp/hash-compat` to Composer suggestions and `require-dev` for PHP 5.5 support
## 5.0.3 (released 2016-05-04)
* Fix hints in PasswordGrant (Issue #560)
* Add meaning of `Resource owner` to terminology.md (Issue #561)
* Use constant for event name instead of explicit string (Issue #563)
* Remove unused request property (Issue #564)
* Correct wrong phpdoc (Issue #569)
* Fixed typo in exception string (Issue #570)
## 5.0.2 (released 2016-04-18)
* `state` parameter is now correctly returned after implicit grant authorization

View File

@@ -17,6 +17,13 @@ It supports out of the box the following grants:
* Resource owner password credentials grant
* Refresh grant
The following RFCs are implemented:
* [RFC6749 "OAuth 2.0"](https://tools.ietf.org/html/rfc6749)
* [RFC6750 " The OAuth 2.0 Authorization Framework: Bearer Token Usage"](https://tools.ietf.org/html/rfc6750)
* [RFC7519 "JSON Web Token (JWT)"](https://tools.ietf.org/html/rfc7519)
* [RFC7636 "Proof Key for Code Exchange by OAuth Public Clients"](https://tools.ietf.org/html/rfc7636)
This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](https://twitter.com/alexbilbie).
## Requirements
@@ -49,6 +56,10 @@ Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague
If you have any questions about OAuth _please_ open a ticket here; please **don't** email the address below.
## Commercial Support
If you would like help implementing this library into your existing platform, or would be interested in OAuth advice or training for you and your team please get in touch with [Glynde Labs](https://glyndelabs.com).
## Security
If you discover any security related issues, please email `hello@alexbilbie.com` instead of using the issue tracker.

View File

@@ -8,12 +8,13 @@
"ext-openssl": "*",
"league/event": "^2.1",
"lcobucci/jwt": "^3.1",
"paragonie/random_compat": "^1.1",
"paragonie/random_compat": "^1.1 || ^2.0",
"psr/http-message": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8 || ^5.0",
"zendframework/zend-diactoros": "^1.0"
"zendframework/zend-diactoros": "^1.0",
"indigophp/hash-compat": "^1.1"
},
"repositories": [
{
@@ -63,5 +64,8 @@
"branch-alias": {
"dev-V5-WIP": "5.0-dev"
}
},
"suggest": {
"indigophp/hash-compat": "Polyfill for hash_equals function for PHP 5.5"
}
}

View File

@@ -48,6 +48,13 @@ class ScopeRepository implements ScopeRepositoryInterface
ClientEntityInterface $clientEntity,
$userIdentifier = null
) {
// Example of programatically modifying the final scope of the access token
if ((int) $userIdentifier === 1) {
$scope = new ScopeEntity();
$scope->setIdentifier('email');
$scopes[] = $scope;
}
return $scopes;
}
}

View File

@@ -11,7 +11,6 @@ namespace OAuth2ServerExamples\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
use OAuth2ServerExamples\Entities\ScopeEntity;
use OAuth2ServerExamples\Entities\UserEntity;
class UserRepository implements UserRepositoryInterface
@@ -26,10 +25,6 @@ class UserRepository implements UserRepositoryInterface
ClientEntityInterface $clientEntity
) {
if ($username === 'alex' && $password === 'whisky') {
$scope = new ScopeEntity();
$scope->setIdentifier('email');
$scopes[] = $scope;
return new UserEntity();
}

View File

@@ -156,7 +156,7 @@ class AuthorizationServer implements EmitterAwareInterface
* @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest
* @param \Psr\Http\Message\ResponseInterface $response
*
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
* @return \Psr\Http\Message\ResponseInterface
*/
public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response)
{

View File

@@ -77,6 +77,9 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
} catch (\InvalidArgumentException $exception) {
// JWT couldn't be parsed so return the request as is
throw OAuthServerException::accessDenied($exception->getMessage());
} catch(\RuntimeException $exception){
//JWR couldn't be parsed so return the request as is
throw OAuthServerException::accessDenied('Error while decoding to JSON');
}
}
}

View File

@@ -52,11 +52,4 @@ interface RefreshTokenEntityInterface
* @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface
*/
public function getAccessToken();
/**
* Has the token expired?
*
* @return bool
*/
public function isExpired();
}

View File

@@ -80,11 +80,4 @@ interface TokenInterface
* @return ScopeEntityInterface[]
*/
public function getScopes();
/**
* Has the token expired?
*
* @return bool
*/
public function isExpired();
}

View File

@@ -59,14 +59,4 @@ trait RefreshTokenTrait
{
$this->expiryDateTime = $dateTime;
}
/**
* Has the token expired?
*
* @return bool
*/
public function isExpired()
{
return (new DateTime()) > $this->getExpiryDateTime();
}
}

View File

@@ -114,14 +114,4 @@ trait TokenEntityTrait
{
$this->client = $client;
}
/**
* Has the token expired?
*
* @return bool
*/
public function isExpired()
{
return (new DateTime()) > $this->getExpiryDateTime();
}
}

View File

@@ -172,6 +172,26 @@ class OAuthServerException extends \Exception
);
}
/**
* Invalid grant.
*
* @param string $hint
*
* @return static
*/
public static function invalidGrant($hint = '')
{
return new static(
'The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token '
. 'is invalid, expired, revoked, does not match the redirection URI used in the authorization request, '
. 'or was issued to another client.',
10,
'invalid_grant',
400,
$hint
);
}
/**
* @return string
*/

View File

@@ -0,0 +1,21 @@
<?php
/**
* @author Ivan Kurnosov <zerkms@zerkms.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Exception;
class UniqueTokenIdentifierConstraintViolationException extends OAuthServerException
{
public static function create()
{
$errorMessage = 'Could not create unique access token identifier';
return new static($errorMessage, 100, 'access_token_duplicate', 500);
}
}

View File

@@ -16,6 +16,7 @@ use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
@@ -35,10 +36,7 @@ abstract class AbstractGrant implements GrantTypeInterface
const SCOPE_DELIMITER_STRING = ' ';
/**
* @var ServerRequestInterface
*/
protected $request;
const MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS = 10;
/**
* @var ClientRepositoryInterface
@@ -142,21 +140,15 @@ abstract class AbstractGrant implements GrantTypeInterface
*/
protected function validateClient(ServerRequestInterface $request)
{
$clientId = $this->getRequestParameter(
'client_id',
$request,
$this->getServerParameter('PHP_AUTH_USER', $request)
);
list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request);
$clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
if (is_null($clientId)) {
throw OAuthServerException::invalidRequest('client_id');
}
// If the client is confidential require the client secret
$clientSecret = $this->getRequestParameter(
'client_secret',
$request,
$this->getServerParameter('PHP_AUTH_PW', $request)
);
$clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);
$client = $this->clientRepository->getClientEntity(
$clientId,
@@ -166,7 +158,7 @@ abstract class AbstractGrant implements GrantTypeInterface
);
if (!$client instanceof ClientEntityInterface) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
@@ -177,13 +169,13 @@ abstract class AbstractGrant implements GrantTypeInterface
is_string($client->getRedirectUri())
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
} elseif (
is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
}
@@ -242,6 +234,38 @@ abstract class AbstractGrant implements GrantTypeInterface
return isset($requestParameters[$parameter]) ? $requestParameters[$parameter] : $default;
}
/**
* Retrieve HTTP Basic Auth credentials with the Authorization header
* of a request. First index of the returned array is the username,
* second is the password (so list() will work). If the header does
* not exist, or is otherwise an invalid HTTP Basic header, return
* [null, null].
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @return string[]|null[]
*/
protected function getBasicAuthCredentials(ServerRequestInterface $request)
{
if (!$request->hasHeader('Authorization')) {
return [null, null];
}
$header = $request->getHeader('Authorization')[0];
if (strpos($header, 'Basic ') !== 0) {
return [null, null];
}
if (!($decoded = base64_decode(substr($header, 6)))) {
return [null, null];
}
if (strpos($decoded, ':') === false) {
return [null, null]; // HTTP Basic header without colon isn't valid
}
return explode(':', $decoded, 2);
}
/**
* Retrieve query string parameter.
*
@@ -300,19 +324,28 @@ abstract class AbstractGrant implements GrantTypeInterface
$userIdentifier,
array $scopes = []
) {
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
$accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier);
$accessToken->setClient($client);
$accessToken->setUserIdentifier($userIdentifier);
$accessToken->setIdentifier($this->generateUniqueIdentifier());
$accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL));
foreach ($scopes as $scope) {
$accessToken->addScope($scope);
}
$this->accessTokenRepository->persistNewAccessToken($accessToken);
return $accessToken;
while ($maxGenerationAttempts-- > 0) {
$accessToken->setIdentifier($this->generateUniqueIdentifier());
try {
$this->accessTokenRepository->persistNewAccessToken($accessToken);
return $accessToken;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
throw $e;
}
}
}
}
/**
@@ -333,8 +366,9 @@ abstract class AbstractGrant implements GrantTypeInterface
$redirectUri,
array $scopes = []
) {
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
$authCode = $this->authCodeRepository->getNewAuthCode();
$authCode->setIdentifier($this->generateUniqueIdentifier());
$authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL));
$authCode->setClient($client);
$authCode->setUserIdentifier($userIdentifier);
@@ -344,9 +378,17 @@ abstract class AbstractGrant implements GrantTypeInterface
$authCode->addScope($scope);
}
$this->authCodeRepository->persistNewAuthCode($authCode);
return $authCode;
while ($maxGenerationAttempts-- > 0) {
$authCode->setIdentifier($this->generateUniqueIdentifier());
try {
$this->authCodeRepository->persistNewAuthCode($authCode);
return $authCode;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
throw $e;
}
}
}
}
/**
@@ -356,14 +398,23 @@ abstract class AbstractGrant implements GrantTypeInterface
*/
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
{
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
$refreshToken->setExpiryDateTime((new \DateTime())->add($this->refreshTokenTTL));
$refreshToken->setAccessToken($accessToken);
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
return $refreshToken;
while ($maxGenerationAttempts-- > 0) {
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
try {
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
return $refreshToken;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
throw $e;
}
}
}
}
/**

View File

@@ -29,6 +29,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
*/
private $authCodeTTL;
/**
* @var bool
*/
private $enableCodeExchangeProof = false;
/**
* @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
@@ -45,6 +50,11 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$this->refreshTokenTTL = new \DateInterval('P1M');
}
public function enableCodeExchangeProof()
{
$this->enableCodeExchangeProof = true;
}
/**
* Respond to an access token request.
*
@@ -118,6 +128,42 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code');
}
// Validate code challenge
if ($this->enableCodeExchangeProof === true) {
$codeVerifier = $this->getRequestParameter('code_verifier', $request, null);
if ($codeVerifier === null) {
throw OAuthServerException::invalidRequest('code_verifier');
}
switch ($authCodePayload->code_challenge_method) {
case 'plain':
if (hash_equals($codeVerifier, $authCodePayload->code_challenge) === false) {
throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
}
break;
case 'S256':
if (
hash_equals(
urlencode(base64_encode(hash('sha256', $codeVerifier))),
$authCodePayload->code_challenge
) === false
) {
throw OAuthServerException::invalidGrant('Failed to verify `code_verifier`.');
}
// @codeCoverageIgnoreStart
break;
default:
throw OAuthServerException::serverError(
sprintf(
'Unsupported code challenge method `%s`',
$authCodePayload->code_challenge_method
)
);
// @codeCoverageIgnoreEnd
}
}
// Issue and persist access + refresh tokens
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $authCodePayload->user_id, $scopes);
$refreshToken = $this->issueRefreshToken($accessToken);
@@ -176,7 +222,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
);
if ($client instanceof ClientEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
@@ -186,13 +232,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
is_string($client->getRedirectUri())
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
} elseif (
is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
}
@@ -213,6 +259,24 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$authorizationRequest->setState($stateParameter);
$authorizationRequest->setScopes($scopes);
if ($this->enableCodeExchangeProof === true) {
$codeChallenge = $this->getQueryStringParameter('code_challenge', $request);
if ($codeChallenge === null) {
throw OAuthServerException::invalidRequest('code_challenge');
}
$codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain');
if (in_array($codeChallengeMethod, ['plain', 'S256']) === false) {
throw OAuthServerException::invalidRequest(
'code_challenge_method',
'Code challenge method must be `plain` or `S256`'
);
}
$authorizationRequest->setCodeChallenge($codeChallenge);
$authorizationRequest->setCodeChallengeMethod($codeChallengeMethod);
}
return $authorizationRequest;
}
@@ -249,12 +313,14 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
'code' => $this->encrypt(
json_encode(
[
'client_id' => $authCode->getClient()->getIdentifier(),
'redirect_uri' => $authCode->getRedirectUri(),
'auth_code_id' => $authCode->getIdentifier(),
'scopes' => $authCode->getScopes(),
'user_id' => $authCode->getUserIdentifier(),
'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'),
'client_id' => $authCode->getClient()->getIdentifier(),
'redirect_uri' => $authCode->getRedirectUri(),
'auth_code_id' => $authCode->getIdentifier(),
'scopes' => $authCode->getScopes(),
'user_id' => $authCode->getUserIdentifier(),
'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'),
'code_challenge' => $authorizationRequest->getCodeChallenge(),
'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(),
]
)
),
@@ -269,7 +335,12 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
// The user denied the client, redirect them back with an error
throw OAuthServerException::accessDenied(
'The user denied the request',
$finalRedirectUri
$this->makeRedirectUri(
$finalRedirectUri,
[
'state' => $authorizationRequest->getState(),
]
)
);
}
}

View File

@@ -41,7 +41,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
*/
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL)
{
throw new \LogicException('The Implicit Grant does nto return refresh tokens');
throw new \LogicException('The Implicit Grant does not return refresh tokens');
}
/**
@@ -51,7 +51,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
*/
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
{
throw new \LogicException('The Implicit Grant does nto return refresh tokens');
throw new \LogicException('The Implicit Grant does not return refresh tokens');
}
/**
@@ -123,7 +123,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
);
if ($client instanceof ClientEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
@@ -133,13 +133,13 @@ class ImplicitGrant extends AbstractAuthorizeGrant
is_string($client->getRedirectUri())
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
} elseif (
is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false
) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
}
@@ -207,7 +207,12 @@ class ImplicitGrant extends AbstractAuthorizeGrant
// The user denied the client, redirect them back with an error
throw OAuthServerException::accessDenied(
'The user denied the request',
$finalRedirectUri
$this->makeRedirectUri(
$finalRedirectUri,
[
'state' => $authorizationRequest->getState(),
]
)
);
}
}

View File

@@ -77,12 +77,12 @@ class PasswordGrant extends AbstractGrant
{
$username = $this->getRequestParameter('username', $request);
if (is_null($username)) {
throw OAuthServerException::invalidRequest('username', '`%s` parameter is missing');
throw OAuthServerException::invalidRequest('username');
}
$password = $this->getRequestParameter('password', $request);
if (is_null($password)) {
throw OAuthServerException::invalidRequest('password', '`%s` parameter is missing');
throw OAuthServerException::invalidRequest('password');
}
$user = $this->userRepository->getUserEntityByUserCredentials(
@@ -92,7 +92,7 @@ class PasswordGrant extends AbstractGrant
$client
);
if (!$user instanceof UserEntityInterface) {
$this->getEmitter()->emit(new RequestEvent('user.authentication.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidCredentials();
}

View File

@@ -107,7 +107,7 @@ class RefreshTokenGrant extends AbstractGrant
$refreshTokenData = json_decode($refreshToken, true);
if ($refreshTokenData['client_id'] !== $clientId) {
$this->getEmitter()->emit(new RequestEvent('refresh_token.client.failed', $request));
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_CLIENT_FAILED, $request));
throw OAuthServerException::invalidRefreshToken('Token is not linked to client');
}

View File

@@ -46,9 +46,8 @@ class AuthorizationServerMiddleware
return $exception->generateHttpResponse($response);
// @codeCoverageIgnoreStart
} catch (\Exception $exception) {
$response->getBody()->write($exception->getMessage());
return $response->withStatus(500);
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
->generateHttpResponse($response);
// @codeCoverageIgnoreEnd
}

View File

@@ -46,9 +46,8 @@ class ResourceServerMiddleware
return $exception->generateHttpResponse($response);
// @codeCoverageIgnoreStart
} catch (\Exception $exception) {
$response->getBody()->write($exception->getMessage());
return $response->withStatus(500);
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
->generateHttpResponse($response);
// @codeCoverageIgnoreEnd
}

View File

@@ -13,6 +13,10 @@ use Psr\Http\Message\ServerRequestInterface;
class RequestEvent extends Event
{
const CLIENT_AUTHENTICATION_FAILED = 'client.authentication.failed';
const USER_AUTHENTICATION_FAILED = 'user.authentication.failed';
const REFRESH_TOKEN_CLIENT_FAILED = 'refresh_token.client.failed';
/**
* @var \Psr\Http\Message\ServerRequestInterface
*/

View File

@@ -64,6 +64,18 @@ class AuthorizationRequest
*/
protected $state;
/**
* The code challenge (if provided)
* @var string
*/
protected $codeChallenge;
/**
* The code challenge method (if provided)
* @var string
*/
protected $codeChallengeMethod;
/**
* @return string
*/
@@ -175,4 +187,36 @@ class AuthorizationRequest
{
$this->state = $state;
}
/**
* @return string
*/
public function getCodeChallenge()
{
return $this->codeChallenge;
}
/**
* @param string $codeChallenge
*/
public function setCodeChallenge($codeChallenge)
{
$this->codeChallenge = $codeChallenge;
}
/**
* @return string
*/
public function getCodeChallengeMethod()
{
return $this->codeChallengeMethod;
}
/**
* @param string $codeChallengeMethod
*/
public function setCodeChallengeMethod($codeChallengeMethod)
{
$this->codeChallengeMethod = $codeChallengeMethod;
}
}

View File

@@ -32,6 +32,76 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
$grantMock->setEmitter(new Emitter());
}
public function testHttpBasicWithPassword()
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withHeader('Authorization', 'Basic ' . base64_encode('Open:Sesame'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
$this->assertSame(['Open', 'Sesame'], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
public function testHttpBasicNoPassword()
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withHeader('Authorization', 'Basic ' . base64_encode('Open:'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
$this->assertSame(['Open', ''], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
public function testHttpBasicNotBasic()
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withHeader('Authorization', 'Foo ' . base64_encode('Open:Sesame'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
$this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
public function testHttpBasicNotBase64()
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withHeader('Authorization', 'Basic ||');
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
$this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
public function testHttpBasicNoColon()
{
/** @var AbstractGrant $grantMock */
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
$abstractGrantReflection = new \ReflectionClass($grantMock);
$serverRequest = new ServerRequest();
$serverRequest = $serverRequest->withHeader('Authorization', 'Basic ' . base64_encode('OpenSesame'));
$basicAuthMethod = $abstractGrantReflection->getMethod('getBasicAuthCredentials');
$basicAuthMethod->setAccessible(true);
$this->assertSame([null, null], $basicAuthMethod->invoke($grantMock, $serverRequest));
}
public function testValidateClientPublic()
{
$client = new ClientEntity();
@@ -275,7 +345,6 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
/** @var RefreshTokenEntityInterface $refreshToken */
$refreshToken = $issueRefreshTokenMethod->invoke($grantMock, $accessToken);
$this->assertTrue($refreshToken instanceof RefreshTokenEntityInterface);
$this->assertFalse($refreshToken->isExpired());
$this->assertEquals($accessToken, $refreshToken->getAccessToken());
}
@@ -301,7 +370,6 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
[new ScopeEntity()]
);
$this->assertTrue($accessToken instanceof AccessTokenEntityInterface);
$this->assertFalse($accessToken->isExpired());
}
public function testIssueAuthCode()

View File

@@ -6,6 +6,7 @@ use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
use League\OAuth2\Server\Grant\AuthCodeGrant;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
@@ -27,7 +28,7 @@ use Zend\Diactoros\ServerRequest;
class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
{
/**
* CryptTrait stub
* @var CryptTraitStub
*/
protected $cryptStub;
@@ -136,6 +137,41 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest);
}
public function testValidateAuthorizationRequestCodeChallenge()
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$request = new ServerRequest(
[],
[],
null,
null,
'php://input',
$headers = [],
$cookies = [],
$queryParams = [
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_challenge' => 'FOOBAR',
]
);
$this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 3
@@ -272,6 +308,82 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$grant->validateAuthorizationRequest($request);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 3
*/
public function testValidateAuthorizationRequestMissingCodeChallenge()
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$request = new ServerRequest(
[],
[],
null,
null,
'php://input',
$headers = [],
$cookies = [],
$queryParams = [
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
]
);
$grant->validateAuthorizationRequest($request);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 3
*/
public function testValidateAuthorizationRequestInvalidCodeChallengeMethod()
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$request = new ServerRequest(
[],
[],
null,
null,
'php://input',
$headers = [],
$cookies = [],
$queryParams = [
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_challenge' => 'foobar',
'code_challenge_method' => 'foo',
]
);
$grant->validateAuthorizationRequest($request);
}
public function testCompleteAuthorizationRequest()
{
$authRequest = new AuthorizationRequest();
@@ -390,6 +502,150 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface);
}
public function testRespondToAccessTokenRequestCodeChallengePlain()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$request = new ServerRequest(
[],
[],
null,
'POST',
'php://input',
[],
[],
[],
[
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_verifier' => 'foobar',
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
'auth_code_id' => uniqid(),
'expire_time' => time() + 3600,
'client_id' => 'foo',
'user_id' => 123,
'scopes' => ['foo'],
'redirect_uri' => 'http://foo/bar',
'code_challenge' => 'foobar',
'code_challenge_method' => 'plain',
]
)
),
]
);
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
$this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface);
$this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface);
}
public function testRespondToAccessTokenRequestCodeChallengeS256()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$request = new ServerRequest(
[],
[],
null,
'POST',
'php://input',
[],
[],
[],
[
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_verifier' => 'foobar',
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
'auth_code_id' => uniqid(),
'expire_time' => time() + 3600,
'client_id' => 'foo',
'user_id' => 123,
'scopes' => ['foo'],
'redirect_uri' => 'http://foo/bar',
'code_challenge' => urlencode(base64_encode(hash('sha256', 'foobar'))),
'code_challenge_method' => 'S256',
]
)
),
]
);
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
$this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface);
$this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 3
@@ -710,4 +966,517 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($e->getHint(), 'Cannot decrypt the authorization code');
}
}
public function testRespondToAccessTokenRequestBadCodeVerifierPlain()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$request = new ServerRequest(
[],
[],
null,
'POST',
'php://input',
[],
[],
[],
[
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_verifier' => 'nope',
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
'auth_code_id' => uniqid(),
'expire_time' => time() + 3600,
'client_id' => 'foo',
'user_id' => 123,
'scopes' => ['foo'],
'redirect_uri' => 'http://foo/bar',
'code_challenge' => 'foobar',
'code_challenge_method' => 'plain',
]
)
),
]
);
try {
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
} catch (OAuthServerException $e) {
$this->assertEquals($e->getHint(), 'Failed to verify `code_verifier`.');
}
}
public function testRespondToAccessTokenRequestBadCodeVerifierS256()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$request = new ServerRequest(
[],
[],
null,
'POST',
'php://input',
[],
[],
[],
[
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_verifier' => 'nope',
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
'auth_code_id' => uniqid(),
'expire_time' => time() + 3600,
'client_id' => 'foo',
'user_id' => 123,
'scopes' => ['foo'],
'redirect_uri' => 'http://foo/bar',
'code_challenge' => 'foobar',
'code_challenge_method' => 'S256',
]
)
),
]
);
try {
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
} catch (OAuthServerException $e) {
$this->assertEquals($e->getHint(), 'Failed to verify `code_verifier`.');
}
}
public function testRespondToAccessTokenRequestMissingCodeVerifier()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$request = new ServerRequest(
[],
[],
null,
'POST',
'php://input',
[],
[],
[],
[
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
'auth_code_id' => uniqid(),
'expire_time' => time() + 3600,
'client_id' => 'foo',
'user_id' => 123,
'scopes' => ['foo'],
'redirect_uri' => 'http://foo/bar',
'code_challenge' => 'foobar',
'code_challenge_method' => 'plain',
]
)
),
]
);
try {
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
} catch (OAuthServerException $e) {
$this->assertEquals($e->getHint(), 'Check the `code_verifier` parameter');
}
}
public function testAuthCodeRepositoryUniqueConstraintCheck()
{
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
$authRequest->setClient(new ClientEntity());
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
$authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
$authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
$authCodeRepository->expects($this->at(0))->method('persistNewAuthCode')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
$authCodeRepository->expects($this->at(1))->method('persistNewAuthCode');
$grant = new AuthCodeGrant(
$authCodeRepository,
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 7
*/
public function testAuthCodeRepositoryFailToPersist()
{
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
$authRequest->setClient(new ClientEntity());
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
$authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
$authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
$authCodeRepository->method('persistNewAuthCode')->willThrowException(OAuthServerException::serverError('something bad happened'));
$grant = new AuthCodeGrant(
$authCodeRepository,
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse);
}
/**
* @expectedException \League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException
* @expectedExceptionCode 100
*/
public function testAuthCodeRepositoryFailToPersistUniqueNoInfiniteLoop()
{
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
$authRequest->setClient(new ClientEntity());
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
$authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
$authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
$authCodeRepository->method('persistNewAuthCode')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
$grant = new AuthCodeGrant(
$authCodeRepository,
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse);
}
public function testRefreshTokenRepositoryUniqueConstraintCheck()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$refreshTokenRepositoryMock->expects($this->at(0))->method('persistNewRefreshToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
$refreshTokenRepositoryMock->expects($this->at(1))->method('persistNewRefreshToken');
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$request = new ServerRequest(
[],
[],
null,
'POST',
'php://input',
[],
[],
[],
[
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
'auth_code_id' => uniqid(),
'expire_time' => time() + 3600,
'client_id' => 'foo',
'user_id' => 123,
'scopes' => ['foo'],
'redirect_uri' => 'http://foo/bar',
]
)
),
]
);
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
$this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface);
$this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 7
*/
public function testRefreshTokenRepositoryFailToPersist()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willThrowException(OAuthServerException::serverError('something bad happened'));
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$request = new ServerRequest(
[],
[],
null,
'POST',
'php://input',
[],
[],
[],
[
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
'auth_code_id' => uniqid(),
'expire_time' => time() + 3600,
'client_id' => 'foo',
'user_id' => 123,
'scopes' => ['foo'],
'redirect_uri' => 'http://foo/bar',
]
)
),
]
);
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
$this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface);
$this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface);
}
/**
* @expectedException \League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException
* @expectedExceptionCode 100
*/
public function testRefreshTokenRepositoryFailToPersistUniqueNoInfiniteLoop()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$request = new ServerRequest(
[],
[],
null,
'POST',
'php://input',
[],
[],
[],
[
'grant_type' => 'authorization_code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
'auth_code_id' => uniqid(),
'expire_time' => time() + 3600,
'client_id' => 'foo',
'user_id' => 123,
'scopes' => ['foo'],
'redirect_uri' => 'http://foo/bar',
]
)
),
]
);
/** @var StubResponseType $response */
$response = $grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
$this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface);
$this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface);
}
}

View File

@@ -3,6 +3,8 @@
namespace LeagueTests\Grant;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
use League\OAuth2\Server\Grant\ImplicitGrant;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
@@ -295,4 +297,76 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase
$grant->completeAuthorizationRequest($authRequest);
}
public function testAccessTokenRepositoryUniqueConstraintCheck()
{
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
$authRequest->setClient(new ClientEntity());
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
/** @var AccessTokenRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $accessTokenRepositoryMock */
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->expects($this->at(0))->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
$accessTokenRepositoryMock->expects($this->at(1))->method('persistNewAccessToken')->willReturnSelf();
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 7
*/
public function testAccessTokenRepositoryFailToPersist()
{
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
$authRequest->setClient(new ClientEntity());
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
/** @var AccessTokenRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $accessTokenRepositoryMock */
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(OAuthServerException::serverError('something bad happened'));
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->completeAuthorizationRequest($authRequest);
}
/**
* @expectedException \League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException
* @expectedExceptionCode 100
*/
public function testAccessTokenRepositoryFailToPersistUniqueNoInfiniteLoop()
{
$authRequest = new AuthorizationRequest();
$authRequest->setAuthorizationApproved(true);
$authRequest->setClient(new ClientEntity());
$authRequest->setGrantTypeId('authorization_code');
$authRequest->setUser(new UserEntity());
/** @var AccessTokenRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $accessTokenRepositoryMock */
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$accessTokenRepositoryMock->method('persistNewAccessToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->completeAuthorizationRequest($authRequest);
}
}

View File

@@ -226,4 +226,31 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase
);
}
}
public function testDetermineMissingBearerInHeader()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock);
$authorizationValidator->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$request = new ServerRequest();
$request = $request->withHeader('authorization', 'Bearer blah.blah.blah');
try {
$authorizationValidator->validateAuthorization($request);
} catch (OAuthServerException $e) {
$this->assertEquals(
'Error while decoding to JSON',
$e->getHint()
);
}
}
}