mirror of
https://github.com/elyby/oauth2-server.git
synced 2024-12-23 05:29:52 +05:30
Merge branch 'V5-WIP' into minor_merge
This commit is contained in:
commit
8b185e0580
@ -2,10 +2,10 @@
|
||||
|
||||
namespace League\OAuth2\Server\Exception;
|
||||
|
||||
use League\OAuth2\Server\Utils\RedirectUri;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequest;
|
||||
use Zend\Diactoros\Uri;
|
||||
|
||||
class OAuthServerException extends \Exception
|
||||
{
|
||||
@ -186,6 +186,7 @@ class OAuthServerException extends \Exception
|
||||
/**
|
||||
* Invalid refresh token
|
||||
*
|
||||
* @param string|null $hint
|
||||
* @return static
|
||||
*/
|
||||
public static function invalidRefreshToken($hint = null)
|
||||
@ -238,16 +239,21 @@ class OAuthServerException extends \Exception
|
||||
}
|
||||
|
||||
if ($this->redirectUri !== null) {
|
||||
$headers['Location'] = RedirectUri::make($this->redirectUri, $payload);
|
||||
$redirectUri = new Uri($this->redirectUri);
|
||||
parse_str($redirectUri->getQuery(), $redirectPayload);
|
||||
|
||||
$headers['Location'] = (string) $redirectUri->withQuery(http_build_query(
|
||||
array_merge($redirectPayload, $payload)
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($headers as $header => $content) {
|
||||
$response = $response->withHeader($header, $content);
|
||||
}
|
||||
|
||||
$response = $response->withStatus($this->getHttpStatusCode());
|
||||
$response->getBody()->write(json_encode($payload));
|
||||
return $response;
|
||||
|
||||
return $response->withStatus($this->getHttpStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,36 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Unauthorized Client Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Exception;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class UnauthorizedClientException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 400;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'unauthorized_client';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('The client is not authorized to request an access token using this method.');
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Unsupported Response Type Exception
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Exception;
|
||||
|
||||
/**
|
||||
* Exception class
|
||||
*/
|
||||
class UnsupportedResponseTypeException extends OAuthException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $httpStatusCode = 400;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $errorType = 'unsupported_response_type';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($parameter, $redirectUri = null)
|
||||
{
|
||||
parent::__construct('The authorization server does not support obtaining an access token using this method.');
|
||||
$this->redirectUri = $redirectUri;
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use League\Event\EmitterAwareTrait;
|
||||
use League\Event\EmitterInterface;
|
||||
use League\Event\Event;
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntity;
|
||||
use League\OAuth2\Server\Entities\Interfaces\ClientEntityInterface;
|
||||
@ -77,6 +78,11 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
*/
|
||||
protected $pathToPublicKey;
|
||||
|
||||
/**
|
||||
* @var \DateInterval
|
||||
*/
|
||||
protected $refreshTokenTTL;
|
||||
|
||||
/**
|
||||
* @param ClientRepositoryInterface $clientRepository
|
||||
*/
|
||||
@ -117,6 +123,22 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
$this->pathToPublicKey = $pathToPublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function setEmitter(EmitterInterface $emitter = null)
|
||||
{
|
||||
$this->emitter = $emitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL)
|
||||
{
|
||||
$this->refreshTokenTTL = $refreshTokenTTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -280,7 +302,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
{
|
||||
$refreshToken = new RefreshTokenEntity();
|
||||
$refreshToken->setIdentifier(SecureKey::generate());
|
||||
$refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('P1M')));
|
||||
$refreshToken->setExpiryDateTime((new \DateTime())->add($this->refreshTokenTTL));
|
||||
$refreshToken->setAccessToken($accessToken);
|
||||
|
||||
return $refreshToken;
|
||||
|
@ -32,14 +32,14 @@ class ClientCredentialsGrant extends AbstractGrant
|
||||
public function respondToRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $tokenTTL
|
||||
\DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
$scopes = $this->validateScopes($request, $client);
|
||||
|
||||
// Issue and persist access token
|
||||
$accessToken = $this->issueAccessToken($tokenTTL, $client, $client->getIdentifier(), $scopes);
|
||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $client->getIdentifier(), $scopes);
|
||||
$this->accessTokenRepository->persistNewAccessToken($accessToken);
|
||||
|
||||
// Inject access token into response type
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use League\Event\EmitterAwareInterface;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||
@ -24,6 +23,13 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||
*/
|
||||
interface GrantTypeInterface extends EmitterAwareInterface
|
||||
{
|
||||
/**
|
||||
* Set refresh token TTL
|
||||
*
|
||||
* @param \DateInterval $refreshTokenTTL
|
||||
*/
|
||||
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL);
|
||||
|
||||
/**
|
||||
* Return the identifier
|
||||
*
|
||||
@ -43,14 +49,14 @@ interface GrantTypeInterface extends EmitterAwareInterface
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $tokenTTL
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
*
|
||||
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
|
||||
*/
|
||||
public function respondToRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
DateInterval $tokenTTL
|
||||
\DateInterval $accessTokenTTL
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -51,6 +51,8 @@ class PasswordGrant extends AbstractGrant
|
||||
) {
|
||||
$this->userRepository = $userRepository;
|
||||
$this->refreshTokenRepository = $refreshTokenRepository;
|
||||
|
||||
$this->refreshTokenTTL = new \DateInterval('P1M');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,7 +61,7 @@ class PasswordGrant extends AbstractGrant
|
||||
public function respondToRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $tokenTTL
|
||||
\DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
@ -67,7 +69,7 @@ class PasswordGrant extends AbstractGrant
|
||||
$scopes = $this->validateScopes($request, $client);
|
||||
|
||||
// Issue and persist new tokens
|
||||
$accessToken = $this->issueAccessToken($tokenTTL, $client, $user->getIdentifier(), $scopes);
|
||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $scopes);
|
||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||
$this->accessTokenRepository->persistNewAccessToken($accessToken);
|
||||
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
|
||||
|
@ -41,6 +41,8 @@ class RefreshTokenGrant extends AbstractGrant
|
||||
public function __construct(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
||||
{
|
||||
$this->refreshTokenRepository = $refreshTokenRepository;
|
||||
|
||||
$this->refreshTokenTTL = new \DateInterval('P1M');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,8 +51,9 @@ class RefreshTokenGrant extends AbstractGrant
|
||||
public function respondToRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
\DateInterval $tokenTTL
|
||||
\DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
$oldRefreshToken = $this->validateOldRefreshToken($request, $client->getIdentifier());
|
||||
$scopes = $this->validateScopes($request, $client);
|
||||
@ -74,9 +77,9 @@ class RefreshTokenGrant extends AbstractGrant
|
||||
$this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']);
|
||||
$this->refreshTokenRepository->revokeRefreshToken($oldRefreshToken['refresh_token_id']);
|
||||
|
||||
$accessToken = $this->issueAccessToken($tokenTTL, $client, $oldRefreshToken['user_id'], $scopes);
|
||||
// Issue and persist new tokens
|
||||
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $oldRefreshToken['user_id'], $scopes);
|
||||
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||
|
||||
$this->accessTokenRepository->persistNewAccessToken($accessToken);
|
||||
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
|
||||
|
||||
|
@ -34,11 +34,13 @@ class AuthenticationServerMiddleware
|
||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
|
||||
{
|
||||
try {
|
||||
$response = $server->respondToRequest($request, $response);
|
||||
$response = $this->server->respondToRequest($request, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
return $response->withStatus(500)->write($exception->getMessage());
|
||||
$response->getBody()->write($exception->getMessage());
|
||||
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
if (in_array($response->getStatusCode(), [400, 401, 500])) {
|
||||
|
@ -33,18 +33,14 @@ class ResourceServerMiddleware
|
||||
*/
|
||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
|
||||
{
|
||||
if ($request->hasHeader('authorization') === false) {
|
||||
$exception = OAuthServerException::accessDenied('Missing authorization header');
|
||||
|
||||
try {
|
||||
$request = $this->server->getResponseType()->determineAccessTokenInHeader($request);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$response->getBody()->write($exception->getMessage());
|
||||
|
||||
$request = $this->server->getResponseType()->determineAccessTokenInHeader($request);
|
||||
|
||||
if ($request->getAttribute('oauth_access_token') === null) {
|
||||
$exception = OAuthServerException::accessDenied($request->getAttribute('oauth_access_token_error'));
|
||||
|
||||
return $exception->generateHttpResponse($response);
|
||||
return $response->withStatus(500);
|
||||
}
|
||||
|
||||
// Pass the request and response on to the next responder in the chain
|
||||
|
@ -13,7 +13,9 @@ namespace League\OAuth2\Server\ResponseTypes;
|
||||
|
||||
use League\OAuth2\Server\Entities\Interfaces\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\Interfaces\RefreshTokenEntityInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
abstract class AbstractResponseType implements ResponseTypeInterface
|
||||
{
|
||||
@ -66,10 +68,22 @@ abstract class AbstractResponseType implements ResponseTypeInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Entities\Interfaces\RefreshTokenEntityInterface $refreshToken
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRefreshToken(RefreshTokenEntityInterface $refreshToken)
|
||||
{
|
||||
$this->refreshToken = $refreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function determineAccessTokenInHeader(ServerRequestInterface $request)
|
||||
{
|
||||
if ($request->hasHeader('authorization') === false) {
|
||||
throw OAuthServerException::accessDenied('Missing "Authorization" header');
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,10 @@ use Lcobucci\JWT\Parser;
|
||||
use Lcobucci\JWT\Signer\Key;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
use League\OAuth2\Server\Entities\Interfaces\RefreshTokenEntityInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Utils\KeyCrypt;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response;
|
||||
|
||||
class BearerTokenResponse extends AbstractResponseType
|
||||
{
|
||||
@ -80,6 +80,8 @@ class BearerTokenResponse extends AbstractResponseType
|
||||
*/
|
||||
public function determineAccessTokenInHeader(ServerRequestInterface $request)
|
||||
{
|
||||
$request = parent::determineAccessTokenInHeader($request);
|
||||
|
||||
$header = $request->getHeader('authorization');
|
||||
$jwt = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0]));
|
||||
|
||||
@ -87,12 +89,12 @@ class BearerTokenResponse extends AbstractResponseType
|
||||
// Attempt to parse and validate the JWT
|
||||
$token = (new Parser())->parse($jwt);
|
||||
if ($token->verify(new Sha256(), $this->pathToPublicKey) === false) {
|
||||
return $request->withAttribute('oauth_access_token_error', 'Access token could not be verified');
|
||||
throw OAuthServerException::accessDenied('Access token could not be verified');
|
||||
}
|
||||
|
||||
// Check if token has been revoked
|
||||
if ($this->accessTokenRepository->isAccessTokenRevoked($token->getClaim('jti'))) {
|
||||
return $request->withAttribute('oauth_access_token_error', 'Access token has been revoked');
|
||||
throw OAuthServerException::accessDenied('Access token has been revoked');
|
||||
}
|
||||
|
||||
// Return the request with additional attributes
|
||||
@ -100,9 +102,9 @@ class BearerTokenResponse extends AbstractResponseType
|
||||
->withAttribute('oauth_client_id', $token->getClaim('aud'))
|
||||
->withAttribute('oauth_user_id', $token->getClaim('sub'))
|
||||
->withAttribute('oauth_scopes', $token->getClaim('scopes'));
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
} catch (\InvalidArgumentException $exception) {
|
||||
// JWT couldn't be parsed so return the request as is
|
||||
return $request->withAttribute('oauth_access_token_error', $e->getMessage());
|
||||
throw OAuthServerException::accessDenied($exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,6 @@ class Server implements EmitterAwareInterface
|
||||
*/
|
||||
protected $enabledGrantTypes = [];
|
||||
|
||||
/**
|
||||
* @var ResponseTypeInterface[]
|
||||
*/
|
||||
protected $grantResponseTypes = [];
|
||||
|
||||
/**
|
||||
* @var DateInterval[]
|
||||
*/
|
||||
@ -91,47 +86,23 @@ class Server implements EmitterAwareInterface
|
||||
$this->responseType = $responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token type that grants will return in the HTTP response
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function getResponseType()
|
||||
{
|
||||
if (!$this->responseType instanceof ResponseTypeInterface) {
|
||||
$this->responseType = new BearerTokenResponse(
|
||||
$this->privateKeyPath,
|
||||
$this->publicKeyPath,
|
||||
$this->accessTokenRepository
|
||||
);
|
||||
}
|
||||
|
||||
return $this->responseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a grant type on the server
|
||||
*
|
||||
* @param \League\OAuth2\Server\Grant\GrantTypeInterface $grantType
|
||||
* @param DateInterval $accessTokenTTL
|
||||
*/
|
||||
public function enableGrantType(
|
||||
GrantTypeInterface $grantType,
|
||||
\DateInterval $accessTokenTTL
|
||||
) {
|
||||
public function enableGrantType(GrantTypeInterface $grantType, \DateInterval $accessTokenTTL)
|
||||
{
|
||||
$grantType->setAccessTokenRepository($this->accessTokenRepository);
|
||||
$grantType->setClientRepository($this->clientRepository);
|
||||
$grantType->setScopeRepository($this->scopeRepository);
|
||||
$grantType->setPathToPrivateKey($this->privateKeyPath);
|
||||
$grantType->setPathToPublicKey($this->publicKeyPath);
|
||||
|
||||
$grantType->setEmitter($this->getEmitter());
|
||||
|
||||
$this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType;
|
||||
|
||||
// Set grant response type
|
||||
$this->grantResponseTypes[$grantType->getIdentifier()] = $this->getResponseType();
|
||||
|
||||
// Set grant access token TTL
|
||||
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL;
|
||||
}
|
||||
|
||||
@ -159,7 +130,7 @@ class Server implements EmitterAwareInterface
|
||||
if ($grantType->canRespondToRequest($request)) {
|
||||
$tokenResponse = $grantType->respondToRequest(
|
||||
$request,
|
||||
$this->grantResponseTypes[$grantType->getIdentifier()],
|
||||
$this->getResponseType(),
|
||||
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]
|
||||
);
|
||||
}
|
||||
@ -171,4 +142,22 @@ class Server implements EmitterAwareInterface
|
||||
|
||||
return $tokenResponse->generateHttpResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token type that grants will return in the HTTP response
|
||||
*
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function getResponseType()
|
||||
{
|
||||
if (!$this->responseType instanceof ResponseTypeInterface) {
|
||||
$this->responseType = new BearerTokenResponse(
|
||||
$this->privateKeyPath,
|
||||
$this->publicKeyPath,
|
||||
$this->accessTokenRepository
|
||||
);
|
||||
}
|
||||
|
||||
return $this->responseType;
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Redirect URI generator
|
||||
*
|
||||
* @package league/oauth2-server
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Utils;
|
||||
|
||||
/**
|
||||
* RedirectUri class
|
||||
*/
|
||||
class RedirectUri
|
||||
{
|
||||
/**
|
||||
* Generate a new redirect uri
|
||||
*
|
||||
* @param string $uri The base URI
|
||||
* @param array $params The query string parameters
|
||||
* @param string $queryDelimiter The query string delimiter (default: "?")
|
||||
*
|
||||
* @return string The updated URI
|
||||
*/
|
||||
public static function make($uri, $params = [], $queryDelimiter = '?')
|
||||
{
|
||||
$uri .= (strstr($uri, $queryDelimiter) === false) ? $queryDelimiter : '&';
|
||||
|
||||
return $uri . http_build_query($params);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user