mirror of
https://github.com/elyby/oauth2-server.git
synced 2025-05-31 14:12:07 +05:30
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
68e4b1d390 | ||
|
5ee1583c5b | ||
|
66de05a395 | ||
|
df20da1235 | ||
|
7321622104 | ||
|
84187041bd | ||
|
9eccc40eb6 | ||
|
8b865cc523 | ||
|
9775c0076b | ||
|
b68ef973df | ||
|
c6e5f12a7c | ||
|
6b88cbeb13 | ||
|
64a0fcb3a6 | ||
|
78dbb267ed | ||
|
22e6a350dd | ||
|
c0936cc320 | ||
|
bb82651bec | ||
|
599c9aba75 | ||
|
4c6c189dff | ||
|
8e8aed1a50 | ||
|
4a4f4fe2d7 |
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,5 +1,18 @@
|
||||
# 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)
|
||||
|
11
README.md
11
README.md
@@ -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.
|
||||
|
@@ -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"
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -52,11 +52,4 @@ interface RefreshTokenEntityInterface
|
||||
* @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface
|
||||
*/
|
||||
public function getAccessToken();
|
||||
|
||||
/**
|
||||
* Has the token expired?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired();
|
||||
}
|
||||
|
@@ -80,11 +80,4 @@ interface TokenInterface
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
public function getScopes();
|
||||
|
||||
/**
|
||||
* Has the token expired?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired();
|
||||
}
|
||||
|
@@ -59,14 +59,4 @@ trait RefreshTokenTrait
|
||||
{
|
||||
$this->expiryDateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the token expired?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired()
|
||||
{
|
||||
return (new DateTime()) > $this->getExpiryDateTime();
|
||||
}
|
||||
}
|
||||
|
@@ -114,14 +114,4 @@ trait TokenEntityTrait
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the token expired?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired()
|
||||
{
|
||||
return (new DateTime()) > $this->getExpiryDateTime();
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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,6 +36,8 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
|
||||
const SCOPE_DELIMITER_STRING = ' ';
|
||||
|
||||
const MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS = 10;
|
||||
|
||||
/**
|
||||
* @var ClientRepositoryInterface
|
||||
*/
|
||||
@@ -137,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,
|
||||
@@ -237,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.
|
||||
*
|
||||
@@ -295,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -328,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);
|
||||
@@ -339,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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);
|
||||
@@ -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(),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -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(),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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()
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user