mirror of
https://github.com/elyby/oauth2-server.git
synced 2025-05-31 14:12:07 +05:30
Compare commits
69 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b8b92e5925 | ||
|
0ebdcd2ab8 | ||
|
9dee08ba3d | ||
|
065ef5db99 | ||
|
039537ebe2 | ||
|
d8930af5ee | ||
|
ada8d20be6 | ||
|
090c01d3d1 | ||
|
4b6ba5859c | ||
|
57323f38f7 | ||
|
46cd448a47 | ||
|
c874c59b9c | ||
|
c3a4670c11 | ||
|
17b6e2a207 | ||
|
54422a244f | ||
|
9899aa1f99 | ||
|
32efd091a1 | ||
|
68e4b1d390 | ||
|
5ee1583c5b | ||
|
66de05a395 | ||
|
df20da1235 | ||
|
7321622104 | ||
|
84187041bd | ||
|
9eccc40eb6 | ||
|
8b865cc523 | ||
|
9775c0076b | ||
|
b68ef973df | ||
|
c6e5f12a7c | ||
|
6b88cbeb13 | ||
|
64a0fcb3a6 | ||
|
78dbb267ed | ||
|
22e6a350dd | ||
|
c0936cc320 | ||
|
bb82651bec | ||
|
599c9aba75 | ||
|
4c6c189dff | ||
|
8e8aed1a50 | ||
|
4a4f4fe2d7 | ||
|
6b18a9441a | ||
|
44ff7b33a1 | ||
|
db055f790d | ||
|
d1bc4848c8 | ||
|
cf63403585 | ||
|
cdf43e498e | ||
|
a12fc98b0d | ||
|
019d285235 | ||
|
0bb968f413 | ||
|
88b19ad2d0 | ||
|
6856699cab | ||
|
72cd9a62e1 | ||
|
acf262f879 | ||
|
5241309bdb | ||
|
9a93dca05c | ||
|
a6b7a5cedc | ||
|
78b6bddc4d | ||
|
14b6761c0f | ||
|
7c61922f07 | ||
|
20535ad95b | ||
|
e885114714 | ||
|
f80d0d39a4 | ||
|
7bfd5b7d0d | ||
|
143a2e32f7 | ||
|
8f418cff08 | ||
|
fcec1f3442 | ||
|
46e7eef14e | ||
|
51f44fdf17 | ||
|
f8b2e80ef3 | ||
|
7045785d89 | ||
|
301ddc53c7 |
@@ -2,7 +2,6 @@ filter:
|
||||
excluded_paths:
|
||||
- tests/*
|
||||
- vendor/*
|
||||
- examples/*
|
||||
checks:
|
||||
php:
|
||||
code_rating: true
|
||||
|
41
CHANGELOG.md
41
CHANGELOG.md
@@ -1,6 +1,45 @@
|
||||
# Changelog
|
||||
|
||||
## 5.0.0 (release 2016-04-17)
|
||||
## 5.1.1 (released 2016-07-26)
|
||||
|
||||
* Improved test suite (Issue #614)
|
||||
* Updated docblocks (Issue #616)
|
||||
* Replace `array_shift` with `foreach` loop (Issue #621)
|
||||
* Allow easy addition of custom fields to Bearer token response (Issue #624)
|
||||
* Key file auto-generation from string (Issue #625)
|
||||
|
||||
## 5.1.0 (released 2016-06-28)
|
||||
|
||||
* Implemented RFC7636 (Issue #574)
|
||||
* Unify middleware exception responses (Issue #578)
|
||||
* Updated examples (Issue #589)
|
||||
* Ensure state is in access denied redirect (Issue #597)
|
||||
* Remove redundant `isExpired()` method from entity interfaces and traits (Issue #600)
|
||||
* Added a check for unique access token constraint violation (Issue #601)
|
||||
* Look at Authorization header directly for HTTP Basic auth checks (Issue #604)
|
||||
* Added catch Runtime exception when parsing JWT string (Issue #605)
|
||||
* Allow `paragonie/random_compat` 2.x (Issue #606)
|
||||
* Added `indigophp/hash-compat` to Composer suggestions and `require-dev` for PHP 5.5 support
|
||||
|
||||
## 5.0.3 (released 2016-05-04)
|
||||
|
||||
* Fix hints in PasswordGrant (Issue #560)
|
||||
* Add meaning of `Resource owner` to terminology.md (Issue #561)
|
||||
* Use constant for event name instead of explicit string (Issue #563)
|
||||
* Remove unused request property (Issue #564)
|
||||
* Correct wrong phpdoc (Issue #569)
|
||||
* Fixed typo in exception string (Issue #570)
|
||||
|
||||
## 5.0.2 (released 2016-04-18)
|
||||
|
||||
* `state` parameter is now correctly returned after implicit grant authorization
|
||||
* 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.
|
||||
|
||||
|
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"
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,4 @@
|
||||
<?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 OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
@@ -16,63 +9,65 @@ use Slim\App;
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
// Add the resource server to the DI container
|
||||
ResourceServer::class => function () {
|
||||
// Setup the authorization server
|
||||
$server = new ResourceServer(
|
||||
new AccessTokenRepository(),
|
||||
'file://' . __DIR__ . '/../public.key'
|
||||
new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
|
||||
'file://' . __DIR__ . '/../public.key' // the authorization server's public key
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
// Add the resource server middleware which will intercept and validate requests
|
||||
$app->add(
|
||||
new \League\OAuth2\Server\Middleware\ResourceServerMiddleware(
|
||||
$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 = [
|
||||
[
|
||||
'id' => 123,
|
||||
'name' => 'Alex',
|
||||
'email' => 'alex@thephpleague.com',
|
||||
],
|
||||
[
|
||||
'id' => 124,
|
||||
'name' => 'Frank',
|
||||
'email' => 'frank@thephpleague.com',
|
||||
],
|
||||
[
|
||||
'id' => 125,
|
||||
'name' => 'Phil',
|
||||
'email' => 'phil@thephpleague.com',
|
||||
],
|
||||
];
|
||||
$users = [
|
||||
[
|
||||
'id' => 123,
|
||||
'name' => 'Alex',
|
||||
'email' => 'alex@thephpleague.com',
|
||||
],
|
||||
[
|
||||
'id' => 124,
|
||||
'name' => 'Frank',
|
||||
'email' => 'frank@thephpleague.com',
|
||||
],
|
||||
[
|
||||
'id' => 125,
|
||||
'name' => 'Phil',
|
||||
'email' => 'phil@thephpleague.com',
|
||||
],
|
||||
];
|
||||
|
||||
// If the access token doesn't have the `basic` scope hide users' names
|
||||
if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < count($users); $i++) {
|
||||
unset($users[$i]['name']);
|
||||
// If the access token doesn't have the `basic` scope hide users' names
|
||||
if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < count($users); $i++) {
|
||||
unset($users[$i]['name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the access token doesn't have the `emal` scope hide users' email addresses
|
||||
if (in_array('email', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < count($users); $i++) {
|
||||
unset($users[$i]['email']);
|
||||
// If the access token doesn't have the `email` scope hide users' email addresses
|
||||
if (in_array('email', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < count($users); $i++) {
|
||||
unset($users[$i]['email']);
|
||||
}
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode($users));
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
);
|
||||
|
||||
$response->getBody()->write(json_encode($users));
|
||||
|
||||
return $response->withStatus(200);
|
||||
});
|
||||
|
||||
$app->run();
|
||||
$app->run();
|
@@ -29,7 +29,7 @@ $app = new App([
|
||||
],
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientReptository();
|
||||
$clientRepository = new ClientRepository();
|
||||
$scopeRepository = new ScopeRepository();
|
||||
$accessTokenRepository = new AccessTokenRepository();
|
||||
$authCodeRepository = new AuthCodeRepository();
|
||||
|
@@ -1,11 +1,4 @@
|
||||
<?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\Exception\OAuthServerException;
|
||||
@@ -18,58 +11,64 @@ use OAuth2ServerExamples\Repositories\UserRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
// Add the authorization server to the DI container
|
||||
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
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKeyPath,
|
||||
$publicKeyPath
|
||||
new ClientRepository(), // instance of ClientRepositoryInterface
|
||||
new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
|
||||
new ScopeRepository(), // instance of ScopeRepositoryInterface
|
||||
'file://'.__DIR__.'/../private.key', // path to private key
|
||||
'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
|
||||
$server->enableGrantType(
|
||||
new PasswordGrant($userRepository, $refreshTokenRepository),
|
||||
new \DateInterval('PT1H')
|
||||
$grant,
|
||||
new \DateInterval('PT1H') // access tokens will expire after 1 hour
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
$app->post(
|
||||
'/access_token',
|
||||
function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
|
||||
try {
|
||||
return $server->respondToAccessTokenRequest($request, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
$body = new Stream('php://temp', 'r+');
|
||||
$body->write($exception->getMessage());
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
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();
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -9,7 +9,6 @@
|
||||
|
||||
namespace League\OAuth2\Server;
|
||||
|
||||
use DateInterval;
|
||||
use League\Event\EmitterAwareInterface;
|
||||
use League\Event\EmitterAwareTrait;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
@@ -28,7 +27,7 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
use EmitterAwareTrait;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Grant\GrantTypeInterface[]
|
||||
* @var GrantTypeInterface[]
|
||||
*/
|
||||
protected $enabledGrantTypes = [];
|
||||
|
||||
@@ -38,44 +37,44 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
protected $grantTypeAccessTokenTTL = [];
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\CryptKey
|
||||
* @var CryptKey
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\CryptKey
|
||||
* @var CryptKey
|
||||
*/
|
||||
protected $publicKey;
|
||||
|
||||
/**
|
||||
* @var ResponseTypeInterface
|
||||
* @var null|ResponseTypeInterface
|
||||
*/
|
||||
protected $responseType;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Repositories\ClientRepositoryInterface
|
||||
* @var ClientRepositoryInterface
|
||||
*/
|
||||
private $clientRepository;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface
|
||||
* @var AccessTokenRepositoryInterface
|
||||
*/
|
||||
private $accessTokenRepository;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Repositories\ScopeRepositoryInterface
|
||||
* @var ScopeRepositoryInterface
|
||||
*/
|
||||
private $scopeRepository;
|
||||
|
||||
/**
|
||||
* New server instance.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Repositories\ClientRepositoryInterface $clientRepository
|
||||
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
|
||||
* @param \League\OAuth2\Server\Repositories\ScopeRepositoryInterface $scopeRepository
|
||||
* @param \League\OAuth2\Server\CryptKey|string $privateKey
|
||||
* @param \League\OAuth2\Server\CryptKey|string $publicKey
|
||||
* @param null|\League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
|
||||
* @param ClientRepositoryInterface $clientRepository
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
* @param ScopeRepositoryInterface $scopeRepository
|
||||
* @param CryptKey|string $privateKey
|
||||
* @param CryptKey|string $publicKey
|
||||
* @param null|ResponseTypeInterface $responseType
|
||||
*/
|
||||
public function __construct(
|
||||
ClientRepositoryInterface $clientRepository,
|
||||
@@ -89,12 +88,12 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
$this->accessTokenRepository = $accessTokenRepository;
|
||||
$this->scopeRepository = $scopeRepository;
|
||||
|
||||
if (!$privateKey instanceof CryptKey) {
|
||||
if ($privateKey instanceof CryptKey === false) {
|
||||
$privateKey = new CryptKey($privateKey);
|
||||
}
|
||||
$this->privateKey = $privateKey;
|
||||
|
||||
if (!$publicKey instanceof CryptKey) {
|
||||
if ($publicKey instanceof CryptKey === false) {
|
||||
$publicKey = new CryptKey($publicKey);
|
||||
}
|
||||
$this->publicKey = $publicKey;
|
||||
@@ -105,12 +104,12 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
/**
|
||||
* Enable a grant type on the server.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Grant\GrantTypeInterface $grantType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param GrantTypeInterface $grantType
|
||||
* @param null|\DateInterval $accessTokenTTL
|
||||
*/
|
||||
public function enableGrantType(GrantTypeInterface $grantType, DateInterval $accessTokenTTL = null)
|
||||
public function enableGrantType(GrantTypeInterface $grantType, \DateInterval $accessTokenTTL = null)
|
||||
{
|
||||
if ($accessTokenTTL instanceof DateInterval === false) {
|
||||
if ($accessTokenTTL instanceof \DateInterval === false) {
|
||||
$accessTokenTTL = new \DateInterval('PT1H');
|
||||
}
|
||||
|
||||
@@ -128,22 +127,17 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
/**
|
||||
* Validate an authorization request
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\OAuthServerException
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return \League\OAuth2\Server\RequestTypes\AuthorizationRequest|null
|
||||
* @return AuthorizationRequest
|
||||
*/
|
||||
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||
{
|
||||
$authRequest = null;
|
||||
$enabledGrantTypes = $this->enabledGrantTypes;
|
||||
while ($authRequest === null && $grantType = array_shift($enabledGrantTypes)) {
|
||||
/** @var \League\OAuth2\Server\Grant\GrantTypeInterface $grantType */
|
||||
foreach ($this->enabledGrantTypes as $grantType) {
|
||||
if ($grantType->canRespondToAuthorizationRequest($request)) {
|
||||
$authRequest = $grantType->validateAuthorizationRequest($request);
|
||||
|
||||
return $authRequest;
|
||||
return $grantType->validateAuthorizationRequest($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,10 +147,10 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
/**
|
||||
* Complete an authorization request
|
||||
*
|
||||
* @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest
|
||||
* @param \Psr\Http\Message\ResponseInterface $response
|
||||
* @param AuthorizationRequest $authRequest
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response)
|
||||
{
|
||||
@@ -168,29 +162,27 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
/**
|
||||
* Return an access token response.
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param \Psr\Http\Message\ResponseInterface $response
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\OAuthServerException
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
$tokenResponse = null;
|
||||
while ($tokenResponse === null && $grantType = array_shift($this->enabledGrantTypes)) {
|
||||
/** @var \League\OAuth2\Server\Grant\GrantTypeInterface $grantType */
|
||||
foreach ($this->enabledGrantTypes as $grantType) {
|
||||
if ($grantType->canRespondToAccessTokenRequest($request)) {
|
||||
$tokenResponse = $grantType->respondToAccessTokenRequest(
|
||||
$request,
|
||||
$this->getResponseType(),
|
||||
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($tokenResponse instanceof ResponseTypeInterface) {
|
||||
return $tokenResponse->generateHttpResponse($response);
|
||||
if ($tokenResponse instanceof ResponseTypeInterface) {
|
||||
return $tokenResponse->generateHttpResponse($response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw OAuthServerException::unsupportedGrantType();
|
||||
@@ -203,8 +195,8 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
*/
|
||||
protected function getResponseType()
|
||||
{
|
||||
if (!$this->responseType instanceof ResponseTypeInterface) {
|
||||
$this->responseType = new BearerTokenResponse($this->accessTokenRepository);
|
||||
if ($this->responseType instanceof ResponseTypeInterface === false) {
|
||||
$this->responseType = new BearerTokenResponse();
|
||||
}
|
||||
|
||||
$this->responseType->setPrivateKey($this->privateKey);
|
||||
|
@@ -22,14 +22,12 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
|
||||
use CryptTrait;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface
|
||||
* @var AccessTokenRepositoryInterface
|
||||
*/
|
||||
private $accessTokenRepository;
|
||||
|
||||
/**
|
||||
* BearerTokenValidator constructor.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
*/
|
||||
public function __construct(AccessTokenRepositoryInterface $accessTokenRepository)
|
||||
{
|
||||
@@ -77,6 +75,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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,17 +8,21 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server;
|
||||
|
||||
class CryptKey
|
||||
{
|
||||
const RSA_KEY_PATTERN =
|
||||
'/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----\n)(.|\n)+(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)$/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $keyPath;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var null|string
|
||||
*/
|
||||
protected $passPhrase;
|
||||
|
||||
@@ -28,6 +32,10 @@ class CryptKey
|
||||
*/
|
||||
public function __construct($keyPath, $passPhrase = null)
|
||||
{
|
||||
if (preg_match(self::RSA_KEY_PATTERN, $keyPath)) {
|
||||
$keyPath = $this->saveKeyToFile($keyPath);
|
||||
}
|
||||
|
||||
if (strpos($keyPath, 'file://') !== 0) {
|
||||
$keyPath = 'file://' . $keyPath;
|
||||
}
|
||||
@@ -40,6 +48,28 @@ class CryptKey
|
||||
$this->passPhrase = $passPhrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function saveKeyToFile($key)
|
||||
{
|
||||
$keyPath = sys_get_temp_dir() . '/' . sha1($key) . '.key';
|
||||
|
||||
if (!file_exists($keyPath) && !touch($keyPath)) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new \RuntimeException('"%s" key file could not be created', $keyPath);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
file_put_contents($keyPath, $key);
|
||||
|
||||
return 'file://' . $keyPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve key path.
|
||||
*
|
||||
|
@@ -8,24 +8,25 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server;
|
||||
|
||||
trait CryptTrait
|
||||
{
|
||||
/**
|
||||
* @var \League\OAuth2\Server\CryptKey
|
||||
* @var CryptKey
|
||||
*/
|
||||
protected $privateKey;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\CryptKey
|
||||
* @var CryptKey
|
||||
*/
|
||||
protected $publicKey;
|
||||
|
||||
/**
|
||||
* Set path to private key.
|
||||
*
|
||||
* @param \League\OAuth2\Server\CryptKey $privateKey
|
||||
* @param CryptKey $privateKey
|
||||
*/
|
||||
public function setPrivateKey(CryptKey $privateKey)
|
||||
{
|
||||
@@ -35,7 +36,7 @@ trait CryptTrait
|
||||
/**
|
||||
* Set path to public key.
|
||||
*
|
||||
* @param \League\OAuth2\Server\CryptKey $publicKey
|
||||
* @param CryptKey $publicKey
|
||||
*/
|
||||
public function setPublicKey(CryptKey $publicKey)
|
||||
{
|
||||
@@ -47,6 +48,8 @@ trait CryptTrait
|
||||
*
|
||||
* @param string $unencryptedData
|
||||
*
|
||||
* @throws \LogicException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function encrypt($unencryptedData)
|
||||
|
@@ -16,7 +16,7 @@ interface AccessTokenEntityInterface extends TokenInterface
|
||||
/**
|
||||
* Generate a JWT from the access token
|
||||
*
|
||||
* @param \League\OAuth2\Server\CryptKey $privateKey
|
||||
* @param CryptKey $privateKey
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
@@ -42,21 +42,14 @@ interface RefreshTokenEntityInterface
|
||||
/**
|
||||
* Set the access token that the refresh token was associated with.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
|
||||
* @param AccessTokenEntityInterface $accessToken
|
||||
*/
|
||||
public function setAccessToken(AccessTokenEntityInterface $accessToken);
|
||||
|
||||
/**
|
||||
* Get the access token that the refresh token was originally associated with.
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface
|
||||
* @return AccessTokenEntityInterface
|
||||
*/
|
||||
public function getAccessToken();
|
||||
|
||||
/**
|
||||
* Has the token expired?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired();
|
||||
}
|
||||
|
@@ -63,14 +63,14 @@ interface TokenInterface
|
||||
/**
|
||||
* Set the client that the token was issued to.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
|
||||
* @param ClientEntityInterface $client
|
||||
*/
|
||||
public function setClient(ClientEntityInterface $client);
|
||||
|
||||
/**
|
||||
* Associate a scope with the token.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface $scope
|
||||
* @param ScopeEntityInterface $scope
|
||||
*/
|
||||
public function addScope(ScopeEntityInterface $scope);
|
||||
|
||||
@@ -80,11 +80,4 @@ interface TokenInterface
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
public function getScopes();
|
||||
|
||||
/**
|
||||
* Has the token expired?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired();
|
||||
}
|
||||
|
@@ -13,13 +13,15 @@ use Lcobucci\JWT\Builder;
|
||||
use Lcobucci\JWT\Signer\Key;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
|
||||
trait AccessTokenTrait
|
||||
{
|
||||
/**
|
||||
* Generate a JWT from the access token
|
||||
*
|
||||
* @param \League\OAuth2\Server\CryptKey $privateKey
|
||||
* @param CryptKey $privateKey
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -36,4 +38,24 @@ trait AccessTokenTrait
|
||||
->sign(new Sha256(), new Key($privateKey->getKeyPath(), $privateKey->getPassPhrase()))
|
||||
->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();
|
||||
}
|
||||
|
@@ -11,8 +11,14 @@ namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
trait ClientTrait
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var string|string[]
|
||||
*/
|
||||
protected $redirectUri;
|
||||
|
||||
/**
|
||||
|
@@ -9,7 +9,6 @@
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
use DateTime;
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
|
||||
trait RefreshTokenTrait
|
||||
@@ -20,7 +19,7 @@ trait RefreshTokenTrait
|
||||
protected $accessToken;
|
||||
|
||||
/**
|
||||
* @var DateTime
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $expiryDateTime;
|
||||
|
||||
@@ -43,7 +42,7 @@ trait RefreshTokenTrait
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return DateTime
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getExpiryDateTime()
|
||||
{
|
||||
@@ -53,20 +52,10 @@ trait RefreshTokenTrait
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param DateTime $dateTime
|
||||
* @param \DateTime $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(DateTime $dateTime)
|
||||
public function setExpiryDateTime(\DateTime $dateTime)
|
||||
{
|
||||
$this->expiryDateTime = $dateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the token expired?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired()
|
||||
{
|
||||
return (new DateTime()) > $this->getExpiryDateTime();
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,6 @@
|
||||
|
||||
namespace League\OAuth2\Server\Entities\Traits;
|
||||
|
||||
use DateTime;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
|
||||
@@ -21,7 +20,7 @@ trait TokenEntityTrait
|
||||
protected $scopes = [];
|
||||
|
||||
/**
|
||||
* @var DateTime
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $expiryDateTime;
|
||||
|
||||
@@ -38,7 +37,7 @@ trait TokenEntityTrait
|
||||
/**
|
||||
* Associate a scope with the token.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface $scope
|
||||
* @param ScopeEntityInterface $scope
|
||||
*/
|
||||
public function addScope(ScopeEntityInterface $scope)
|
||||
{
|
||||
@@ -58,7 +57,7 @@ trait TokenEntityTrait
|
||||
/**
|
||||
* Get the token's expiry date time.
|
||||
*
|
||||
* @return DateTime
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getExpiryDateTime()
|
||||
{
|
||||
@@ -68,9 +67,9 @@ trait TokenEntityTrait
|
||||
/**
|
||||
* Set the date time when the token expires.
|
||||
*
|
||||
* @param DateTime $dateTime
|
||||
* @param \DateTime $dateTime
|
||||
*/
|
||||
public function setExpiryDateTime(DateTime $dateTime)
|
||||
public function setExpiryDateTime(\DateTime $dateTime)
|
||||
{
|
||||
$this->expiryDateTime = $dateTime;
|
||||
}
|
||||
@@ -108,20 +107,10 @@ trait TokenEntityTrait
|
||||
/**
|
||||
* Set the client that the token was issued to.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
|
||||
* @param ClientEntityInterface $client
|
||||
*/
|
||||
public function setClient(ClientEntityInterface $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the token expired?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isExpired()
|
||||
{
|
||||
return (new DateTime()) > $this->getExpiryDateTime();
|
||||
}
|
||||
}
|
||||
|
@@ -69,7 +69,7 @@ class OAuthServerException extends \Exception
|
||||
* Invalid request error.
|
||||
*
|
||||
* @param string $parameter The invalid parameter
|
||||
* @param string|null $hint
|
||||
* @param null|string $hint
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
@@ -143,7 +143,7 @@ class OAuthServerException extends \Exception
|
||||
/**
|
||||
* Invalid refresh token.
|
||||
*
|
||||
* @param string|null $hint
|
||||
* @param null|string $hint
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
@@ -155,8 +155,8 @@ class OAuthServerException extends \Exception
|
||||
/**
|
||||
* Access denied.
|
||||
*
|
||||
* @param string|null $hint
|
||||
* @param string|null $redirectUri
|
||||
* @param null|string $hint
|
||||
* @param null|string $redirectUri
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
@@ -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
|
||||
*/
|
||||
@@ -183,11 +203,10 @@ class OAuthServerException extends \Exception
|
||||
/**
|
||||
* Generate a HTTP response.
|
||||
*
|
||||
* @param \Psr\Http\Message\ResponseInterface $response
|
||||
* @param bool $useFragment True if errors should be in the URI fragment instead of
|
||||
* query string
|
||||
* @param ResponseInterface $response
|
||||
* @param bool $useFragment True if errors should be in the URI fragment instead of query string
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function generateHttpResponse(ResponseInterface $response, $useFragment = false)
|
||||
{
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -13,9 +13,12 @@ namespace League\OAuth2\Server\Grant;
|
||||
use League\Event\EmitterAwareTrait;
|
||||
use League\OAuth2\Server\CryptTrait;
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||
@@ -35,10 +38,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
|
||||
const SCOPE_DELIMITER_STRING = ' ';
|
||||
|
||||
/**
|
||||
* @var ServerRequestInterface
|
||||
*/
|
||||
protected $request;
|
||||
const MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS = 10;
|
||||
|
||||
/**
|
||||
* @var ClientRepositoryInterface
|
||||
@@ -56,17 +56,17 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
protected $scopeRepository;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface
|
||||
* @var AuthCodeRepositoryInterface
|
||||
*/
|
||||
protected $authCodeRepository;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface
|
||||
* @var RefreshTokenRepositoryInterface
|
||||
*/
|
||||
protected $refreshTokenRepository;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Repositories\UserRepositoryInterface
|
||||
* @var UserRepositoryInterface
|
||||
*/
|
||||
protected $userRepository;
|
||||
|
||||
@@ -100,7 +100,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
*/
|
||||
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
||||
{
|
||||
@@ -108,7 +108,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository
|
||||
* @param AuthCodeRepositoryInterface $authCodeRepository
|
||||
*/
|
||||
public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository)
|
||||
{
|
||||
@@ -116,7 +116,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Repositories\UserRepositoryInterface $userRepository
|
||||
* @param UserRepositoryInterface $userRepository
|
||||
*/
|
||||
public function setUserRepository(UserRepositoryInterface $userRepository)
|
||||
{
|
||||
@@ -134,29 +134,23 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
/**
|
||||
* Validate the client.
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\OAuthServerException
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\ClientEntityInterface
|
||||
* @return ClientEntityInterface
|
||||
*/
|
||||
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,
|
||||
@@ -165,8 +159,8 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
true
|
||||
);
|
||||
|
||||
if (!$client instanceof ClientEntityInterface) {
|
||||
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
|
||||
if ($client instanceof ClientEntityInterface === false) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
|
||||
@@ -177,13 +171,13 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
is_string($client->getRedirectUri())
|
||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} elseif (
|
||||
is_array($client->getRedirectUri())
|
||||
&& in_array($redirectUri, $client->getRedirectUri()) === false
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
}
|
||||
@@ -197,9 +191,9 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
* @param string $scopes
|
||||
* @param string $redirectUri
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\OAuthServerException
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
public function validateScopes(
|
||||
$scopes,
|
||||
@@ -216,7 +210,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
foreach ($scopesList as $scopeItem) {
|
||||
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
|
||||
|
||||
if (!$scope instanceof ScopeEntityInterface) {
|
||||
if ($scope instanceof ScopeEntityInterface === false) {
|
||||
throw OAuthServerException::invalidScope($scopeItem, $redirectUri);
|
||||
}
|
||||
|
||||
@@ -229,9 +223,9 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
/**
|
||||
* Retrieve request parameter.
|
||||
*
|
||||
* @param string $parameter
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
* @param string $parameter
|
||||
* @param ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
@@ -242,12 +236,45 @@ 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 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 \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
* @param string $parameter
|
||||
* @param ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
@@ -259,9 +286,9 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
/**
|
||||
* Retrieve cookie parameter.
|
||||
*
|
||||
* @param string $parameter
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
* @param string $parameter
|
||||
* @param ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
@@ -273,9 +300,9 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
/**
|
||||
* Retrieve server parameter.
|
||||
*
|
||||
* @param string $parameter
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
* @param string $parameter
|
||||
* @param ServerRequestInterface $request
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
@@ -287,12 +314,15 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
/**
|
||||
* Issue an access token.
|
||||
*
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
|
||||
* @param string $userIdentifier
|
||||
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param ClientEntityInterface $client
|
||||
* @param string $userIdentifier
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface
|
||||
* @throws OAuthServerException
|
||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||
*
|
||||
* @return AccessTokenEntityInterface
|
||||
*/
|
||||
protected function issueAccessToken(
|
||||
\DateInterval $accessTokenTTL,
|
||||
@@ -300,31 +330,43 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue an auth code.
|
||||
*
|
||||
* @param \DateInterval $authCodeTTL
|
||||
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
|
||||
* @param string $userIdentifier
|
||||
* @param string $redirectUri
|
||||
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
|
||||
* @param \DateInterval $authCodeTTL
|
||||
* @param ClientEntityInterface $client
|
||||
* @param string $userIdentifier
|
||||
* @param string $redirectUri
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\AuthCodeEntityInterface
|
||||
* @throws OAuthServerException
|
||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||
*
|
||||
* @return AuthCodeEntityInterface
|
||||
*/
|
||||
protected function issueAuthCode(
|
||||
\DateInterval $authCodeTTL,
|
||||
@@ -333,8 +375,9 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
$redirectUri,
|
||||
array $scopes = []
|
||||
) {
|
||||
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||
|
||||
$authCode = $this->authCodeRepository->getNewAuthCode();
|
||||
$authCode->setIdentifier($this->generateUniqueIdentifier());
|
||||
$authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL));
|
||||
$authCode->setClient($client);
|
||||
$authCode->setUserIdentifier($userIdentifier);
|
||||
@@ -344,26 +387,46 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
|
||||
* @param AccessTokenEntityInterface $accessToken
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\RefreshTokenEntityInterface
|
||||
* @throws OAuthServerException
|
||||
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||
*
|
||||
* @return RefreshTokenEntityInterface
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,7 +434,7 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
*
|
||||
* @param int $length
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\OAuthServerException
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
@@ -9,7 +9,6 @@
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use DateInterval;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||
@@ -30,9 +29,14 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
private $authCodeTTL;
|
||||
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository
|
||||
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
* @param \DateInterval $authCodeTTL
|
||||
* @var bool
|
||||
*/
|
||||
private $enableCodeExchangeProof = false;
|
||||
|
||||
/**
|
||||
* @param AuthCodeRepositoryInterface $authCodeRepository
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
* @param \DateInterval $authCodeTTL
|
||||
*/
|
||||
public function __construct(
|
||||
AuthCodeRepositoryInterface $authCodeRepository,
|
||||
@@ -45,21 +49,26 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
$this->refreshTokenTTL = new \DateInterval('P1M');
|
||||
}
|
||||
|
||||
public function enableCodeExchangeProof()
|
||||
{
|
||||
$this->enableCodeExchangeProof = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to an access token request.
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\OAuthServerException
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
ResponseTypeInterface $responseType,
|
||||
DateInterval $accessTokenTTL
|
||||
\DateInterval $accessTokenTTL
|
||||
) {
|
||||
// Validate request
|
||||
$client = $this->validateClient($request);
|
||||
@@ -98,7 +107,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
foreach ($authCodePayload->scopes as $scopeId) {
|
||||
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId);
|
||||
|
||||
if (!$scope instanceof ScopeEntityInterface) {
|
||||
if ($scope instanceof ScopeEntityInterface === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw OAuthServerException::invalidScope($scopeId);
|
||||
// @codeCoverageIgnoreEnd
|
||||
@@ -108,11 +117,52 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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);
|
||||
@@ -171,7 +221,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
);
|
||||
|
||||
if ($client instanceof ClientEntityInterface === false) {
|
||||
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
|
||||
@@ -181,20 +231,22 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
is_string($client->getRedirectUri())
|
||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} elseif (
|
||||
is_array($client->getRedirectUri())
|
||||
&& in_array($redirectUri, $client->getRedirectUri()) === false
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
}
|
||||
|
||||
$scopes = $this->validateScopes(
|
||||
$this->getQueryStringParameter('scope', $request),
|
||||
$client->getRedirectUri()
|
||||
is_array($client->getRedirectUri())
|
||||
? $client->getRedirectUri()[0]
|
||||
: $client->getRedirectUri()
|
||||
);
|
||||
|
||||
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||
@@ -206,6 +258,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;
|
||||
}
|
||||
|
||||
@@ -234,25 +304,27 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
$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->setRedirectUri(
|
||||
$this->makeRedirectUri(
|
||||
$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'),
|
||||
'code_challenge' => $authorizationRequest->getCodeChallenge(),
|
||||
'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(),
|
||||
]
|
||||
)
|
||||
),
|
||||
'state' => $authorizationRequest->getState(),
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
@@ -262,7 +334,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(),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||
|
@@ -8,6 +8,7 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use League\Event\EmitterAwareInterface;
|
||||
@@ -41,11 +42,11 @@ interface GrantTypeInterface extends EmitterAwareInterface
|
||||
/**
|
||||
* Respond to an incoming request.
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
*
|
||||
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
@@ -56,7 +57,7 @@ interface GrantTypeInterface extends EmitterAwareInterface
|
||||
/**
|
||||
* The grant type should return true if it is able to response to an authorization request
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -69,7 +70,7 @@ interface GrantTypeInterface extends EmitterAwareInterface
|
||||
* If the validation is successful an AuthorizationRequest object will be returned. This object can be safely
|
||||
* serialized in a user's session, and can be used during user authentication and authorization.
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return AuthorizationRequest
|
||||
*/
|
||||
@@ -80,9 +81,9 @@ interface GrantTypeInterface extends EmitterAwareInterface
|
||||
* The AuthorizationRequest object's $userId property must be set to the authenticated user and the
|
||||
* $authorizationApproved property must reflect their desire to authorize or deny the client.
|
||||
*
|
||||
* @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authorizationRequest
|
||||
* @param AuthorizationRequest $authorizationRequest
|
||||
*
|
||||
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest);
|
||||
|
||||
@@ -91,7 +92,7 @@ interface GrantTypeInterface extends EmitterAwareInterface
|
||||
*
|
||||
* For example most grant types will check that the $_POST['grant_type'] property matches it's identifier property.
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -100,35 +101,35 @@ interface GrantTypeInterface extends EmitterAwareInterface
|
||||
/**
|
||||
* Set the client repository.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Repositories\ClientRepositoryInterface $clientRepository
|
||||
* @param ClientRepositoryInterface $clientRepository
|
||||
*/
|
||||
public function setClientRepository(ClientRepositoryInterface $clientRepository);
|
||||
|
||||
/**
|
||||
* Set the access token repository.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
*/
|
||||
public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository);
|
||||
|
||||
/**
|
||||
* Set the scope repository.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Repositories\ScopeRepositoryInterface $scopeRepository
|
||||
* @param ScopeRepositoryInterface $scopeRepository
|
||||
*/
|
||||
public function setScopeRepository(ScopeRepositoryInterface $scopeRepository);
|
||||
|
||||
/**
|
||||
* Set the path to the private key.
|
||||
*
|
||||
* @param \League\OAuth2\Server\CryptKey $privateKey
|
||||
* @param CryptKey $privateKey
|
||||
*/
|
||||
public function setPrivateKey(CryptKey $privateKey);
|
||||
|
||||
/**
|
||||
* Set the path to the public key.
|
||||
*
|
||||
* @param \League\OAuth2\Server\CryptKey $publicKey
|
||||
* @param CryptKey $publicKey
|
||||
*/
|
||||
public function setPublicKey(CryptKey $publicKey);
|
||||
}
|
||||
|
@@ -41,17 +41,17 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
*/
|
||||
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL)
|
||||
{
|
||||
throw new \LogicException('The Implicit Grant does nto return refresh tokens');
|
||||
throw new \LogicException('The Implicit Grant does not return refresh tokens');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
*
|
||||
* @throw \LogicException
|
||||
*/
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,11 +75,11 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
/**
|
||||
* Respond to an incoming request.
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseTypeInterface $responseType
|
||||
* @param \DateInterval $accessTokenTTL
|
||||
*
|
||||
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
|
||||
* @return ResponseTypeInterface
|
||||
*/
|
||||
public function respondToAccessTokenRequest(
|
||||
ServerRequestInterface $request,
|
||||
@@ -123,7 +123,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
);
|
||||
|
||||
if ($client instanceof ClientEntityInterface === false) {
|
||||
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
|
||||
@@ -133,20 +133,22 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
is_string($client->getRedirectUri())
|
||||
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
} elseif (
|
||||
is_array($client->getRedirectUri())
|
||||
&& in_array($redirectUri, $client->getRedirectUri()) === false
|
||||
) {
|
||||
$this->getEmitter()->emit(new RequestEvent('client.authentication.failed', $request));
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||
throw OAuthServerException::invalidClient();
|
||||
}
|
||||
}
|
||||
|
||||
$scopes = $this->validateScopes(
|
||||
$this->getQueryStringParameter('scope', $request),
|
||||
$client->getRedirectUri()
|
||||
is_array($client->getRedirectUri())
|
||||
? $client->getRedirectUri()[0]
|
||||
: $client->getRedirectUri()
|
||||
);
|
||||
|
||||
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||
@@ -185,15 +187,16 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
$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->setRedirectUri(
|
||||
$this->makeRedirectUri(
|
||||
$finalRedirectUri,
|
||||
$redirectPayload,
|
||||
[
|
||||
'access_token' => (string) $accessToken->convertToJWT($this->privateKey),
|
||||
'token_type' => 'bearer',
|
||||
'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(),
|
||||
'state' => $authorizationRequest->getState(),
|
||||
],
|
||||
'#'
|
||||
)
|
||||
);
|
||||
@@ -204,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(),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
@@ -25,8 +26,8 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||
class PasswordGrant extends AbstractGrant
|
||||
{
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Repositories\UserRepositoryInterface $userRepository
|
||||
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
* @param UserRepositoryInterface $userRepository
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
*/
|
||||
public function __construct(
|
||||
UserRepositoryInterface $userRepository,
|
||||
@@ -66,23 +67,23 @@ class PasswordGrant extends AbstractGrant
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ClientEntityInterface $client
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\OAuthServerException
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\UserEntityInterface
|
||||
* @return UserEntityInterface
|
||||
*/
|
||||
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
|
||||
{
|
||||
$username = $this->getRequestParameter('username', $request);
|
||||
if (is_null($username)) {
|
||||
throw OAuthServerException::invalidRequest('username', '`%s` parameter is missing');
|
||||
throw OAuthServerException::invalidRequest('username');
|
||||
}
|
||||
|
||||
$password = $this->getRequestParameter('password', $request);
|
||||
if (is_null($password)) {
|
||||
throw OAuthServerException::invalidRequest('password', '`%s` parameter is missing');
|
||||
throw OAuthServerException::invalidRequest('password');
|
||||
}
|
||||
|
||||
$user = $this->userRepository->getUserEntityByUserCredentials(
|
||||
@@ -91,8 +92,8 @@ class PasswordGrant extends AbstractGrant
|
||||
$this->getIdentifier(),
|
||||
$client
|
||||
);
|
||||
if (!$user instanceof UserEntityInterface) {
|
||||
$this->getEmitter()->emit(new RequestEvent('user.authentication.failed', $request));
|
||||
if ($user instanceof UserEntityInterface === false) {
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
|
||||
|
||||
throw OAuthServerException::invalidCredentials();
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Grant;
|
||||
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
@@ -23,7 +24,7 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||
class RefreshTokenGrant extends AbstractGrant
|
||||
{
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||
*/
|
||||
public function __construct(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
||||
{
|
||||
@@ -50,7 +51,7 @@ class RefreshTokenGrant extends AbstractGrant
|
||||
$scopes = array_map(function ($scopeId) use ($client) {
|
||||
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId);
|
||||
|
||||
if (!$scope instanceof ScopeEntityInterface) {
|
||||
if ($scope instanceof ScopeEntityInterface === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw OAuthServerException::invalidScope($scopeId);
|
||||
// @codeCoverageIgnoreEnd
|
||||
@@ -84,10 +85,10 @@ class RefreshTokenGrant extends AbstractGrant
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param string $clientId
|
||||
* @param ServerRequestInterface $request
|
||||
* @param string $clientId
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\OAuthServerException
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@@ -107,7 +108,7 @@ class RefreshTokenGrant extends AbstractGrant
|
||||
|
||||
$refreshTokenData = json_decode($refreshToken, true);
|
||||
if ($refreshTokenData['client_id'] !== $clientId) {
|
||||
$this->getEmitter()->emit(new RequestEvent('refresh_token.client.failed', $request));
|
||||
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_CLIENT_FAILED, $request));
|
||||
throw OAuthServerException::invalidRefreshToken('Token is not linked to client');
|
||||
}
|
||||
|
||||
|
@@ -17,14 +17,12 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||
class AuthorizationServerMiddleware
|
||||
{
|
||||
/**
|
||||
* @var \League\OAuth2\Server\AuthorizationServer
|
||||
* @var AuthorizationServer
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* AuthorizationServerMiddleware constructor.
|
||||
*
|
||||
* @param \League\OAuth2\Server\AuthorizationServer $server
|
||||
* @param AuthorizationServer $server
|
||||
*/
|
||||
public function __construct(AuthorizationServer $server)
|
||||
{
|
||||
@@ -32,11 +30,11 @@ class AuthorizationServerMiddleware
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param \Psr\Http\Message\ResponseInterface $response
|
||||
* @param callable $next
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param callable $next
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
|
||||
{
|
||||
@@ -46,9 +44,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
|
||||
}
|
||||
|
||||
|
@@ -17,14 +17,12 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||
class ResourceServerMiddleware
|
||||
{
|
||||
/**
|
||||
* @var \League\OAuth2\Server\ResourceServer
|
||||
* @var ResourceServer
|
||||
*/
|
||||
private $server;
|
||||
|
||||
/**
|
||||
* ResourceServerMiddleware constructor.
|
||||
*
|
||||
* @param \League\OAuth2\Server\ResourceServer $server
|
||||
* @param ResourceServer $server
|
||||
*/
|
||||
public function __construct(ResourceServer $server)
|
||||
{
|
||||
@@ -32,9 +30,9 @@ class ResourceServerMiddleware
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param \Psr\Http\Message\ResponseInterface $response
|
||||
* @param callable $next
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param callable $next
|
||||
*
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
@@ -46,9 +44,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
|
||||
}
|
||||
|
||||
|
@@ -11,6 +11,7 @@ namespace League\OAuth2\Server\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||
|
||||
/**
|
||||
* Access token interface.
|
||||
@@ -20,9 +21,9 @@ interface AccessTokenRepositoryInterface extends RepositoryInterface
|
||||
/**
|
||||
* Create a new access token
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity
|
||||
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
|
||||
* @param mixed $userIdentifier
|
||||
* @param ClientEntityInterface $clientEntity
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
* @param mixed $userIdentifier
|
||||
*
|
||||
* @return AccessTokenEntityInterface
|
||||
*/
|
||||
@@ -31,7 +32,7 @@ interface AccessTokenRepositoryInterface extends RepositoryInterface
|
||||
/**
|
||||
* Persists a new access token to permanent storage.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessTokenEntity
|
||||
* @param AccessTokenEntityInterface $accessTokenEntity
|
||||
*/
|
||||
public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity);
|
||||
|
||||
|
@@ -6,6 +6,7 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||
@@ -18,14 +19,14 @@ interface AuthCodeRepositoryInterface extends RepositoryInterface
|
||||
/**
|
||||
* Creates a new AuthCode
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\AuthCodeEntityInterface
|
||||
* @return AuthCodeEntityInterface
|
||||
*/
|
||||
public function getNewAuthCode();
|
||||
|
||||
/**
|
||||
* Persists a new auth code to permanent storage.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entities\AuthCodeEntityInterface $authCodeEntity
|
||||
* @param AuthCodeEntityInterface $authCodeEntity
|
||||
*/
|
||||
public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity);
|
||||
|
||||
|
@@ -6,8 +6,11 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
|
||||
/**
|
||||
* Client storage interface.
|
||||
*/
|
||||
@@ -19,10 +22,10 @@ interface ClientRepositoryInterface extends RepositoryInterface
|
||||
* @param string $clientIdentifier The client's identifier
|
||||
* @param string $grantType The grant type used
|
||||
* @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
|
||||
* @param bool $mustValidateSecret If true the client must attempt to validate the secret if the client
|
||||
* is confidential
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\ClientEntityInterface
|
||||
* @return ClientEntityInterface
|
||||
*/
|
||||
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true);
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||
@@ -25,7 +26,7 @@ interface RefreshTokenRepositoryInterface extends RepositoryInterface
|
||||
/**
|
||||
* Create a new refresh token_name.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshTokenEntity
|
||||
* @param RefreshTokenEntityInterface $refreshTokenEntity
|
||||
*/
|
||||
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity);
|
||||
|
||||
|
@@ -6,6 +6,7 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\Repositories;
|
||||
|
||||
/**
|
||||
|
@@ -22,7 +22,7 @@ interface ScopeRepositoryInterface extends RepositoryInterface
|
||||
*
|
||||
* @param string $identifier The scope identifier
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface
|
||||
* @return ScopeEntityInterface
|
||||
*/
|
||||
public function getScopeEntityByIdentifier($identifier);
|
||||
|
||||
@@ -30,12 +30,12 @@ interface ScopeRepositoryInterface extends RepositoryInterface
|
||||
* Given a client, grant type and optional user identifier validate the set of scopes requested are valid and optionally
|
||||
* append additional scopes or remove requested scopes.
|
||||
*
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
* @param string $grantType
|
||||
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity
|
||||
* @param null|string $userIdentifier
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
* @param string $grantType
|
||||
* @param ClientEntityInterface $clientEntity
|
||||
* @param null|string $userIdentifier
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
public function finalizeScopes(
|
||||
array $scopes,
|
||||
|
@@ -10,18 +10,19 @@
|
||||
namespace League\OAuth2\Server\Repositories;
|
||||
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||
|
||||
interface UserRepositoryInterface extends RepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Get a user entity.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param string $grantType The grant type used
|
||||
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param string $grantType The grant type used
|
||||
* @param ClientEntityInterface $clientEntity
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\UserEntityInterface
|
||||
* @return UserEntityInterface
|
||||
*/
|
||||
public function getUserEntityByUserCredentials(
|
||||
$username,
|
||||
|
@@ -6,6 +6,7 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server;
|
||||
|
||||
use League\Event\Event;
|
||||
@@ -13,16 +14,20 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class RequestEvent extends Event
|
||||
{
|
||||
const CLIENT_AUTHENTICATION_FAILED = 'client.authentication.failed';
|
||||
const USER_AUTHENTICATION_FAILED = 'user.authentication.failed';
|
||||
const REFRESH_TOKEN_CLIENT_FAILED = 'refresh_token.client.failed';
|
||||
|
||||
/**
|
||||
* @var \Psr\Http\Message\ServerRequestInterface
|
||||
* @var ServerRequestInterface
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* RequestEvent constructor.
|
||||
*
|
||||
* @param string $name
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param string $name
|
||||
* @param ServerRequestInterface $request
|
||||
*/
|
||||
public function __construct($name, ServerRequestInterface $request)
|
||||
{
|
||||
|
@@ -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
|
||||
*/
|
||||
@@ -113,7 +125,7 @@ class AuthorizationRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
public function getScopes()
|
||||
{
|
||||
@@ -121,9 +133,9 @@ class AuthorizationRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
|
||||
* @param ScopeEntityInterface[] $scopes
|
||||
*/
|
||||
public function setScopes($scopes)
|
||||
public function setScopes(array $scopes)
|
||||
{
|
||||
$this->scopes = $scopes;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -6,34 +6,38 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server;
|
||||
|
||||
use League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface;
|
||||
use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class ResourceServer
|
||||
{
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface
|
||||
* @var AccessTokenRepositoryInterface
|
||||
*/
|
||||
private $accessTokenRepository;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\CryptKey|string
|
||||
* @var CryptKey
|
||||
*/
|
||||
private $publicKey;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface|null
|
||||
* @var null|AuthorizationValidatorInterface
|
||||
*/
|
||||
private $authorizationValidator;
|
||||
|
||||
/**
|
||||
* New server instance.
|
||||
*
|
||||
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
|
||||
* @param \League\OAuth2\Server\CryptKey|string $publicKey
|
||||
* @param null|\League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface $authorizationValidator
|
||||
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||
* @param CryptKey|string $publicKey
|
||||
* @param null|AuthorizationValidatorInterface $authorizationValidator
|
||||
*/
|
||||
public function __construct(
|
||||
AccessTokenRepositoryInterface $accessTokenRepository,
|
||||
@@ -42,7 +46,7 @@ class ResourceServer
|
||||
) {
|
||||
$this->accessTokenRepository = $accessTokenRepository;
|
||||
|
||||
if (!$publicKey instanceof CryptKey) {
|
||||
if ($publicKey instanceof CryptKey === false) {
|
||||
$publicKey = new CryptKey($publicKey);
|
||||
}
|
||||
$this->publicKey = $publicKey;
|
||||
@@ -51,11 +55,11 @@ class ResourceServer
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface
|
||||
* @return AuthorizationValidatorInterface
|
||||
*/
|
||||
protected function getAuthorizationValidator()
|
||||
{
|
||||
if (!$this->authorizationValidator instanceof AuthorizationValidatorInterface) {
|
||||
if ($this->authorizationValidator instanceof AuthorizationValidatorInterface === false) {
|
||||
$this->authorizationValidator = new BearerTokenValidator($this->accessTokenRepository);
|
||||
}
|
||||
|
||||
@@ -67,11 +71,11 @@ class ResourceServer
|
||||
/**
|
||||
* Determine the access token validity.
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param ServerRequestInterface $request
|
||||
*
|
||||
* @throws \League\OAuth2\Server\Exception\OAuthServerException
|
||||
* @throws OAuthServerException
|
||||
*
|
||||
* @return \Psr\Http\Message\ServerRequestInterface
|
||||
* @return ServerRequestInterface
|
||||
*/
|
||||
public function validateAuthenticatedRequest(ServerRequestInterface $request)
|
||||
{
|
||||
|
@@ -8,6 +8,7 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\ResponseTypes;
|
||||
|
||||
use League\OAuth2\Server\CryptTrait;
|
||||
@@ -19,12 +20,12 @@ abstract class AbstractResponseType implements ResponseTypeInterface
|
||||
use CryptTrait;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Entities\AccessTokenEntityInterface
|
||||
* @var AccessTokenEntityInterface
|
||||
*/
|
||||
protected $accessToken;
|
||||
|
||||
/**
|
||||
* @var \League\OAuth2\Server\Entities\RefreshTokenEntityInterface
|
||||
* @var RefreshTokenEntityInterface
|
||||
*/
|
||||
protected $refreshToken;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Bearer Token Type.
|
||||
* OAuth 2.0 Bearer Token Response.
|
||||
*
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
@@ -8,8 +8,10 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\ResponseTypes;
|
||||
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
@@ -47,6 +49,8 @@ class BearerTokenResponse extends AbstractResponseType
|
||||
$responseParams['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
$responseParams = array_merge($this->getExtraParams($this->accessToken), $responseParams);
|
||||
|
||||
$response = $response
|
||||
->withStatus(200)
|
||||
->withHeader('pragma', 'no-cache')
|
||||
@@ -57,4 +61,17 @@ class BearerTokenResponse extends AbstractResponseType
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom fields to your Bearer Token response here, then override
|
||||
* AuthorizationServer::getResponseType() to pull in your version of
|
||||
* this class rather than the default.
|
||||
*
|
||||
* @param AccessTokenEntityInterface $accessToken
|
||||
* @return array
|
||||
*/
|
||||
protected function getExtraParams(AccessTokenEntityInterface $accessToken)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* OAuth 2.0 Redirect Response.
|
||||
*
|
||||
* @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\ResponseTypes;
|
||||
|
||||
|
@@ -8,6 +8,7 @@
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
namespace League\OAuth2\Server\ResponseTypes;
|
||||
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
@@ -17,12 +18,12 @@ use Psr\Http\Message\ResponseInterface;
|
||||
interface ResponseTypeInterface
|
||||
{
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
|
||||
* @param AccessTokenEntityInterface $accessToken
|
||||
*/
|
||||
public function setAccessToken(AccessTokenEntityInterface $accessToken);
|
||||
|
||||
/**
|
||||
* @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshToken
|
||||
* @param RefreshTokenEntityInterface $refreshToken
|
||||
*/
|
||||
public function setRefreshToken(RefreshTokenEntityInterface $refreshToken);
|
||||
|
||||
|
@@ -29,9 +29,9 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
|
||||
public function testRespondToRequestInvalidGrantType()
|
||||
{
|
||||
$server = new AuthorizationServer(
|
||||
$this->getMock(ClientRepositoryInterface::class),
|
||||
$this->getMock(AccessTokenRepositoryInterface::class),
|
||||
$this->getMock(ScopeRepositoryInterface::class),
|
||||
$this->getMockBuilder(ClientRepositoryInterface::class)->getMock(),
|
||||
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||
'file://' . __DIR__ . '/Stubs/private.key',
|
||||
'file://' . __DIR__ . '/Stubs/public.key',
|
||||
new StubResponseType()
|
||||
@@ -49,13 +49,13 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testRespondToRequest()
|
||||
{
|
||||
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
|
||||
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||
$clientRepository->method('getClientEntity')->willReturn(new ClientEntity());
|
||||
|
||||
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||
|
||||
$accessTokenRepositoryMock = $this->getMock(AccessTokenRepositoryInterface::class);
|
||||
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||
|
||||
$server = new AuthorizationServer(
|
||||
@@ -78,12 +78,12 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testGetResponseType()
|
||||
{
|
||||
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
|
||||
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$this->getMock(AccessTokenRepositoryInterface::class),
|
||||
$this->getMock(ScopeRepositoryInterface::class),
|
||||
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||
'file://' . __DIR__ . '/Stubs/private.key',
|
||||
'file://' . __DIR__ . '/Stubs/public.key'
|
||||
);
|
||||
@@ -97,12 +97,12 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testCompleteAuthorizationRequest()
|
||||
{
|
||||
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
|
||||
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$this->getMock(AccessTokenRepositoryInterface::class),
|
||||
$this->getMock(ScopeRepositoryInterface::class),
|
||||
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||
'file://' . __DIR__ . '/Stubs/private.key',
|
||||
'file://' . __DIR__ . '/Stubs/public.key'
|
||||
);
|
||||
@@ -112,7 +112,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$grant = new AuthCodeGrant(
|
||||
$authCodeRepository,
|
||||
$this->getMock(RefreshTokenRepositoryInterface::class),
|
||||
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
|
||||
new \DateInterval('PT10M')
|
||||
);
|
||||
|
||||
@@ -139,16 +139,16 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
|
||||
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||
|
||||
$grant = new AuthCodeGrant(
|
||||
$this->getMock(AuthCodeRepositoryInterface::class),
|
||||
$this->getMock(RefreshTokenRepositoryInterface::class),
|
||||
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
|
||||
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
|
||||
new \DateInterval('PT10M')
|
||||
);
|
||||
$grant->setClientRepository($clientRepositoryMock);
|
||||
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepositoryMock,
|
||||
$this->getMock(AccessTokenRepositoryInterface::class),
|
||||
$this->getMock(ScopeRepositoryInterface::class),
|
||||
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||
'file://' . __DIR__ . '/Stubs/private.key',
|
||||
'file://' . __DIR__ . '/Stubs/public.key'
|
||||
);
|
||||
@@ -178,9 +178,9 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
|
||||
public function testValidateAuthorizationRequestUnregistered()
|
||||
{
|
||||
$server = new AuthorizationServer(
|
||||
$this->getMock(ClientRepositoryInterface::class),
|
||||
$this->getMock(AccessTokenRepositoryInterface::class),
|
||||
$this->getMock(ScopeRepositoryInterface::class),
|
||||
$this->getMockBuilder(ClientRepositoryInterface::class)->getMock(),
|
||||
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||
'file://' . __DIR__ . '/Stubs/private.key',
|
||||
'file://' . __DIR__ . '/Stubs/public.key'
|
||||
);
|
||||
|
36
tests/CryptKeyTest.php
Normal file
36
tests/CryptKeyTest.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace LeagueTests\Utils;
|
||||
|
||||
use League\OAuth2\Server\CryptKey;
|
||||
|
||||
class CryptKeyTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testNoFile()
|
||||
{
|
||||
new CryptKey('undefined file');
|
||||
}
|
||||
|
||||
public function testKeyCreation()
|
||||
{
|
||||
$keyFile = __DIR__ . '/Stubs/public.key';
|
||||
$key = new CryptKey($keyFile, 'secret');
|
||||
|
||||
$this->assertEquals('file://' . $keyFile, $key->getKeyPath());
|
||||
$this->assertEquals('secret', $key->getPassPhrase());
|
||||
}
|
||||
|
||||
public function testKeyFileCreation()
|
||||
{
|
||||
$keyContent = file_get_contents(__DIR__ . '/Stubs/public.key');
|
||||
$key = new CryptKey($keyContent);
|
||||
|
||||
$this->assertEquals(
|
||||
'file://' . sys_get_temp_dir() . '/' . sha1($keyContent) . '.key',
|
||||
$key->getKeyPath()
|
||||
);
|
||||
}
|
||||
}
|
@@ -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();
|
||||
@@ -256,7 +326,7 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testIssueRefreshToken()
|
||||
{
|
||||
$refreshTokenRepoMock = $this->getMock(RefreshTokenRepositoryInterface::class);
|
||||
$refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||
$refreshTokenRepoMock
|
||||
->expects($this->once())
|
||||
->method('getNewRefreshToken')
|
||||
@@ -275,13 +345,12 @@ 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());
|
||||
}
|
||||
|
||||
public function testIssueAccessToken()
|
||||
{
|
||||
$accessTokenRepoMock = $this->getMock(AccessTokenRepositoryInterface::class);
|
||||
$accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||
$accessTokenRepoMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||
|
||||
/** @var AbstractGrant $grantMock */
|
||||
@@ -301,12 +370,11 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
|
||||
[new ScopeEntity()]
|
||||
);
|
||||
$this->assertTrue($accessToken instanceof AccessTokenEntityInterface);
|
||||
$this->assertFalse($accessToken->isExpired());
|
||||
}
|
||||
|
||||
public function testIssueAuthCode()
|
||||
{
|
||||
$authCodeRepoMock = $this->getMock(AuthCodeRepositoryInterface::class);
|
||||
$authCodeRepoMock = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
|
||||
$authCodeRepoMock->expects($this->once())->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
|
||||
|
||||
/** @var AbstractGrant $grantMock */
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -3,9 +3,12 @@
|
||||
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;
|
||||
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
||||
use LeagueTests\Stubs\AccessTokenEntity;
|
||||
@@ -295,4 +298,104 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testSetRefreshTokenTTL()
|
||||
{
|
||||
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||
$grant->setRefreshTokenTTL(new \DateInterval('PT10M'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testSetRefreshTokenRepository()
|
||||
{
|
||||
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testCompleteAuthorizationRequestNoUser()
|
||||
{
|
||||
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||
$grant->completeAuthorizationRequest(new AuthorizationRequest());
|
||||
}
|
||||
}
|
||||
|
@@ -21,8 +21,8 @@ class PasswordGrantTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testGetIdentifier()
|
||||
{
|
||||
$userRepositoryMock = $this->getMock(UserRepositoryInterface::class);
|
||||
$refreshTokenRepositoryMock = $this->getMock(RefreshTokenRepositoryInterface::class);
|
||||
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
|
||||
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||
|
||||
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
|
||||
$this->assertEquals('password', $grant->getIdentifier());
|
||||
|
@@ -32,7 +32,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testGetIdentifier()
|
||||
{
|
||||
$refreshTokenRepositoryMock = $this->getMock(RefreshTokenRepositoryInterface::class);
|
||||
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||
|
||||
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||
$this->assertEquals('refresh_token', $grant->getIdentifier());
|
||||
|
@@ -19,13 +19,13 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testValidResponse()
|
||||
{
|
||||
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
|
||||
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||
$clientRepository->method('getClientEntity')->willReturn(new ClientEntity());
|
||||
|
||||
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||
|
||||
$accessRepositoryMock = $this->getMock(AccessTokenRepositoryInterface::class);
|
||||
$accessRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||
$accessRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||
|
||||
$server = new AuthorizationServer(
|
||||
@@ -58,13 +58,13 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testOAuthErrorResponse()
|
||||
{
|
||||
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
|
||||
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||
$clientRepository->method('getClientEntity')->willReturn(null);
|
||||
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$this->getMock(AccessTokenRepositoryInterface::class),
|
||||
$this->getMock(ScopeRepositoryInterface::class),
|
||||
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||
'file://' . __DIR__ . '/../Stubs/private.key',
|
||||
'file://' . __DIR__ . '/../Stubs/public.key',
|
||||
new StubResponseType()
|
||||
|
@@ -16,7 +16,7 @@ class ResourceServerMiddlewareTest extends \PHPUnit_Framework_TestCase
|
||||
public function testValidResponse()
|
||||
{
|
||||
$server = new ResourceServer(
|
||||
$this->getMock(AccessTokenRepositoryInterface::class),
|
||||
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||
'file://' . __DIR__ . '/../Stubs/public.key'
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@ class ResourceServerMiddlewareTest extends \PHPUnit_Framework_TestCase
|
||||
public function testValidResponseExpiredToken()
|
||||
{
|
||||
$server = new ResourceServer(
|
||||
$this->getMock(AccessTokenRepositoryInterface::class),
|
||||
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||
'file://' . __DIR__ . '/../Stubs/public.key'
|
||||
);
|
||||
|
||||
@@ -86,7 +86,7 @@ class ResourceServerMiddlewareTest extends \PHPUnit_Framework_TestCase
|
||||
public function testErrorResponse()
|
||||
{
|
||||
$server = new ResourceServer(
|
||||
$this->getMock(AccessTokenRepositoryInterface::class),
|
||||
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||
'file://' . __DIR__ . '/../Stubs/public.key'
|
||||
);
|
||||
|
||||
|
@@ -13,7 +13,7 @@ class ResourceServerTest extends \PHPUnit_Framework_TestCase
|
||||
public function testValidateAuthenticatedRequest()
|
||||
{
|
||||
$server = new ResourceServer(
|
||||
$this->getMock(AccessTokenRepositoryInterface::class),
|
||||
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||
'file://' . __DIR__ . '/Stubs/public.key'
|
||||
);
|
||||
|
||||
|
@@ -61,6 +61,53 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertTrue(isset($json->refresh_token));
|
||||
}
|
||||
|
||||
public function testGenerateHttpResponseWithExtraParams()
|
||||
{
|
||||
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||
|
||||
$responseType = new BearerTokenResponseWithParams($accessTokenRepositoryMock);
|
||||
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||
$responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
|
||||
|
||||
$client = new ClientEntity();
|
||||
$client->setIdentifier('clientName');
|
||||
|
||||
$scope = new ScopeEntity();
|
||||
$scope->setIdentifier('basic');
|
||||
|
||||
$accessToken = new AccessTokenEntity();
|
||||
$accessToken->setIdentifier('abcdef');
|
||||
$accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H')));
|
||||
$accessToken->setClient($client);
|
||||
$accessToken->addScope($scope);
|
||||
|
||||
$refreshToken = new RefreshTokenEntity();
|
||||
$refreshToken->setIdentifier('abcdef');
|
||||
$refreshToken->setAccessToken($accessToken);
|
||||
$refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H')));
|
||||
|
||||
$responseType->setAccessToken($accessToken);
|
||||
$responseType->setRefreshToken($refreshToken);
|
||||
|
||||
$response = $responseType->generateHttpResponse(new Response());
|
||||
|
||||
$this->assertTrue($response instanceof ResponseInterface);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertEquals('no-cache', $response->getHeader('pragma')[0]);
|
||||
$this->assertEquals('no-store', $response->getHeader('cache-control')[0]);
|
||||
$this->assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]);
|
||||
|
||||
$response->getBody()->rewind();
|
||||
$json = json_decode($response->getBody()->getContents());
|
||||
$this->assertEquals('Bearer', $json->token_type);
|
||||
$this->assertTrue(isset($json->expires_in));
|
||||
$this->assertTrue(isset($json->access_token));
|
||||
$this->assertTrue(isset($json->refresh_token));
|
||||
|
||||
$this->assertTrue(isset($json->foo));
|
||||
$this->assertEquals('bar', $json->foo);
|
||||
}
|
||||
|
||||
public function testDetermineAccessTokenInHeaderValidToken()
|
||||
{
|
||||
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||
@@ -226,4 +273,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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
tests/ResponseTypes/BearerTokenResponseWithParams.php
Normal file
14
tests/ResponseTypes/BearerTokenResponseWithParams.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace LeagueTests\ResponseTypes;
|
||||
|
||||
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
|
||||
|
||||
class BearerTokenResponseWithParams extends BearerTokenResponse
|
||||
{
|
||||
protected function getExtraParams(AccessTokenEntityInterface $accessToken)
|
||||
{
|
||||
return ['foo' => 'bar', 'token_type' => 'Should not overwrite'];
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user