Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add token revocation support #995

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
44 changes: 44 additions & 0 deletions src/AuthorizationServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ class AuthorizationServer implements EmitterAwareInterface
*/
protected $enabledGrantTypes = [];

/**
* @var RevokeTokenHandler
*/
protected $revokeTokenHandler = null;

/**
* @var DateInterval[]
*/
Expand Down Expand Up @@ -206,6 +211,45 @@ public function respondToAccessTokenRequest(ServerRequestInterface $request, Res
throw OAuthServerException::unsupportedGrantType();
}

/**
* Enable the revoke token handler on the server.
*
* @param RevokeTokenHandler $handler
*/
public function enableRevokeTokenHandler(RevokeTokenHandler $handler)
{
$handler->setAccessTokenRepository($this->accessTokenRepository);
$handler->setClientRepository($this->clientRepository);
$handler->setEncryptionKey($this->encryptionKey);
$handler->setEmitter($this->getEmitter());

$this->revokeTokenHandler = $handler;
}

/**
* Return an revoke token response.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
*
* @throws OAuthServerException
*
* @return ResponseInterface
*/
public function respondToRevokeTokenRequest(ServerRequestInterface $request, ResponseInterface $response)
{
if ($this->revokeTokenHandler !== null) {
$revokeResponse = $this->revokeTokenHandler->respondToRevokeTokenRequest($request, $this->getResponseType());

if ($revokeResponse instanceof ResponseTypeInterface) {
return $revokeResponse->generateHttpResponse($response);
}
}

$errorMessage = 'Token revocation not supported.';
throw new OAuthServerException($errorMessage, 3, 'invalid_request', 400);
}

/**
* Get the token type that grants will return in the HTTP response.
*
Expand Down
173 changes: 10 additions & 163 deletions src/Grant/AbstractGrant.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
use League\OAuth2\Server\RequestEvent;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\RequestValidatorTrait;
use LogicException;
use Psr\Http\Message\ServerRequestInterface;
use TypeError;
Expand All @@ -41,7 +41,7 @@
*/
abstract class AbstractGrant implements GrantTypeInterface
{
use EmitterAwareTrait, CryptTrait;
use EmitterAwareTrait, CryptTrait, RequestValidatorTrait;

const SCOPE_DELIMITER_STRING = ' ';

Expand Down Expand Up @@ -92,6 +92,14 @@ abstract class AbstractGrant implements GrantTypeInterface
*/
protected $defaultScope;

/**
* @return ClientRepositoryInterface
*/
public function getClientRepository()
{
return $this->clientRepository;
}

/**
* @param ClientRepositoryInterface $clientRepository
*/
Expand Down Expand Up @@ -166,76 +174,6 @@ public function setDefaultScope($scope)
$this->defaultScope = $scope;
}

/**
* Validate the client.
*
* @param ServerRequestInterface $request
*
* @throws OAuthServerException
*
* @return ClientEntityInterface
*/
protected function validateClient(ServerRequestInterface $request)
{
list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request);

$clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
if ($clientId === null) {
throw OAuthServerException::invalidRequest('client_id');
}

// If the client is confidential require the client secret
$clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);

$client = $this->clientRepository->getClientEntity(
$clientId,
$this->getIdentifier(),
$clientSecret,
true
);

if ($client instanceof ClientEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}

$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);

if ($redirectUri !== null) {
$this->validateRedirectUri($redirectUri, $client, $request);
}

return $client;
}

/**
* Validate redirectUri from the request.
* If a redirect URI is provided ensure it matches what is pre-registered
*
* @param string $redirectUri
* @param ClientEntityInterface $client
* @param ServerRequestInterface $request
*
* @throws OAuthServerException
*/
protected function validateRedirectUri(
string $redirectUri,
ClientEntityInterface $client,
ServerRequestInterface $request
) {
if (\is_string($client->getRedirectUri())
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
} elseif (\is_array($client->getRedirectUri())
&& \in_array($redirectUri, $client->getRedirectUri(), true) === false
) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
}

/**
* Validate scopes in the request.
*
Expand Down Expand Up @@ -281,97 +219,6 @@ private function convertScopesQueryStringToArray($scopes)
});
}

/**
* Retrieve request parameter.
*
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
protected function getRequestParameter($parameter, ServerRequestInterface $request, $default = null)
{
$requestParameters = (array) $request->getParsedBody();

return $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 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.
*
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
protected function getQueryStringParameter($parameter, ServerRequestInterface $request, $default = null)
{
return isset($request->getQueryParams()[$parameter]) ? $request->getQueryParams()[$parameter] : $default;
}

/**
* Retrieve cookie parameter.
*
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
protected function getCookieParameter($parameter, ServerRequestInterface $request, $default = null)
{
return isset($request->getCookieParams()[$parameter]) ? $request->getCookieParams()[$parameter] : $default;
}

/**
* Retrieve server parameter.
*
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
protected function getServerParameter($parameter, ServerRequestInterface $request, $default = null)
{
return isset($request->getServerParams()[$parameter]) ? $request->getServerParams()[$parameter] : $default;
}

/**
* Issue an access token.
*
Expand Down
Loading