Compare commits

..

34 Commits
5.0.0 ... 5.0.3

Author SHA1 Message Date
Alex Bilbie
6b18a9441a Updated changelog 2016-05-04 09:13:20 +01:00
Alex Bilbie
44ff7b33a1 Merge branch 'master' of github.com:thephpleague/oauth2-server 2016-05-04 09:10:11 +01:00
Alex Bilbie
db055f790d Revert "Remove redundant parameters in example" #553
This reverts commit 9a93dca05c.
2016-05-04 09:10:05 +01:00
Alex Bilbie
d1bc4848c8 Revert "Remove redundant parameters in example"
This reverts commit 9a93dca05c.
2016-05-04 09:07:50 +01:00
Alex Bilbie
cf63403585 Merge branch 'master' of github.com:thephpleague/oauth2-server 2016-05-04 08:56:04 +01:00
Alex Bilbie
cdf43e498e Use constant for event name instead of explicit string. Fixes #563 2016-05-04 08:55:57 +01:00
Alex Bilbie
a12fc98b0d Merge pull request #569 from ismailbaskin/patch-2
Correct wrong phpdoc
2016-05-04 08:45:58 +01:00
Alex Bilbie
019d285235 Merge pull request #570 from Themodem/master
Fixed typo in exception string
2016-05-04 08:45:37 +01:00
Lee
0bb968f413 Fixed typo in exception string 2016-05-04 15:13:48 +08:00
ismail BASKIN
88b19ad2d0 Correct wrong phpdoc 2016-05-04 00:54:36 +03:00
Alex Bilbie
6856699cab Merge pull request #564 from ismailbaskin/patch-1
Remove unused request property
2016-04-30 12:16:12 +01:00
ismail BASKIN
72cd9a62e1 Remove unused request property 2016-04-30 05:08:28 +03:00
Alex Bilbie
acf262f879 Merge pull request #553 from markinjapan/patch-1
Remove redundant parameters in getNewToken()
2016-04-27 20:58:29 +01:00
Alex Bilbie
5241309bdb Fixes #560 2016-04-27 20:53:12 +01:00
Mark
9a93dca05c Remove redundant parameters in example 2016-04-20 16:52:54 +09:00
Mark
a6b7a5cedc Remove use of redundant parameters 2016-04-20 16:52:36 +09:00
Mark
78b6bddc4d Remove redundant parameters 2016-04-20 16:29:37 +09:00
Alex Bilbie
14b6761c0f Changelog update 2016-04-19 10:28:20 +01:00
Alex Bilbie
7c61922f07 Merge pull request #551 from ivyhjk/patch-1
wrong comment "month"
2016-04-19 09:53:17 +01:00
ivyhjk
20535ad95b wrong comment "month" 2016-04-18 18:08:27 -03:00
Alex Bilbie
e885114714 Improved examples 2016-04-18 12:23:21 +01:00
Alex Bilbie
f80d0d39a4 Updated .scrutenizer.yml 2016-04-18 12:23:13 +01:00
Alex Bilbie
7bfd5b7d0d Added abstract methods for required methods 2016-04-18 12:22:15 +01:00
Alex Bilbie
143a2e32f7 Client may return an array of redirect URIs 2016-04-18 12:21:42 +01:00
Alex Bilbie
8f418cff08 Added missing state parameter in redirect response 2016-04-18 12:19:54 +01:00
Alex Bilbie
fcec1f3442 Cody tidy 2016-04-18 12:19:36 +01:00
Alex Bilbie
46e7eef14e Client could potentially return an array of redirect URIs 2016-04-18 12:12:36 +01:00
Alex Bilbie
51f44fdf17 Code tidy 2016-04-18 12:12:06 +01:00
Alex Bilbie
f8b2e80ef3 Removed unnecessary parameter usage 2016-04-18 12:10:57 +01:00
Alex Bilbie
7045785d89 Spelling fix 2016-04-18 08:41:00 +01:00
Alex Bilbie
301ddc53c7 Updated changelog 2016-04-18 08:40:34 +01:00
Alex Bilbie
2a6f900323 Updated examples 2016-04-18 08:32:58 +01:00
Alex Bilbie
fb8f47e868 Added $mustValidateSecret parameter to ClientRepositoryInterface:: getClientEntity(). Fixes #550 2016-04-18 08:32:49 +01:00
Alex Bilbie
5b192b3548 Updated README 2016-04-17 13:32:20 +01:00
16 changed files with 204 additions and 151 deletions

View File

@@ -2,7 +2,6 @@ filter:
excluded_paths: excluded_paths:
- tests/* - tests/*
- vendor/* - vendor/*
- examples/*
checks: checks:
php: php:
code_rating: true code_rating: true

View File

@@ -1,6 +1,24 @@
# Changelog # Changelog
## 5.0.0 (release 2016-04-17) ## 5.0.3 (released 2016-05-04)
* Fix hints in PasswordGrant (Issue #560)
* Add meaning of `Resource owner` to terminology.md (Issue #561)
* Use constant for event name instead of explicit string (Issue #563)
* Remove unused request property (Issue #564)
* Correct wrong phpdoc (Issue #569)
* Fixed typo in exception string (Issue #570)
## 5.0.2 (released 2016-04-18)
* `state` parameter is now correctly returned after implicit grant authorization
* Small code and docblock improvements
## 5.0.1 (released 2016-04-18)
* Fixes an issue (#550) whereby it was unclear whether or not to validate a client's secret during a request.
## 5.0.0 (released 2016-04-17)
Version 5 is a complete code rewrite. Version 5 is a complete code rewrite.

View File

@@ -43,11 +43,6 @@ You can contribute to the documentation in the [gh-pages branch](https://github.
Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CONDUCT.md) for details. Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CONDUCT.md) for details.
## Integration
- [CakePHP 3](https://github.com/uafrica/oauth-server)
- [Laravel](https://github.com/lucadegasperi/oauth2-server-laravel)
## Support ## Support
Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague/oauth2-server/issues). Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague/oauth2-server/issues).
@@ -56,7 +51,7 @@ If you have any questions about OAuth _please_ open a ticket here; please **don'
## Security ## Security
If you discover any security related issues, please email hello@alexbilbie.com instead of using the issue tracker. If you discover any security related issues, please email `hello@alexbilbie.com` instead of using the issue tracker.
## License ## License

View File

@@ -1,11 +1,4 @@
<?php <?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
use League\OAuth2\Server\ResourceServer; use League\OAuth2\Server\ResourceServer;
use OAuth2ServerExamples\Repositories\AccessTokenRepository; use OAuth2ServerExamples\Repositories\AccessTokenRepository;
@@ -16,63 +9,65 @@ use Slim\App;
include __DIR__ . '/../vendor/autoload.php'; include __DIR__ . '/../vendor/autoload.php';
$app = new App([ $app = new App([
'settings' => [ // Add the resource server to the DI container
'displayErrorDetails' => true,
],
ResourceServer::class => function () { ResourceServer::class => function () {
// Setup the authorization server
$server = new ResourceServer( $server = new ResourceServer(
new AccessTokenRepository(), new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
'file://' . __DIR__ . '/../public.key' 'file://' . __DIR__ . '/../public.key' // the authorization server's public key
); );
return $server; return $server;
}, },
]); ]);
// Add the resource server middleware which will intercept and validate requests
$app->add( $app->add(
new \League\OAuth2\Server\Middleware\ResourceServerMiddleware( new \League\OAuth2\Server\Middleware\ResourceServerMiddleware(
$app->getContainer()->get(ResourceServer::class) $app->getContainer()->get(ResourceServer::class)
) )
); );
$app->get('/users', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { // An example endpoint secured with OAuth 2.0
$app->get(
'/users',
function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
$users = [ $users = [
[ [
'id' => 123, 'id' => 123,
'name' => 'Alex', 'name' => 'Alex',
'email' => 'alex@thephpleague.com', 'email' => 'alex@thephpleague.com',
], ],
[ [
'id' => 124, 'id' => 124,
'name' => 'Frank', 'name' => 'Frank',
'email' => 'frank@thephpleague.com', 'email' => 'frank@thephpleague.com',
], ],
[ [
'id' => 125, 'id' => 125,
'name' => 'Phil', 'name' => 'Phil',
'email' => 'phil@thephpleague.com', 'email' => 'phil@thephpleague.com',
], ],
]; ];
// If the access token doesn't have the `basic` scope hide users' names // If the access token doesn't have the `basic` scope hide users' names
if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) { if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) {
for ($i = 0; $i < count($users); $i++) { for ($i = 0; $i < count($users); $i++) {
unset($users[$i]['name']); unset($users[$i]['name']);
}
} }
}
// If the access token doesn't have the `emal` scope hide users' email addresses // If the access token doesn't have the `email` scope hide users' email addresses
if (in_array('email', $request->getAttribute('oauth_scopes')) === false) { if (in_array('email', $request->getAttribute('oauth_scopes')) === false) {
for ($i = 0; $i < count($users); $i++) { for ($i = 0; $i < count($users); $i++) {
unset($users[$i]['email']); unset($users[$i]['email']);
}
} }
$response->getBody()->write(json_encode($users));
return $response->withStatus(200);
} }
);
$response->getBody()->write(json_encode($users)); $app->run();
return $response->withStatus(200);
});
$app->run();

View File

@@ -30,9 +30,9 @@ $app = new App([
$accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface $accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface
// Path to public and private keys // Path to public and private keys
$privateKey = 'file://path/to/private.key'; $privateKey = 'file://'.__DIR__.'/../private.key';
//$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase //$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase
$publicKey = 'file://path/to/public.key'; $publicKey = 'file://'.__DIR__.'/../public.key';
// Setup the authorization server // Setup the authorization server
$server = new AuthorizationServer( $server = new AuthorizationServer(

View File

@@ -1,11 +1,4 @@
<?php <?php
/**
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
use League\OAuth2\Server\AuthorizationServer; use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Exception\OAuthServerException;
@@ -18,58 +11,64 @@ use OAuth2ServerExamples\Repositories\UserRepository;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Slim\App; use Slim\App;
use Zend\Diactoros\Stream;
include __DIR__ . '/../vendor/autoload.php'; include __DIR__ . '/../vendor/autoload.php';
$app = new App([ $app = new App([
'settings' => [ // Add the authorization server to the DI container
'displayErrorDetails' => true,
],
AuthorizationServer::class => function () { AuthorizationServer::class => function () {
// Init our repositories
$clientRepository = new ClientRepository();
$accessTokenRepository = new AccessTokenRepository();
$scopeRepository = new ScopeRepository();
$userRepository = new UserRepository();
$refreshTokenRepository = new RefreshTokenRepository();
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
// Setup the authorization server // Setup the authorization server
$server = new AuthorizationServer( $server = new AuthorizationServer(
$clientRepository, new ClientRepository(), // instance of ClientRepositoryInterface
$accessTokenRepository, new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
$scopeRepository, new ScopeRepository(), // instance of ScopeRepositoryInterface
$privateKeyPath, 'file://'.__DIR__.'/../private.key', // path to private key
$publicKeyPath 'file://'.__DIR__.'/../public.key' // path to public key
); );
$grant = new PasswordGrant(
new UserRepository(), // instance of UserRepositoryInterface
new RefreshTokenRepository() // instance of RefreshTokenRepositoryInterface
);
$grant->setRefreshTokenTTL(new \DateInterval('P1M')); // refresh tokens will expire after 1 month
// Enable the password grant on the server with a token TTL of 1 hour // Enable the password grant on the server with a token TTL of 1 hour
$server->enableGrantType( $server->enableGrantType(
new PasswordGrant($userRepository, $refreshTokenRepository), $grant,
new \DateInterval('PT1H') new \DateInterval('PT1H') // access tokens will expire after 1 hour
); );
return $server; return $server;
}, },
]); ]);
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) { $app->post(
/* @var \League\OAuth2\Server\AuthorizationServer $server */ '/access_token',
$server = $app->getContainer()->get(AuthorizationServer::class); function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
try { /* @var \League\OAuth2\Server\AuthorizationServer $server */
return $server->respondToAccessTokenRequest($request, $response); $server = $app->getContainer()->get(AuthorizationServer::class);
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body); try {
// Try to respond to the access token request
return $server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $exception) {
// All instances of OAuthServerException can be converted to a PSR-7 response
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
// Catch unexpected exceptions
$body = $response->getBody();
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
}
} }
}); );
$app->run(); $app->run();

View File

@@ -17,13 +17,14 @@ class ClientRepository implements ClientRepositoryInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getClientEntity($clientIdentifier, $clientSecret = null, $redirectUri = null, $grantType = null) public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true)
{ {
$clients = [ $clients = [
'myawesomeapp' => [ 'myawesomeapp' => [
'secret' => password_hash('abc123', PASSWORD_BCRYPT), 'secret' => password_hash('abc123', PASSWORD_BCRYPT),
'name' => 'My Awesome App', 'name' => 'My Awesome App',
'redirect_uri' => 'http://foo/bar', 'redirect_uri' => 'http://foo/bar',
'is_confidential' => true,
], ],
]; ];
@@ -32,6 +33,14 @@ class ClientRepository implements ClientRepositoryInterface
return; return;
} }
if (
$mustValidateSecret === true
&& $clients[$clientIdentifier]['is_confidential'] === true
&& password_verify($clientSecret, $clients[$clientIdentifier]['secret']) === false
) {
return;
}
$client = new ClientEntity(); $client = new ClientEntity();
$client->setIdentifier($clientIdentifier); $client->setIdentifier($clientIdentifier);
$client->setName($clients[$clientIdentifier]['name']); $client->setName($clients[$clientIdentifier]['name']);

View File

@@ -156,7 +156,7 @@ class AuthorizationServer implements EmitterAwareInterface
* @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest * @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest
* @param \Psr\Http\Message\ResponseInterface $response * @param \Psr\Http\Message\ResponseInterface $response
* *
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface * @return \Psr\Http\Message\ResponseInterface
*/ */
public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response) public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response)
{ {
@@ -204,7 +204,7 @@ class AuthorizationServer implements EmitterAwareInterface
protected function getResponseType() protected function getResponseType()
{ {
if (!$this->responseType instanceof ResponseTypeInterface) { if (!$this->responseType instanceof ResponseTypeInterface) {
$this->responseType = new BearerTokenResponse($this->accessTokenRepository); $this->responseType = new BearerTokenResponse();
} }
$this->responseType->setPrivateKey($this->privateKey); $this->responseType->setPrivateKey($this->privateKey);

View File

@@ -13,6 +13,8 @@ use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key; use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Rsa\Sha256; use Lcobucci\JWT\Signer\Rsa\Sha256;
use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
trait AccessTokenTrait trait AccessTokenTrait
{ {
@@ -36,4 +38,24 @@ trait AccessTokenTrait
->sign(new Sha256(), new Key($privateKey->getKeyPath(), $privateKey->getPassPhrase())) ->sign(new Sha256(), new Key($privateKey->getKeyPath(), $privateKey->getPassPhrase()))
->getToken(); ->getToken();
} }
/**
* @return ClientEntityInterface
*/
abstract public function getClient();
/**
* @return \DateTime
*/
abstract public function getExpiryDateTime();
/**
* @return string|int
*/
abstract public function getUserIdentifier();
/**
* @return ScopeEntityInterface[]
*/
abstract public function getScopes();
} }

View File

@@ -35,11 +35,6 @@ abstract class AbstractGrant implements GrantTypeInterface
const SCOPE_DELIMITER_STRING = ' '; const SCOPE_DELIMITER_STRING = ' ';
/**
* @var ServerRequestInterface
*/
protected $request;
/** /**
* @var ClientRepositoryInterface * @var ClientRepositoryInterface
*/ */
@@ -161,11 +156,12 @@ abstract class AbstractGrant implements GrantTypeInterface
$client = $this->clientRepository->getClientEntity( $client = $this->clientRepository->getClientEntity(
$clientId, $clientId,
$this->getIdentifier(), $this->getIdentifier(),
$clientSecret $clientSecret,
true
); );
if (!$client instanceof ClientEntityInterface) { if (!$client instanceof ClientEntityInterface) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient(); throw OAuthServerException::invalidClient();
} }
@@ -176,13 +172,13 @@ abstract class AbstractGrant implements GrantTypeInterface
is_string($client->getRedirectUri()) is_string($client->getRedirectUri())
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0) && (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
) { ) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient(); throw OAuthServerException::invalidClient();
} elseif ( } elseif (
is_array($client->getRedirectUri()) is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false && in_array($redirectUri, $client->getRedirectUri()) === false
) { ) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient(); throw OAuthServerException::invalidClient();
} }
} }

View File

@@ -108,7 +108,12 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
} }
// Finalize the requested scopes // Finalize the requested scopes
$scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $authCodePayload->user_id); $scopes = $this->scopeRepository->finalizeScopes(
$scopes,
$this->getIdentifier(),
$client,
$authCodePayload->user_id
);
} catch (\LogicException $e) { } catch (\LogicException $e) {
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code'); throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code');
} }
@@ -165,11 +170,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$client = $this->clientRepository->getClientEntity( $client = $this->clientRepository->getClientEntity(
$clientId, $clientId,
$this->getIdentifier() $this->getIdentifier(),
null,
false
); );
if ($client instanceof ClientEntityInterface === false) { if ($client instanceof ClientEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient(); throw OAuthServerException::invalidClient();
} }
@@ -179,20 +186,22 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
is_string($client->getRedirectUri()) is_string($client->getRedirectUri())
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0) && (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
) { ) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient(); throw OAuthServerException::invalidClient();
} elseif ( } elseif (
is_array($client->getRedirectUri()) is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false && in_array($redirectUri, $client->getRedirectUri()) === false
) { ) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient(); throw OAuthServerException::invalidClient();
} }
} }
$scopes = $this->validateScopes( $scopes = $this->validateScopes(
$this->getQueryStringParameter('scope', $request), $this->getQueryStringParameter('scope', $request),
$client->getRedirectUri() is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri()
); );
$stateParameter = $this->getQueryStringParameter('state', $request); $stateParameter = $this->getQueryStringParameter('state', $request);
@@ -232,25 +241,25 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$authorizationRequest->getScopes() $authorizationRequest->getScopes()
); );
$redirectPayload['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'),
]
)
);
$redirectPayload['state'] = $authorizationRequest->getState();
$response = new RedirectResponse(); $response = new RedirectResponse();
$response->setRedirectUri( $response->setRedirectUri(
$this->makeRedirectUri( $this->makeRedirectUri(
$finalRedirectUri, $finalRedirectUri,
$redirectPayload [
'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'),
]
)
),
'state' => $authorizationRequest->getState(),
]
) )
); );

View File

@@ -41,7 +41,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
*/ */
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL) public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL)
{ {
throw new \LogicException('The Implicit Grant does nto return refresh tokens'); throw new \LogicException('The Implicit Grant does not return refresh tokens');
} }
/** /**
@@ -51,7 +51,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
*/ */
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository) public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
{ {
throw new \LogicException('The Implicit Grant does nto return refresh tokens'); throw new \LogicException('The Implicit Grant does not return refresh tokens');
} }
/** /**
@@ -117,11 +117,13 @@ class ImplicitGrant extends AbstractAuthorizeGrant
$client = $this->clientRepository->getClientEntity( $client = $this->clientRepository->getClientEntity(
$clientId, $clientId,
$this->getIdentifier() $this->getIdentifier(),
null,
false
); );
if ($client instanceof ClientEntityInterface === false) { if ($client instanceof ClientEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient(); throw OAuthServerException::invalidClient();
} }
@@ -131,20 +133,22 @@ class ImplicitGrant extends AbstractAuthorizeGrant
is_string($client->getRedirectUri()) is_string($client->getRedirectUri())
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0) && (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
) { ) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient(); throw OAuthServerException::invalidClient();
} elseif ( } elseif (
is_array($client->getRedirectUri()) is_array($client->getRedirectUri())
&& in_array($redirectUri, $client->getRedirectUri()) === false && in_array($redirectUri, $client->getRedirectUri()) === false
) { ) {
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request)); $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient(); throw OAuthServerException::invalidClient();
} }
} }
$scopes = $this->validateScopes( $scopes = $this->validateScopes(
$this->getQueryStringParameter('scope', $request), $this->getQueryStringParameter('scope', $request),
$client->getRedirectUri() is_array($client->getRedirectUri())
? $client->getRedirectUri()[0]
: $client->getRedirectUri()
); );
$stateParameter = $this->getQueryStringParameter('state', $request); $stateParameter = $this->getQueryStringParameter('state', $request);
@@ -183,15 +187,16 @@ class ImplicitGrant extends AbstractAuthorizeGrant
$authorizationRequest->getScopes() $authorizationRequest->getScopes()
); );
$redirectPayload['access_token'] = (string) $accessToken->convertToJWT($this->privateKey);
$redirectPayload['token_type'] = 'bearer';
$redirectPayload['expires_in'] = $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp();
$response = new RedirectResponse(); $response = new RedirectResponse();
$response->setRedirectUri( $response->setRedirectUri(
$this->makeRedirectUri( $this->makeRedirectUri(
$finalRedirectUri, $finalRedirectUri,
$redirectPayload, [
'access_token' => (string) $accessToken->convertToJWT($this->privateKey),
'token_type' => 'bearer',
'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(),
'state' => $authorizationRequest->getState(),
],
'#' '#'
) )
); );

View File

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

View File

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

View File

@@ -16,11 +16,13 @@ interface ClientRepositoryInterface extends RepositoryInterface
/** /**
* Get a client. * Get a client.
* *
* @param string $clientIdentifier The client's identifier * @param string $clientIdentifier The client's identifier
* @param string $grantType The grant type used * @param string $grantType The grant type used
* @param null|string $clientSecret The client's secret (if sent) * @param null|string $clientSecret The client's secret (if sent)
* @param bool $mustValidateSecret If true the client must attempt to validate the secret unless the client
* is confidential
* *
* @return \League\OAuth2\Server\Entities\ClientEntityInterface * @return \League\OAuth2\Server\Entities\ClientEntityInterface
*/ */
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null); public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true);
} }

View File

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