commit
425f6d1e2e
@ -0,0 +1,5 @@
|
|||||||
|
/vendor
|
||||||
|
/composer.lock
|
||||||
|
phpunit.xml
|
||||||
|
.idea
|
||||||
|
build
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (C) Alex Bilbie
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.6.0",
|
||||||
|
"league/oauth2-server":"*",
|
||||||
|
"ext-openssl": "*",
|
||||||
|
"league/event": "^2.1",
|
||||||
|
"lcobucci/jwt": "^3.1",
|
||||||
|
"paragonie/random_compat": "^2.0",
|
||||||
|
"psr/http-message": "^1.0",
|
||||||
|
"defuse/php-encryption": "^2.1",
|
||||||
|
"slim/slim": "3.0.*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^4.8 || ^5.0",
|
||||||
|
"zendframework/zend-diactoros": "^1.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"League\\OAuth2\\Server\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"LeagueTests\\": "tests/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
# Example implementations
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
0. Run `composer install` in this directory to install dependencies
|
||||||
|
0. Create a private key `openssl genrsa -out private.key 2048`
|
||||||
|
0. Create a public key `openssl rsa -in private.key -pubout > public.key`
|
||||||
|
0. `cd` into the public directory
|
||||||
|
0. Start a PHP server `php -S localhost:4444`
|
||||||
|
|
||||||
|
## Testing the client credentials grant example
|
||||||
|
|
||||||
|
Send the following cURL request:
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -X "POST" "http://localhost:4444/client_credentials.php/access_token" \
|
||||||
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||||
|
-H "Accept: 1.0" \
|
||||||
|
--data-urlencode "grant_type=client_credentials" \
|
||||||
|
--data-urlencode "client_id=myawesomeapp" \
|
||||||
|
--data-urlencode "client_secret=abc123" \
|
||||||
|
--data-urlencode "scope=basic email"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing the password grant example
|
||||||
|
|
||||||
|
Send the following cURL request:
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -X "POST" "http://localhost:4444/password.php/access_token" \
|
||||||
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||||
|
-H "Accept: 1.0" \
|
||||||
|
--data-urlencode "grant_type=password" \
|
||||||
|
--data-urlencode "client_id=myawesomeapp" \
|
||||||
|
--data-urlencode "client_secret=abc123" \
|
||||||
|
--data-urlencode "username=alex" \
|
||||||
|
--data-urlencode "password=whisky" \
|
||||||
|
--data-urlencode "scope=basic email"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing the refresh token grant example
|
||||||
|
|
||||||
|
Send the following cURL request. Replace `{{REFRESH_TOKEN}}` with a refresh token from another grant above:
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -X "POST" "http://localhost:4444/refresh_token.php/access_token" \
|
||||||
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||||
|
-H "Accept: 1.0" \
|
||||||
|
--data-urlencode "grant_type=refresh_token" \
|
||||||
|
--data-urlencode "client_id=myawesomeapp" \
|
||||||
|
--data-urlencode "client_secret=abc123" \
|
||||||
|
--data-urlencode "refresh_token={{REFRESH_TOKEN}}"
|
||||||
|
```
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEAxHSx2v6sdLIBYIGyDo68YXh2okin6R6Abt8PVMNAr/8+wCLX
|
||||||
|
JibPFR930d9wmu7NxKR2uXC7HmsFe5mgU8Y+EeziuW+1a5pNY77cB/z8j02dxZam
|
||||||
|
KW/U+mI4t/j4iGveM4fWsxx6fAYIhF60Wtj/fcNPrncCPcfKHW7fJhuth6ExwLgl
|
||||||
|
sN/LwVGHstP/KUY/F+nUX3l7Tj25FNaV1+I5bB5Rfi1Kp1udvVYSIhFIvgZdND9Y
|
||||||
|
JCT4u5HfbS7JohL4qkFVLuaILW+T7QWx68fZlJ115sDFzr0eno5OwzwRw6aqmB0C
|
||||||
|
Ukr2BmW/8EgUA5HU9MMfSPnbBBxsjCu3gWL+FwIDAQABAoIBAQCe/EVXEhUkl03P
|
||||||
|
+QXNDCxdwSxY5u2kFgAgObphgWYfSDRTL57oDC44TPAd03KAn9O8WNCQADAeTMPy
|
||||||
|
JACA64Ud4pQVN9cdDpGksDfG6zDFFC4jUmiETjGQs95s3DrdxFXdZAgXiWuCZSuN
|
||||||
|
XEse3k/dSit46GzS7JWfvjMu1uVXRWxQiRmke18tHqNJ+KA8hhF05olMGEA6oLwG
|
||||||
|
bu1pUL01iS+6UfVa1Oi7z8zGqTXpGKY6hHRjXQEZXH1oA6CaogTlUvtGUzRSYKeP
|
||||||
|
Ya1xVuF6gQDRrMKeZe9Vzyk2/uXgIP3aJS0NjPwXPnj3Ni6FtyjX+MV4Jomvb5FR
|
||||||
|
RgBOOIURAoGBAOBMFsWq4rvwDDZ8F/+qifYkzOOrXmM10Xq0XqULokEOGlZjRGhE
|
||||||
|
nHl23atGvHfXWWQayP705ZsW4MDxzLhtkCeCxgRWWsNDaEBY+Eq6QLZNPfoQ8KqU
|
||||||
|
40BZYxMmxereweQ2e5GPa6FQe0AcBnC8jOgQcMjdV5OvLhZ5g9LMb6YvAoGBAOA5
|
||||||
|
M1jN8vc6Lr2AJBtPsRxobc2Q6moB0aqc0/+a+N+ZeahO6RkoGragMsRq57U1SFUR
|
||||||
|
oUEiJoUZzpVFul21fYFEym3m395agNZNioFks0P1lwFMXc+JLpXEjRKGSCD7K4lD
|
||||||
|
dn7Mhp2eXP2PRBYWsJkfLoU8c6NMGf2871Ai4xSZAoGADQ9hJBFXMmo/y8xd+V0M
|
||||||
|
u3BZHciUrmIr2GE4QZPz1qXjkrQk/40/LzNkpNxxjOjVI6cLnQzVcbpbS+DIctSu
|
||||||
|
WB6dORNuJYB/SX/ktTUzH2pP+YeS+9u/f7e0tSDE1XAzCf7CIy+erL3q70iyd04D
|
||||||
|
Rl/usUNyHf9NR7G7o0KUHbECgYEAjjUZEMCF0TDQhhVoUP4JCUheI5s+YP9IV9nh
|
||||||
|
RuKl50Jye0GY6wCZeKw/pn1a+X2So4lr6WBcZ9xHPjscCOTbdYoIjQ15fI+P2NsS
|
||||||
|
9h/E5lwzanphoQeTSR+AjXgm9Ov337WuyJq04fCXb6VWfF/lnye8nHoLqUzRyzDx
|
||||||
|
4rRNqzkCgYARGIA5qZMfDX1ogTGQZ1ghF3vRiPpRkEdNgoAOEfV9kAC8f55a8t6i
|
||||||
|
8sKM/vUVAj8bvV1W/xUaVMKEh06wI5z4uBUj5R+8wuWNKSrCv6TG/Znq62A/AGwe
|
||||||
|
hUlY+Em75lKV7Yn2ZbW6VT05l3zV/sIQhm5Xld98JFCqxCmkx08WXw==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
Binary file not shown.
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use League\OAuth2\Server\ResourceServer;
|
||||||
|
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Slim\App;
|
||||||
|
|
||||||
|
include __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
$app = new App([
|
||||||
|
// Add the resource server to the DI container
|
||||||
|
ResourceServer::class => function () {
|
||||||
|
$server = new ResourceServer(
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// 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 `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);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$app->run();
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
<?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;
|
||||||
|
use League\OAuth2\Server\Grant\AuthCodeGrant;
|
||||||
|
use OAuth2ServerExamples\Entities\UserEntity;
|
||||||
|
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\AuthCodeRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||||
|
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,
|
||||||
|
],
|
||||||
|
AuthorizationServer::class => function () {
|
||||||
|
// Init our repositories
|
||||||
|
$clientRepository = new ClientRepository();
|
||||||
|
$scopeRepository = new ScopeRepository();
|
||||||
|
$accessTokenRepository = new AccessTokenRepository();
|
||||||
|
$authCodeRepository = new AuthCodeRepository();
|
||||||
|
$refreshTokenRepository = new RefreshTokenRepository();
|
||||||
|
|
||||||
|
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||||
|
|
||||||
|
// Setup the authorization server
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepository,
|
||||||
|
$accessTokenRepository,
|
||||||
|
$scopeRepository,
|
||||||
|
$privateKeyPath,
|
||||||
|
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enable the authentication code grant on the server with a token TTL of 1 hour
|
||||||
|
$server->enableGrantType(
|
||||||
|
new AuthCodeGrant(
|
||||||
|
$authCodeRepository,
|
||||||
|
$refreshTokenRepository,
|
||||||
|
new \DateInterval('PT10M')
|
||||||
|
),
|
||||||
|
new \DateInterval('PT1H')
|
||||||
|
);
|
||||||
|
|
||||||
|
return $server;
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||||
|
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||||
|
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Validate the HTTP request and return an AuthorizationRequest object.
|
||||||
|
// The auth request object can be serialized into a user's session
|
||||||
|
$authRequest = $server->validateAuthorizationRequest($request);
|
||||||
|
|
||||||
|
// Once the user has logged in set the user on the AuthorizationRequest
|
||||||
|
$authRequest->setUser(new UserEntity());
|
||||||
|
|
||||||
|
// Once the user has approved or denied the client update the status
|
||||||
|
// (true = approved, false = denied)
|
||||||
|
$authRequest->setAuthorizationApproved(true);
|
||||||
|
|
||||||
|
// Return the HTTP redirect response
|
||||||
|
return $server->completeAuthorizationRequest($authRequest, $response);
|
||||||
|
} catch (OAuthServerException $exception) {
|
||||||
|
return $exception->generateHttpResponse($response);
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
$body = new Stream('php://temp', 'r+');
|
||||||
|
$body->write($exception->getMessage());
|
||||||
|
|
||||||
|
return $response->withStatus(500)->withBody($body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||||
|
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||||
|
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
return $response->withStatus(500)->withBody($body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->run();
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
<?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;
|
||||||
|
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||||
|
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,
|
||||||
|
],
|
||||||
|
AuthorizationServer::class => function () {
|
||||||
|
// Init our repositories
|
||||||
|
$clientRepository = new ClientRepository(); // instance of ClientRepositoryInterface
|
||||||
|
$scopeRepository = new ScopeRepository(); // instance of ScopeRepositoryInterface
|
||||||
|
$accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface
|
||||||
|
|
||||||
|
// Path to public and private keys
|
||||||
|
$privateKey = 'file://' . __DIR__ . '/../private.key';
|
||||||
|
//$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase
|
||||||
|
|
||||||
|
// Setup the authorization server
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepository,
|
||||||
|
$accessTokenRepository,
|
||||||
|
$scopeRepository,
|
||||||
|
$privateKey,
|
||||||
|
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enable the client credentials grant on the server
|
||||||
|
$server->enableGrantType(
|
||||||
|
new \League\OAuth2\Server\Grant\ClientCredentialsGrant(),
|
||||||
|
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);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Try to respond to the request
|
||||||
|
return $server->respondToAccessTokenRequest($request, $response);
|
||||||
|
} catch (OAuthServerException $exception) {
|
||||||
|
|
||||||
|
// All instances of OAuthServerException can be formatted into a HTTP response
|
||||||
|
return $exception->generateHttpResponse($response);
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
|
||||||
|
// Unknown exception
|
||||||
|
$body = new Stream('php://temp', 'r+');
|
||||||
|
$body->write($exception->getMessage());
|
||||||
|
|
||||||
|
return $response->withStatus(500)->withBody($body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->run();
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
<?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;
|
||||||
|
use League\OAuth2\Server\Grant\ImplicitGrant;
|
||||||
|
use OAuth2ServerExamples\Entities\UserEntity;
|
||||||
|
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||||
|
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,
|
||||||
|
],
|
||||||
|
AuthorizationServer::class => function () {
|
||||||
|
// Init our repositories
|
||||||
|
$clientRepository = new ClientRepository();
|
||||||
|
$scopeRepository = new ScopeRepository();
|
||||||
|
$accessTokenRepository = new AccessTokenRepository();
|
||||||
|
|
||||||
|
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||||
|
|
||||||
|
// Setup the authorization server
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepository,
|
||||||
|
$accessTokenRepository,
|
||||||
|
$scopeRepository,
|
||||||
|
$privateKeyPath,
|
||||||
|
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||||
|
);
|
||||||
|
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
|
||||||
|
|
||||||
|
// Enable the implicit grant on the server with a token TTL of 1 hour
|
||||||
|
$server->enableGrantType(new ImplicitGrant(new \DateInterval('PT1H')));
|
||||||
|
|
||||||
|
return $server;
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||||
|
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||||
|
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Validate the HTTP request and return an AuthorizationRequest object.
|
||||||
|
// The auth request object can be serialized into a user's session
|
||||||
|
$authRequest = $server->validateAuthorizationRequest($request);
|
||||||
|
|
||||||
|
// Once the user has logged in set the user on the AuthorizationRequest
|
||||||
|
$authRequest->setUser(new UserEntity());
|
||||||
|
|
||||||
|
// Once the user has approved or denied the client update the status
|
||||||
|
// (true = approved, false = denied)
|
||||||
|
$authRequest->setAuthorizationApproved(true);
|
||||||
|
|
||||||
|
// Return the HTTP redirect response
|
||||||
|
return $server->completeAuthorizationRequest($authRequest, $response);
|
||||||
|
} catch (OAuthServerException $exception) {
|
||||||
|
return $exception->generateHttpResponse($response);
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
$body = new Stream('php://temp', 'r+');
|
||||||
|
$body->write($exception->getMessage());
|
||||||
|
|
||||||
|
return $response->withStatus(500)->withBody($body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->run();
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: Wind
|
||||||
|
* Date: 2017/10/28
|
||||||
|
* Time: 11:45
|
||||||
|
*/
|
||||||
|
echo 'index';
|
||||||
@ -0,0 +1,109 @@
|
|||||||
|
<?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\Grant\AuthCodeGrant;
|
||||||
|
use League\OAuth2\Server\Grant\RefreshTokenGrant;
|
||||||
|
use League\OAuth2\Server\Middleware\AuthorizationServerMiddleware;
|
||||||
|
use League\OAuth2\Server\Middleware\ResourceServerMiddleware;
|
||||||
|
use League\OAuth2\Server\ResourceServer;
|
||||||
|
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\AuthCodeRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||||
|
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,
|
||||||
|
],
|
||||||
|
AuthorizationServer::class => function () {
|
||||||
|
// Init our repositories
|
||||||
|
$clientRepository = new ClientRepository();
|
||||||
|
$accessTokenRepository = new AccessTokenRepository();
|
||||||
|
$scopeRepository = new ScopeRepository();
|
||||||
|
$authCodeRepository = new AuthCodeRepository();
|
||||||
|
$refreshTokenRepository = new RefreshTokenRepository();
|
||||||
|
|
||||||
|
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||||
|
|
||||||
|
// Setup the authorization server
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepository,
|
||||||
|
$accessTokenRepository,
|
||||||
|
$scopeRepository,
|
||||||
|
$privateKeyPath,
|
||||||
|
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enable the authentication code grant on the server with a token TTL of 1 hour
|
||||||
|
$server->enableGrantType(
|
||||||
|
new AuthCodeGrant(
|
||||||
|
$authCodeRepository,
|
||||||
|
$refreshTokenRepository,
|
||||||
|
new \DateInterval('PT10M')
|
||||||
|
),
|
||||||
|
new \DateInterval('PT1H')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enable the refresh token grant on the server with a token TTL of 1 month
|
||||||
|
$server->enableGrantType(
|
||||||
|
new RefreshTokenGrant($refreshTokenRepository),
|
||||||
|
new \DateInterval('P1M')
|
||||||
|
);
|
||||||
|
|
||||||
|
return $server;
|
||||||
|
},
|
||||||
|
ResourceServer::class => function () {
|
||||||
|
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
|
||||||
|
|
||||||
|
$server = new ResourceServer(
|
||||||
|
new AccessTokenRepository(),
|
||||||
|
$publicKeyPath
|
||||||
|
);
|
||||||
|
|
||||||
|
return $server;
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Access token issuer
|
||||||
|
$app->post('/access_token', function () {
|
||||||
|
})->add(new AuthorizationServerMiddleware($app->getContainer()->get(AuthorizationServer::class)));
|
||||||
|
|
||||||
|
// Secured API
|
||||||
|
$app->group('/api', function () {
|
||||||
|
$this->get('/user', function (ServerRequestInterface $request, ResponseInterface $response) {
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
if (in_array('basic', $request->getAttribute('oauth_scopes', []))) {
|
||||||
|
$params = [
|
||||||
|
'id' => 1,
|
||||||
|
'name' => 'Alex',
|
||||||
|
'city' => 'London',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array('email', $request->getAttribute('oauth_scopes', []))) {
|
||||||
|
$params['email'] = 'alex@example.com';
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = new Stream('php://temp', 'r+');
|
||||||
|
$body->write(json_encode($params));
|
||||||
|
|
||||||
|
return $response->withBody($body);
|
||||||
|
});
|
||||||
|
})->add(new ResourceServerMiddleware($app->getContainer()->get(ResourceServer::class)));
|
||||||
|
|
||||||
|
$app->run();
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use League\OAuth2\Server\AuthorizationServer;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\Grant\PasswordGrant;
|
||||||
|
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\UserRepository;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Slim\App;
|
||||||
|
|
||||||
|
include __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
$app = new App([
|
||||||
|
// Add the authorization server to the DI container
|
||||||
|
AuthorizationServer::class => function () {
|
||||||
|
|
||||||
|
// Setup the authorization server
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
new ClientRepository(), // instance of ClientRepositoryInterface
|
||||||
|
new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
|
||||||
|
new ScopeRepository(), // instance of ScopeRepositoryInterface
|
||||||
|
'file://' . __DIR__ . '/../private.key', // path to private key
|
||||||
|
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen' // encryption 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(
|
||||||
|
$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);
|
||||||
|
|
||||||
|
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();
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
<?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;
|
||||||
|
use League\OAuth2\Server\Grant\RefreshTokenGrant;
|
||||||
|
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ClientRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
|
||||||
|
use OAuth2ServerExamples\Repositories\ScopeRepository;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Slim\App;
|
||||||
|
|
||||||
|
include __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
$app = new App([
|
||||||
|
'settings' => [
|
||||||
|
'displayErrorDetails' => true,
|
||||||
|
],
|
||||||
|
AuthorizationServer::class => function () {
|
||||||
|
// Init our repositories
|
||||||
|
$clientRepository = new ClientRepository();
|
||||||
|
$accessTokenRepository = new AccessTokenRepository();
|
||||||
|
$scopeRepository = new ScopeRepository();
|
||||||
|
$refreshTokenRepository = new RefreshTokenRepository();
|
||||||
|
|
||||||
|
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||||
|
|
||||||
|
// Setup the authorization server
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepository,
|
||||||
|
$accessTokenRepository,
|
||||||
|
$scopeRepository,
|
||||||
|
$privateKeyPath,
|
||||||
|
'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enable the refresh token grant on the server
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepository);
|
||||||
|
$grant->setRefreshTokenTTL(new \DateInterval('P1M')); // The refresh token will expire in 1 month
|
||||||
|
|
||||||
|
$server->enableGrantType(
|
||||||
|
$grant,
|
||||||
|
new \DateInterval('PT1H') // The new access token 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);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $server->respondToAccessTokenRequest($request, $response);
|
||||||
|
} catch (OAuthServerException $exception) {
|
||||||
|
return $exception->generateHttpResponse($response);
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
$response->getBody()->write($exception->getMessage());
|
||||||
|
|
||||||
|
return $response->withStatus(500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$app->run();
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: Wind
|
||||||
|
* Date: 2017/10/25
|
||||||
|
* Time: 13:46
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
$test_data = [
|
||||||
|
['id' => 0, 'name' => 'A'],
|
||||||
|
['id' => 1, 'name' => 'a', 'pid' => 0],
|
||||||
|
['id' => 2, 'name' => 'b', 'pid' => 1],
|
||||||
|
['id' => 3, 'name' => 'c', 'pid' => 1],
|
||||||
|
['id' => 4, 'name' => 'd', 'pid' => 5],
|
||||||
|
['id' => 5, 'name' => 'e', 'pid' => 0],
|
||||||
|
['id' => 6, 'name' => 'e', 'pid' => 4],
|
||||||
|
];
|
||||||
|
function m_tree($arr, $pid = 0)
|
||||||
|
{
|
||||||
|
$tree = [];
|
||||||
|
foreach ($arr as $t) {
|
||||||
|
if ($t['id'] == $pid) {
|
||||||
|
$tree = $t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($arr as $child) {
|
||||||
|
if (isset($child['pid']) && $child['pid'] == $tree['id']) {
|
||||||
|
$tree['child'][] = $child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($tree as $k1 => $v1) {
|
||||||
|
if (is_array($v1)) {
|
||||||
|
foreach ($v1 as $k2 => $v2) {
|
||||||
|
$tree['child'][$k2] = m_tree($arr, $v2['id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<pre>';
|
||||||
|
print_r(m_tree($test_data));
|
||||||
|
echo '</pre>';
|
||||||
|
die;
|
||||||
|
|
||||||
|
function tree($directory)
|
||||||
|
{
|
||||||
|
$mydir = dir($directory);
|
||||||
|
echo "<ul>\n";
|
||||||
|
while ($file = $mydir->read()) {
|
||||||
|
if ($directory == '/') {
|
||||||
|
$child = $directory . $file;
|
||||||
|
} else {
|
||||||
|
$child = "$directory/$file";
|
||||||
|
}
|
||||||
|
if ((is_dir($child)) AND ($file != ".") AND ($file != "..") AND ($file != '$RECYCLE.BIN') AND ($file != 'System Volume Information')) {
|
||||||
|
var_dump("directory: " . $child);
|
||||||
|
echo "<li><font color=\"#ff00cc\"><b>$file</b></font></li>\n";
|
||||||
|
tree($child);
|
||||||
|
} else
|
||||||
|
echo "<li>$file</li>\n";
|
||||||
|
}
|
||||||
|
echo "</ul>\n";
|
||||||
|
$mydir->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChmod($filepath)
|
||||||
|
{
|
||||||
|
return substr(base_convert(@fileperms($filepath), 10, 8), -4);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dirs = tree('/');
|
||||||
|
|
||||||
|
var_dump($dirs);
|
||||||
|
|
||||||
|
var_dump(is_writable('/test'));
|
||||||
|
|
||||||
|
$perms = getChmod('/test/');
|
||||||
|
var_dump(fileperms('/test'));
|
||||||
|
|
||||||
|
var_dump(scandir('/'));
|
||||||
|
var_dump($_SERVER['DOCUMENT_ROOT']);
|
||||||
|
chdir('d:/www');
|
||||||
|
var_dump(scandir('/'));
|
||||||
|
echo getcwd();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Entities;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\AccessTokenTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
|
||||||
|
|
||||||
|
class AccessTokenEntity implements AccessTokenEntityInterface
|
||||||
|
{
|
||||||
|
use AccessTokenTrait, TokenEntityTrait, EntityTrait;
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Entities;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\AuthCodeTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
|
||||||
|
|
||||||
|
class AuthCodeEntity implements AuthCodeEntityInterface
|
||||||
|
{
|
||||||
|
use EntityTrait, TokenEntityTrait, AuthCodeTrait;
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Entities;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\ClientTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
|
||||||
|
class ClientEntity implements ClientEntityInterface
|
||||||
|
{
|
||||||
|
use EntityTrait, ClientTrait;
|
||||||
|
|
||||||
|
public function setName($name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRedirectUri($uri)
|
||||||
|
{
|
||||||
|
$this->redirectUri = $uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Entities;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\RefreshTokenTrait;
|
||||||
|
|
||||||
|
class RefreshTokenEntity implements RefreshTokenEntityInterface
|
||||||
|
{
|
||||||
|
use RefreshTokenTrait, EntityTrait;
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Entities;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
|
||||||
|
class ScopeEntity implements ScopeEntityInterface
|
||||||
|
{
|
||||||
|
use EntityTrait;
|
||||||
|
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
return $this->getIdentifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Entities;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||||
|
|
||||||
|
class UserEntity implements UserEntityInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return the user's identifier.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getIdentifier()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Repositories;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use OAuth2ServerExamples\Entities\AccessTokenEntity;
|
||||||
|
|
||||||
|
class AccessTokenRepository implements AccessTokenRepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity)
|
||||||
|
{
|
||||||
|
// Some logic here to save the access token to a database
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function revokeAccessToken($tokenId)
|
||||||
|
{
|
||||||
|
// Some logic here to revoke the access token
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isAccessTokenRevoked($tokenId)
|
||||||
|
{
|
||||||
|
return false; // Access token hasn't been revoked
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null)
|
||||||
|
{
|
||||||
|
$accessToken = new AccessTokenEntity();
|
||||||
|
$accessToken->setClient($clientEntity);
|
||||||
|
foreach ($scopes as $scope) {
|
||||||
|
$accessToken->addScope($scope);
|
||||||
|
}
|
||||||
|
$accessToken->setUserIdentifier($userIdentifier);
|
||||||
|
|
||||||
|
return $accessToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Repositories;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||||
|
use OAuth2ServerExamples\Entities\AuthCodeEntity;
|
||||||
|
|
||||||
|
class AuthCodeRepository implements AuthCodeRepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity)
|
||||||
|
{
|
||||||
|
// Some logic to persist the auth code to a database
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function revokeAuthCode($codeId)
|
||||||
|
{
|
||||||
|
// Some logic to revoke the auth code in a database
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isAuthCodeRevoked($codeId)
|
||||||
|
{
|
||||||
|
return false; // The auth code has not been revoked
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getNewAuthCode()
|
||||||
|
{
|
||||||
|
return new AuthCodeEntity();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Repositories;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||||
|
use OAuth2ServerExamples\Entities\ClientEntity;
|
||||||
|
|
||||||
|
class ClientRepository implements ClientRepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true)
|
||||||
|
{
|
||||||
|
$clients = [
|
||||||
|
'myawesomeapp' => [
|
||||||
|
'secret' => password_hash('abc123', PASSWORD_BCRYPT),
|
||||||
|
'name' => 'My Awesome App',
|
||||||
|
'redirect_uri' => 'http://foo/bar',
|
||||||
|
'is_confidential' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Check if client is registered
|
||||||
|
if (array_key_exists($clientIdentifier, $clients) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$mustValidateSecret === true
|
||||||
|
&& $clients[$clientIdentifier]['is_confidential'] === true
|
||||||
|
&& password_verify($clientSecret, $clients[$clientIdentifier]['secret']) === false
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier($clientIdentifier);
|
||||||
|
$client->setName($clients[$clientIdentifier]['name']);
|
||||||
|
$client->setRedirectUri($clients[$clientIdentifier]['redirect_uri']);
|
||||||
|
|
||||||
|
return $client;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Repositories;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||||
|
use OAuth2ServerExamples\Entities\RefreshTokenEntity;
|
||||||
|
|
||||||
|
class RefreshTokenRepository implements RefreshTokenRepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntityInterface)
|
||||||
|
{
|
||||||
|
// Some logic to persist the refresh token in a database
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function revokeRefreshToken($tokenId)
|
||||||
|
{
|
||||||
|
// Some logic to revoke the refresh token in a database
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isRefreshTokenRevoked($tokenId)
|
||||||
|
{
|
||||||
|
return false; // The refresh token has not been revoked
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getNewRefreshToken()
|
||||||
|
{
|
||||||
|
return new RefreshTokenEntity();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Repositories;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||||
|
use OAuth2ServerExamples\Entities\ScopeEntity;
|
||||||
|
|
||||||
|
class ScopeRepository implements ScopeRepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getScopeEntityByIdentifier($scopeIdentifier)
|
||||||
|
{
|
||||||
|
$scopes = [
|
||||||
|
'basic' => [
|
||||||
|
'description' => 'Basic details about you',
|
||||||
|
],
|
||||||
|
'email' => [
|
||||||
|
'description' => 'Your email address',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (array_key_exists($scopeIdentifier, $scopes) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope = new ScopeEntity();
|
||||||
|
$scope->setIdentifier($scopeIdentifier);
|
||||||
|
|
||||||
|
return $scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function finalizeScopes(
|
||||||
|
array $scopes,
|
||||||
|
$grantType,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OAuth2ServerExamples\Repositories;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
|
||||||
|
use OAuth2ServerExamples\Entities\UserEntity;
|
||||||
|
|
||||||
|
class UserRepository implements UserRepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getUserEntityByUserCredentials(
|
||||||
|
$username,
|
||||||
|
$password,
|
||||||
|
$grantType,
|
||||||
|
ClientEntityInterface $clientEntity
|
||||||
|
) {
|
||||||
|
if ($username === 'alex' && $password === 'whisky') {
|
||||||
|
return new UserEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit colors="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="true"
|
||||||
|
stopOnFailure="true" stopOnIncomplete="false" stopOnSkipped="false" bootstrap="tests/Bootstrap.php">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Tests">
|
||||||
|
<directory>./tests/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<filter>
|
||||||
|
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||||
|
<directory suffix=".php">src</directory>
|
||||||
|
<exclude>
|
||||||
|
<directory suffix=".php">src/ResponseTypes/DefaultTemplates</directory>
|
||||||
|
<directory suffix=".php">src/TemplateRenderer</directory>
|
||||||
|
</exclude>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
<logging>
|
||||||
|
<log type="coverage-text" target="php://stdout" title="thephpleague/oauth2-server" charset="UTF-8" yui="true"
|
||||||
|
highlight="true" lowUpperBound="60" highLowerBound="90"/>
|
||||||
|
<log type="coverage-html" target="build/coverage" title="thephpleague/oauth2-server" charset="UTF-8" yui="true"
|
||||||
|
highlight="true" lowUpperBound="60" highLowerBound="90"/>
|
||||||
|
</logging>
|
||||||
|
</phpunit>
|
||||||
@ -0,0 +1,207 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
use League\Event\EmitterAwareInterface;
|
||||||
|
use League\Event\EmitterAwareTrait;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\Grant\GrantTypeInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class AuthorizationServer implements EmitterAwareInterface
|
||||||
|
{
|
||||||
|
use EmitterAwareTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var GrantTypeInterface[]
|
||||||
|
*/
|
||||||
|
protected $enabledGrantTypes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateInterval[]
|
||||||
|
*/
|
||||||
|
protected $grantTypeAccessTokenTTL = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CryptKey
|
||||||
|
*/
|
||||||
|
protected $privateKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CryptKey
|
||||||
|
*/
|
||||||
|
protected $publicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|ResponseTypeInterface
|
||||||
|
*/
|
||||||
|
protected $responseType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClientRepositoryInterface
|
||||||
|
*/
|
||||||
|
private $clientRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var AccessTokenRepositoryInterface
|
||||||
|
*/
|
||||||
|
private $accessTokenRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ScopeRepositoryInterface
|
||||||
|
*/
|
||||||
|
private $scopeRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $encryptionKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New server instance.
|
||||||
|
*
|
||||||
|
* @param ClientRepositoryInterface $clientRepository
|
||||||
|
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||||
|
* @param ScopeRepositoryInterface $scopeRepository
|
||||||
|
* @param CryptKey|string $privateKey
|
||||||
|
* @param string $encryptionKey
|
||||||
|
* @param null|ResponseTypeInterface $responseType
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
ClientRepositoryInterface $clientRepository,
|
||||||
|
AccessTokenRepositoryInterface $accessTokenRepository,
|
||||||
|
ScopeRepositoryInterface $scopeRepository,
|
||||||
|
$privateKey,
|
||||||
|
$encryptionKey,
|
||||||
|
ResponseTypeInterface $responseType = null
|
||||||
|
) {
|
||||||
|
$this->clientRepository = $clientRepository;
|
||||||
|
$this->accessTokenRepository = $accessTokenRepository;
|
||||||
|
$this->scopeRepository = $scopeRepository;
|
||||||
|
|
||||||
|
if ($privateKey instanceof CryptKey === false) {
|
||||||
|
$privateKey = new CryptKey($privateKey);
|
||||||
|
}
|
||||||
|
$this->privateKey = $privateKey;
|
||||||
|
|
||||||
|
$this->encryptionKey = $encryptionKey;
|
||||||
|
$this->responseType = $responseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable a grant type on the server.
|
||||||
|
*
|
||||||
|
* @param GrantTypeInterface $grantType
|
||||||
|
* @param null|\DateInterval $accessTokenTTL
|
||||||
|
*/
|
||||||
|
public function enableGrantType(GrantTypeInterface $grantType, \DateInterval $accessTokenTTL = null)
|
||||||
|
{
|
||||||
|
if ($accessTokenTTL instanceof \DateInterval === false) {
|
||||||
|
$accessTokenTTL = new \DateInterval('PT1H');
|
||||||
|
}
|
||||||
|
|
||||||
|
$grantType->setAccessTokenRepository($this->accessTokenRepository);
|
||||||
|
$grantType->setClientRepository($this->clientRepository);
|
||||||
|
$grantType->setScopeRepository($this->scopeRepository);
|
||||||
|
$grantType->setPrivateKey($this->privateKey);
|
||||||
|
$grantType->setEmitter($this->getEmitter());
|
||||||
|
$grantType->setEncryptionKey($this->encryptionKey);
|
||||||
|
|
||||||
|
$this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType;
|
||||||
|
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate an authorization request
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
*
|
||||||
|
* @return AuthorizationRequest
|
||||||
|
*/
|
||||||
|
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
foreach ($this->enabledGrantTypes as $grantType) {
|
||||||
|
if ($grantType->canRespondToAuthorizationRequest($request)) {
|
||||||
|
return $grantType->validateAuthorizationRequest($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw OAuthServerException::unsupportedGrantType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete an authorization request
|
||||||
|
*
|
||||||
|
* @param AuthorizationRequest $authRequest
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
return $this->enabledGrantTypes[$authRequest->getGrantTypeId()]
|
||||||
|
->completeAuthorizationRequest($authRequest)
|
||||||
|
->generateHttpResponse($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an access token response.
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw OAuthServerException::unsupportedGrantType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the token type that grants will return in the HTTP response.
|
||||||
|
*
|
||||||
|
* @return ResponseTypeInterface
|
||||||
|
*/
|
||||||
|
protected function getResponseType()
|
||||||
|
{
|
||||||
|
if ($this->responseType instanceof ResponseTypeInterface === false) {
|
||||||
|
$this->responseType = new BearerTokenResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->responseType->setPrivateKey($this->privateKey);
|
||||||
|
$this->responseType->setEncryptionKey($this->encryptionKey);
|
||||||
|
|
||||||
|
return $this->responseType;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\AuthorizationValidators;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
interface AuthorizationValidatorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine the access token in the authorization header and append OAUth properties to the request
|
||||||
|
* as attributes.
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
*
|
||||||
|
* @return ServerRequestInterface
|
||||||
|
*/
|
||||||
|
public function validateAuthorization(ServerRequestInterface $request);
|
||||||
|
}
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\AuthorizationValidators;
|
||||||
|
|
||||||
|
use Lcobucci\JWT\Parser;
|
||||||
|
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||||
|
use Lcobucci\JWT\ValidationData;
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
use League\OAuth2\Server\CryptTrait;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class BearerTokenValidator implements AuthorizationValidatorInterface
|
||||||
|
{
|
||||||
|
use CryptTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var AccessTokenRepositoryInterface
|
||||||
|
*/
|
||||||
|
private $accessTokenRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \League\OAuth2\Server\CryptKey
|
||||||
|
*/
|
||||||
|
protected $publicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||||
|
*/
|
||||||
|
public function __construct(AccessTokenRepositoryInterface $accessTokenRepository)
|
||||||
|
{
|
||||||
|
$this->accessTokenRepository = $accessTokenRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the public key
|
||||||
|
*
|
||||||
|
* @param \League\OAuth2\Server\CryptKey $key
|
||||||
|
*/
|
||||||
|
public function setPublicKey(CryptKey $key)
|
||||||
|
{
|
||||||
|
$this->publicKey = $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function validateAuthorization(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
if ($request->hasHeader('authorization') === false) {
|
||||||
|
throw OAuthServerException::accessDenied('Missing "Authorization" header');
|
||||||
|
}
|
||||||
|
|
||||||
|
$header = $request->getHeader('authorization');
|
||||||
|
$jwt = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0]));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Attempt to parse and validate the JWT
|
||||||
|
$token = (new Parser())->parse($jwt);
|
||||||
|
if ($token->verify(new Sha256(), $this->publicKey->getKeyPath()) === false) {
|
||||||
|
throw OAuthServerException::accessDenied('Access token could not be verified');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure access token hasn't expired
|
||||||
|
$data = new ValidationData();
|
||||||
|
$data->setCurrentTime(time());
|
||||||
|
|
||||||
|
if ($token->validate($data) === false) {
|
||||||
|
throw OAuthServerException::accessDenied('Access token is invalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if token has been revoked
|
||||||
|
if ($this->accessTokenRepository->isAccessTokenRevoked($token->getClaim('jti'))) {
|
||||||
|
throw OAuthServerException::accessDenied('Access token has been revoked');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the request with additional attributes
|
||||||
|
return $request
|
||||||
|
->withAttribute('oauth_access_token_id', $token->getClaim('jti'))
|
||||||
|
->withAttribute('oauth_client_id', $token->getClaim('aud'))
|
||||||
|
->withAttribute('oauth_user_id', $token->getClaim('sub'))
|
||||||
|
->withAttribute('oauth_scopes', $token->getClaim('scopes'));
|
||||||
|
} 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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Cryptography key holder.
|
||||||
|
*
|
||||||
|
* @author Julián Gutiérrez <juliangut@gmail.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @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 null|string
|
||||||
|
*/
|
||||||
|
protected $passPhrase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $keyPath
|
||||||
|
* @param null|string $passPhrase
|
||||||
|
* @param bool $keyPermissionsCheck
|
||||||
|
*/
|
||||||
|
public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = true)
|
||||||
|
{
|
||||||
|
if (preg_match(self::RSA_KEY_PATTERN, $keyPath)) {
|
||||||
|
$keyPath = $this->saveKeyToFile($keyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($keyPath, 'file://') !== 0) {
|
||||||
|
$keyPath = 'file://' . $keyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists($keyPath) || !is_readable($keyPath)) {
|
||||||
|
throw new \LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($keyPermissionsCheck === true) {
|
||||||
|
// Verify the permissions of the key
|
||||||
|
$keyPathPerms = decoct(fileperms($keyPath) & 0777);
|
||||||
|
if (in_array($keyPathPerms, ['600', '660'], true) === false) {
|
||||||
|
trigger_error(sprintf(
|
||||||
|
'Key file "%s" permissions are not correct, should be 600 or 660 instead of %s',
|
||||||
|
$keyPath,
|
||||||
|
$keyPathPerms
|
||||||
|
), E_USER_NOTICE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->keyPath = $keyPath;
|
||||||
|
$this->passPhrase = $passPhrase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
*
|
||||||
|
* @throws \RuntimeException
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function saveKeyToFile($key)
|
||||||
|
{
|
||||||
|
$tmpDir = sys_get_temp_dir();
|
||||||
|
$keyPath = $tmpDir . '/' . sha1($key) . '.key';
|
||||||
|
|
||||||
|
if (!file_exists($keyPath) && !touch($keyPath)) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
throw new \RuntimeException(sprintf('"%s" key file could not be created', $keyPath));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_put_contents($keyPath, $key) === false) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
throw new \RuntimeException(sprintf('Unable to write key file to temporary directory "%s"', $tmpDir));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chmod($keyPath, 0600) === false) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
throw new \RuntimeException(sprintf('The key file "%s" file mode could not be changed with chmod to 600', $keyPath));
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'file://' . $keyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve key path.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getKeyPath()
|
||||||
|
{
|
||||||
|
return $this->keyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve key pass phrase.
|
||||||
|
*
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
public function getPassPhrase()
|
||||||
|
{
|
||||||
|
return $this->passPhrase;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Public/private key encryption.
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
use Defuse\Crypto\Crypto;
|
||||||
|
|
||||||
|
trait CryptTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $encryptionKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt data with a private key.
|
||||||
|
*
|
||||||
|
* @param string $unencryptedData
|
||||||
|
*
|
||||||
|
* @throws \LogicException
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function encrypt($unencryptedData)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new \LogicException($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt data with a public key.
|
||||||
|
*
|
||||||
|
* @param string $encryptedData
|
||||||
|
*
|
||||||
|
* @throws \LogicException
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function decrypt($encryptedData)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new \LogicException($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the encryption key
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
*/
|
||||||
|
public function setEncryptionKey($key = null)
|
||||||
|
{
|
||||||
|
$this->encryptionKey = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
|
||||||
|
interface AccessTokenEntityInterface extends TokenInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Generate a JWT from the access token
|
||||||
|
*
|
||||||
|
* @param CryptKey $privateKey
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function convertToJWT(CryptKey $privateKey);
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities;
|
||||||
|
|
||||||
|
interface AuthCodeEntityInterface extends TokenInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRedirectUri();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $uri
|
||||||
|
*/
|
||||||
|
public function setRedirectUri($uri);
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities;
|
||||||
|
|
||||||
|
interface ClientEntityInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the client's identifier.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIdentifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the client's name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the registered redirect URI (as a string).
|
||||||
|
*
|
||||||
|
* Alternatively return an indexed array of redirect URIs.
|
||||||
|
*
|
||||||
|
* @return string|string[]
|
||||||
|
*/
|
||||||
|
public function getRedirectUri();
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities;
|
||||||
|
|
||||||
|
interface RefreshTokenEntityInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the token's identifier.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIdentifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the token's identifier.
|
||||||
|
*
|
||||||
|
* @param $identifier
|
||||||
|
*/
|
||||||
|
public function setIdentifier($identifier);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the token's expiry date time.
|
||||||
|
*
|
||||||
|
* @return \DateTime
|
||||||
|
*/
|
||||||
|
public function getExpiryDateTime();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the date time when the token expires.
|
||||||
|
*
|
||||||
|
* @param \DateTime $dateTime
|
||||||
|
*/
|
||||||
|
public function setExpiryDateTime(\DateTime $dateTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the access token that the refresh token was associated with.
|
||||||
|
*
|
||||||
|
* @param AccessTokenEntityInterface $accessToken
|
||||||
|
*/
|
||||||
|
public function setAccessToken(AccessTokenEntityInterface $accessToken);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the access token that the refresh token was originally associated with.
|
||||||
|
*
|
||||||
|
* @return AccessTokenEntityInterface
|
||||||
|
*/
|
||||||
|
public function getAccessToken();
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities;
|
||||||
|
|
||||||
|
interface ScopeEntityInterface extends \JsonSerializable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the scope's identifier.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIdentifier();
|
||||||
|
}
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities;
|
||||||
|
|
||||||
|
interface TokenInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the token's identifier.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIdentifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the token's identifier.
|
||||||
|
*
|
||||||
|
* @param $identifier
|
||||||
|
*/
|
||||||
|
public function setIdentifier($identifier);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the token's expiry date time.
|
||||||
|
*
|
||||||
|
* @return \DateTime
|
||||||
|
*/
|
||||||
|
public function getExpiryDateTime();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the date time when the token expires.
|
||||||
|
*
|
||||||
|
* @param \DateTime $dateTime
|
||||||
|
*/
|
||||||
|
public function setExpiryDateTime(\DateTime $dateTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the identifier of the user associated with the token.
|
||||||
|
*
|
||||||
|
* @param string|int $identifier The identifier of the user
|
||||||
|
*/
|
||||||
|
public function setUserIdentifier($identifier);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the token user's identifier.
|
||||||
|
*
|
||||||
|
* @return string|int
|
||||||
|
*/
|
||||||
|
public function getUserIdentifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the client that the token was issued to.
|
||||||
|
*
|
||||||
|
* @return ClientEntityInterface
|
||||||
|
*/
|
||||||
|
public function getClient();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the client that the token was issued to.
|
||||||
|
*
|
||||||
|
* @param ClientEntityInterface $client
|
||||||
|
*/
|
||||||
|
public function setClient(ClientEntityInterface $client);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate a scope with the token.
|
||||||
|
*
|
||||||
|
* @param ScopeEntityInterface $scope
|
||||||
|
*/
|
||||||
|
public function addScope(ScopeEntityInterface $scope);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of scopes associated with the token.
|
||||||
|
*
|
||||||
|
* @return ScopeEntityInterface[]
|
||||||
|
*/
|
||||||
|
public function getScopes();
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities\Traits;
|
||||||
|
|
||||||
|
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 CryptKey $privateKey
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function convertToJWT(CryptKey $privateKey)
|
||||||
|
{
|
||||||
|
return (new Builder())
|
||||||
|
->setAudience($this->getClient()->getIdentifier())
|
||||||
|
->setId($this->getIdentifier(), true)
|
||||||
|
->setIssuedAt(time())
|
||||||
|
->setNotBefore(time())
|
||||||
|
->setExpiration($this->getExpiryDateTime()->getTimestamp())
|
||||||
|
->setSubject($this->getUserIdentifier())
|
||||||
|
->set('scopes', $this->getScopes())
|
||||||
|
->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();
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities\Traits;
|
||||||
|
|
||||||
|
trait AuthCodeTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var null|string
|
||||||
|
*/
|
||||||
|
protected $redirectUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRedirectUri()
|
||||||
|
{
|
||||||
|
return $this->redirectUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $uri
|
||||||
|
*/
|
||||||
|
public function setRedirectUri($uri)
|
||||||
|
{
|
||||||
|
$this->redirectUri = $uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities\Traits;
|
||||||
|
|
||||||
|
trait ClientTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|string[]
|
||||||
|
*/
|
||||||
|
protected $redirectUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the client's name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the registered redirect URI (as a string).
|
||||||
|
*
|
||||||
|
* Alternatively return an indexed array of redirect URIs.
|
||||||
|
*
|
||||||
|
* @return string|string[]
|
||||||
|
*/
|
||||||
|
public function getRedirectUri()
|
||||||
|
{
|
||||||
|
return $this->redirectUri;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities\Traits;
|
||||||
|
|
||||||
|
trait EntityTrait
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $identifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getIdentifier()
|
||||||
|
{
|
||||||
|
return $this->identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $identifier
|
||||||
|
*/
|
||||||
|
public function setIdentifier($identifier)
|
||||||
|
{
|
||||||
|
$this->identifier = $identifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities\Traits;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
|
||||||
|
trait RefreshTokenTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var AccessTokenEntityInterface
|
||||||
|
*/
|
||||||
|
protected $accessToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
*/
|
||||||
|
protected $expiryDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setAccessToken(AccessTokenEntityInterface $accessToken)
|
||||||
|
{
|
||||||
|
$this->accessToken = $accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getAccessToken()
|
||||||
|
{
|
||||||
|
return $this->accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the token's expiry date time.
|
||||||
|
*
|
||||||
|
* @return \DateTime
|
||||||
|
*/
|
||||||
|
public function getExpiryDateTime()
|
||||||
|
{
|
||||||
|
return $this->expiryDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the date time when the token expires.
|
||||||
|
*
|
||||||
|
* @param \DateTime $dateTime
|
||||||
|
*/
|
||||||
|
public function setExpiryDateTime(\DateTime $dateTime)
|
||||||
|
{
|
||||||
|
$this->expiryDateTime = $dateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities\Traits;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||||
|
|
||||||
|
trait TokenEntityTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ScopeEntityInterface[]
|
||||||
|
*/
|
||||||
|
protected $scopes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateTime
|
||||||
|
*/
|
||||||
|
protected $expiryDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|int
|
||||||
|
*/
|
||||||
|
protected $userIdentifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClientEntityInterface
|
||||||
|
*/
|
||||||
|
protected $client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate a scope with the token.
|
||||||
|
*
|
||||||
|
* @param ScopeEntityInterface $scope
|
||||||
|
*/
|
||||||
|
public function addScope(ScopeEntityInterface $scope)
|
||||||
|
{
|
||||||
|
$this->scopes[$scope->getIdentifier()] = $scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of scopes associated with the token.
|
||||||
|
*
|
||||||
|
* @return ScopeEntityInterface[]
|
||||||
|
*/
|
||||||
|
public function getScopes()
|
||||||
|
{
|
||||||
|
return array_values($this->scopes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the token's expiry date time.
|
||||||
|
*
|
||||||
|
* @return \DateTime
|
||||||
|
*/
|
||||||
|
public function getExpiryDateTime()
|
||||||
|
{
|
||||||
|
return $this->expiryDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the date time when the token expires.
|
||||||
|
*
|
||||||
|
* @param \DateTime $dateTime
|
||||||
|
*/
|
||||||
|
public function setExpiryDateTime(\DateTime $dateTime)
|
||||||
|
{
|
||||||
|
$this->expiryDateTime = $dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the identifier of the user associated with the token.
|
||||||
|
*
|
||||||
|
* @param string|int $identifier The identifier of the user
|
||||||
|
*/
|
||||||
|
public function setUserIdentifier($identifier)
|
||||||
|
{
|
||||||
|
$this->userIdentifier = $identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the token user's identifier.
|
||||||
|
*
|
||||||
|
* @return string|int
|
||||||
|
*/
|
||||||
|
public function getUserIdentifier()
|
||||||
|
{
|
||||||
|
return $this->userIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the client that the token was issued to.
|
||||||
|
*
|
||||||
|
* @return ClientEntityInterface
|
||||||
|
*/
|
||||||
|
public function getClient()
|
||||||
|
{
|
||||||
|
return $this->client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the client that the token was issued to.
|
||||||
|
*
|
||||||
|
* @param ClientEntityInterface $client
|
||||||
|
*/
|
||||||
|
public function setClient(ClientEntityInterface $client)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Entities;
|
||||||
|
|
||||||
|
interface UserEntityInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return the user's identifier.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getIdentifier();
|
||||||
|
}
|
||||||
@ -0,0 +1,296 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace League\OAuth2\Server\Exception;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
class OAuthServerException extends \Exception
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $httpStatusCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $errorType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|string
|
||||||
|
*/
|
||||||
|
private $hint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|string
|
||||||
|
*/
|
||||||
|
private $redirectUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw a new exception.
|
||||||
|
*
|
||||||
|
* @param string $message Error message
|
||||||
|
* @param int $code Error code
|
||||||
|
* @param string $errorType Error type
|
||||||
|
* @param int $httpStatusCode HTTP status code to send (default = 400)
|
||||||
|
* @param null|string $hint A helper hint
|
||||||
|
* @param null|string $redirectUri A HTTP URI to redirect the user back to
|
||||||
|
*/
|
||||||
|
public function __construct($message, $code, $errorType, $httpStatusCode = 400, $hint = null, $redirectUri = null)
|
||||||
|
{
|
||||||
|
parent::__construct($message, $code);
|
||||||
|
$this->httpStatusCode = $httpStatusCode;
|
||||||
|
$this->errorType = $errorType;
|
||||||
|
$this->hint = $hint;
|
||||||
|
$this->redirectUri = $redirectUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsupported grant type error.
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function unsupportedGrantType()
|
||||||
|
{
|
||||||
|
$errorMessage = 'The authorization grant type is not supported by the authorization server.';
|
||||||
|
$hint = 'Check the `grant_type` parameter';
|
||||||
|
|
||||||
|
return new static($errorMessage, 2, 'unsupported_grant_type', 400, $hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid request error.
|
||||||
|
*
|
||||||
|
* @param string $parameter The invalid parameter
|
||||||
|
* @param null|string $hint
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function invalidRequest($parameter, $hint = null)
|
||||||
|
{
|
||||||
|
$errorMessage = 'The request is missing a required parameter, includes an invalid parameter value, ' .
|
||||||
|
'includes a parameter more than once, or is otherwise malformed.';
|
||||||
|
$hint = ($hint === null) ? sprintf('Check the `%s` parameter', $parameter) : $hint;
|
||||||
|
|
||||||
|
return new static($errorMessage, 3, 'invalid_request', 400, $hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid client error.
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function invalidClient()
|
||||||
|
{
|
||||||
|
$errorMessage = 'Client authentication failed';
|
||||||
|
|
||||||
|
return new static($errorMessage, 4, 'invalid_client', 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid scope error.
|
||||||
|
*
|
||||||
|
* @param string $scope The bad scope
|
||||||
|
* @param null|string $redirectUri A HTTP URI to redirect the user back to
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function invalidScope($scope, $redirectUri = null)
|
||||||
|
{
|
||||||
|
$errorMessage = 'The requested scope is invalid, unknown, or malformed';
|
||||||
|
$hint = sprintf(
|
||||||
|
'Check the `%s` scope',
|
||||||
|
htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false)
|
||||||
|
);
|
||||||
|
|
||||||
|
return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid credentials error.
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function invalidCredentials()
|
||||||
|
{
|
||||||
|
return new static('The user credentials were incorrect.', 6, 'invalid_credentials', 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server error.
|
||||||
|
*
|
||||||
|
* @param $hint
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public static function serverError($hint)
|
||||||
|
{
|
||||||
|
return new static(
|
||||||
|
'The authorization server encountered an unexpected condition which prevented it from fulfilling'
|
||||||
|
. ' the request: ' . $hint,
|
||||||
|
7,
|
||||||
|
'server_error',
|
||||||
|
500
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid refresh token.
|
||||||
|
*
|
||||||
|
* @param null|string $hint
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function invalidRefreshToken($hint = null)
|
||||||
|
{
|
||||||
|
return new static('The refresh token is invalid.', 8, 'invalid_request', 401, $hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access denied.
|
||||||
|
*
|
||||||
|
* @param null|string $hint
|
||||||
|
* @param null|string $redirectUri
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function accessDenied($hint = null, $redirectUri = null)
|
||||||
|
{
|
||||||
|
return new static(
|
||||||
|
'The resource owner or authorization server denied the request.',
|
||||||
|
9,
|
||||||
|
'access_denied',
|
||||||
|
401,
|
||||||
|
$hint,
|
||||||
|
$redirectUri
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
public function getErrorType()
|
||||||
|
{
|
||||||
|
return $this->errorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a HTTP response.
|
||||||
|
*
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @param bool $useFragment True if errors should be in the URI fragment instead of query string
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function generateHttpResponse(ResponseInterface $response, $useFragment = false)
|
||||||
|
{
|
||||||
|
$headers = $this->getHttpHeaders();
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'error' => $this->getErrorType(),
|
||||||
|
'message' => $this->getMessage(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->hint !== null) {
|
||||||
|
$payload['hint'] = $this->hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->redirectUri !== null) {
|
||||||
|
if ($useFragment === true) {
|
||||||
|
$this->redirectUri .= (strstr($this->redirectUri, '#') === false) ? '#' : '&';
|
||||||
|
} else {
|
||||||
|
$this->redirectUri .= (strstr($this->redirectUri, '?') === false) ? '?' : '&';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response->withStatus(302)->withHeader('Location', $this->redirectUri . http_build_query($payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($headers as $header => $content) {
|
||||||
|
$response = $response->withHeader($header, $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->getBody()->write(json_encode($payload));
|
||||||
|
|
||||||
|
return $response->withStatus($this->getHttpStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all headers that have to be send with the error response.
|
||||||
|
*
|
||||||
|
* @return array Array with header values
|
||||||
|
*/
|
||||||
|
public function getHttpHeaders()
|
||||||
|
{
|
||||||
|
$headers = [
|
||||||
|
'Content-type' => 'application/json',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Add "WWW-Authenticate" header
|
||||||
|
//
|
||||||
|
// RFC 6749, section 5.2.:
|
||||||
|
// "If the client attempted to authenticate via the 'Authorization'
|
||||||
|
// request header field, the authorization server MUST
|
||||||
|
// respond with an HTTP 401 (Unauthorized) status code and
|
||||||
|
// include the "WWW-Authenticate" response header field
|
||||||
|
// matching the authentication scheme used by the client.
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
if ($this->errorType === 'invalid_client') {
|
||||||
|
$authScheme = 'Basic';
|
||||||
|
if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER) !== false
|
||||||
|
&& strpos($_SERVER['HTTP_AUTHORIZATION'], 'Bearer') === 0
|
||||||
|
) {
|
||||||
|
$authScheme = 'Bearer';
|
||||||
|
}
|
||||||
|
$headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"';
|
||||||
|
}
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
return $headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the HTTP status code to send when the exceptions is output.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getHttpStatusCode()
|
||||||
|
{
|
||||||
|
return $this->httpStatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
public function getHint()
|
||||||
|
{
|
||||||
|
return $this->hint;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Abstract authorization grant.
|
||||||
|
*
|
||||||
|
* @author Julián Gutiérrez <juliangut@gmail.com>
|
||||||
|
* @copyright Copyright (c) Alex Bilbie
|
||||||
|
* @license http://mit-license.org/
|
||||||
|
*
|
||||||
|
* @link https://github.com/thephpleague/oauth2-server
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace League\OAuth2\Server\Grant;
|
||||||
|
|
||||||
|
abstract class AbstractAuthorizeGrant extends AbstractGrant
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $uri
|
||||||
|
* @param array $params
|
||||||
|
* @param string $queryDelimiter
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function makeRedirectUri($uri, $params = [], $queryDelimiter = '?')
|
||||||
|
{
|
||||||
|
$uri .= (strstr($uri, $queryDelimiter) === false) ? $queryDelimiter : '&';
|
||||||
|
|
||||||
|
return $uri . http_build_query($params);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,512 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* OAuth 2.0 Abstract grant.
|
||||||
|
*
|
||||||
|
* @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\Grant;
|
||||||
|
|
||||||
|
use League\Event\EmitterAwareTrait;
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
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;
|
||||||
|
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\RequestEvent;
|
||||||
|
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract grant class.
|
||||||
|
*/
|
||||||
|
abstract class AbstractGrant implements GrantTypeInterface
|
||||||
|
{
|
||||||
|
use EmitterAwareTrait, CryptTrait;
|
||||||
|
|
||||||
|
const SCOPE_DELIMITER_STRING = ' ';
|
||||||
|
|
||||||
|
const MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClientRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $clientRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var AccessTokenRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $accessTokenRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ScopeRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $scopeRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var AuthCodeRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $authCodeRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RefreshTokenRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $refreshTokenRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var UserRepositoryInterface
|
||||||
|
*/
|
||||||
|
protected $userRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \DateInterval
|
||||||
|
*/
|
||||||
|
protected $refreshTokenTTL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \League\OAuth2\Server\CryptKey
|
||||||
|
*/
|
||||||
|
protected $privateKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientRepositoryInterface $clientRepository
|
||||||
|
*/
|
||||||
|
public function setClientRepository(ClientRepositoryInterface $clientRepository)
|
||||||
|
{
|
||||||
|
$this->clientRepository = $clientRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||||
|
*/
|
||||||
|
public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository)
|
||||||
|
{
|
||||||
|
$this->accessTokenRepository = $accessTokenRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ScopeRepositoryInterface $scopeRepository
|
||||||
|
*/
|
||||||
|
public function setScopeRepository(ScopeRepositoryInterface $scopeRepository)
|
||||||
|
{
|
||||||
|
$this->scopeRepository = $scopeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||||
|
*/
|
||||||
|
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
||||||
|
{
|
||||||
|
$this->refreshTokenRepository = $refreshTokenRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AuthCodeRepositoryInterface $authCodeRepository
|
||||||
|
*/
|
||||||
|
public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository)
|
||||||
|
{
|
||||||
|
$this->authCodeRepository = $authCodeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UserRepositoryInterface $userRepository
|
||||||
|
*/
|
||||||
|
public function setUserRepository(UserRepositoryInterface $userRepository)
|
||||||
|
{
|
||||||
|
$this->userRepository = $userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL)
|
||||||
|
{
|
||||||
|
$this->refreshTokenTTL = $refreshTokenTTL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the private key
|
||||||
|
*
|
||||||
|
* @param \League\OAuth2\Server\CryptKey $key
|
||||||
|
*/
|
||||||
|
public function setPrivateKey(CryptKey $key)
|
||||||
|
{
|
||||||
|
$this->privateKey = $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the client.
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
*
|
||||||
|
* @return ClientEntityInterface
|
||||||
|
*/
|
||||||
|
protected function validateClient(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request);
|
||||||
|
|
||||||
|
$clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
|
||||||
|
if (is_null($clientId)) {
|
||||||
|
throw OAuthServerException::invalidRequest('client_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the client is confidential require the client secret
|
||||||
|
$clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);
|
||||||
|
|
||||||
|
$client = $this->clientRepository->getClientEntity(
|
||||||
|
$clientId,
|
||||||
|
$this->getIdentifier(),
|
||||||
|
$clientSecret,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($client instanceof ClientEntityInterface === false) {
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
|
throw OAuthServerException::invalidClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a redirect URI is provided ensure it matches what is pre-registered
|
||||||
|
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
|
||||||
|
if ($redirectUri !== null) {
|
||||||
|
if (
|
||||||
|
is_string($client->getRedirectUri())
|
||||||
|
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||||
|
) {
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
|
throw OAuthServerException::invalidClient();
|
||||||
|
} elseif (
|
||||||
|
is_array($client->getRedirectUri())
|
||||||
|
&& in_array($redirectUri, $client->getRedirectUri()) === false
|
||||||
|
) {
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
|
throw OAuthServerException::invalidClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate scopes in the request.
|
||||||
|
*
|
||||||
|
* @param string $scopes
|
||||||
|
* @param string $redirectUri
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
*
|
||||||
|
* @return ScopeEntityInterface[]
|
||||||
|
*/
|
||||||
|
public function validateScopes(
|
||||||
|
$scopes,
|
||||||
|
$redirectUri = null
|
||||||
|
) {
|
||||||
|
$scopesList = array_filter(
|
||||||
|
explode(self::SCOPE_DELIMITER_STRING, trim($scopes)),
|
||||||
|
function ($scope) {
|
||||||
|
return !empty($scope);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$scopes = [];
|
||||||
|
foreach ($scopesList as $scopeItem) {
|
||||||
|
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
|
||||||
|
|
||||||
|
if ($scope instanceof ScopeEntityInterface === false) {
|
||||||
|
throw OAuthServerException::invalidScope($scopeItem, $redirectUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
$scopes[] = $scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve request parameter.
|
||||||
|
*
|
||||||
|
* @param string $parameter
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param mixed $default
|
||||||
|
*
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
protected function getRequestParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||||
|
{
|
||||||
|
$requestParameters = (array) $request->getParsedBody();
|
||||||
|
|
||||||
|
return 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 ServerRequestInterface $request
|
||||||
|
* @param mixed $default
|
||||||
|
*
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
protected function getQueryStringParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||||
|
{
|
||||||
|
return isset($request->getQueryParams()[$parameter]) ? $request->getQueryParams()[$parameter] : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve cookie parameter.
|
||||||
|
*
|
||||||
|
* @param string $parameter
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param mixed $default
|
||||||
|
*
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
protected function getCookieParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||||
|
{
|
||||||
|
return isset($request->getCookieParams()[$parameter]) ? $request->getCookieParams()[$parameter] : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve server parameter.
|
||||||
|
*
|
||||||
|
* @param string $parameter
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param mixed $default
|
||||||
|
*
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
protected function getServerParameter($parameter, ServerRequestInterface $request, $default = null)
|
||||||
|
{
|
||||||
|
return isset($request->getServerParams()[$parameter]) ? $request->getServerParams()[$parameter] : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issue an access token.
|
||||||
|
*
|
||||||
|
* @param \DateInterval $accessTokenTTL
|
||||||
|
* @param ClientEntityInterface $client
|
||||||
|
* @param string $userIdentifier
|
||||||
|
* @param ScopeEntityInterface[] $scopes
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||||
|
*
|
||||||
|
* @return AccessTokenEntityInterface
|
||||||
|
*/
|
||||||
|
protected function issueAccessToken(
|
||||||
|
\DateInterval $accessTokenTTL,
|
||||||
|
ClientEntityInterface $client,
|
||||||
|
$userIdentifier,
|
||||||
|
array $scopes = []
|
||||||
|
) {
|
||||||
|
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||||
|
|
||||||
|
$accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier);
|
||||||
|
$accessToken->setClient($client);
|
||||||
|
$accessToken->setUserIdentifier($userIdentifier);
|
||||||
|
$accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL));
|
||||||
|
|
||||||
|
foreach ($scopes as $scope) {
|
||||||
|
$accessToken->addScope($scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ClientEntityInterface $client
|
||||||
|
* @param string $userIdentifier
|
||||||
|
* @param string $redirectUri
|
||||||
|
* @param ScopeEntityInterface[] $scopes
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||||
|
*
|
||||||
|
* @return AuthCodeEntityInterface
|
||||||
|
*/
|
||||||
|
protected function issueAuthCode(
|
||||||
|
\DateInterval $authCodeTTL,
|
||||||
|
ClientEntityInterface $client,
|
||||||
|
$userIdentifier,
|
||||||
|
$redirectUri,
|
||||||
|
array $scopes = []
|
||||||
|
) {
|
||||||
|
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||||
|
|
||||||
|
$authCode = $this->authCodeRepository->getNewAuthCode();
|
||||||
|
$authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL));
|
||||||
|
$authCode->setClient($client);
|
||||||
|
$authCode->setUserIdentifier($userIdentifier);
|
||||||
|
$authCode->setRedirectUri($redirectUri);
|
||||||
|
|
||||||
|
foreach ($scopes as $scope) {
|
||||||
|
$authCode->addScope($scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($maxGenerationAttempts-- > 0) {
|
||||||
|
$authCode->setIdentifier($this->generateUniqueIdentifier());
|
||||||
|
try {
|
||||||
|
$this->authCodeRepository->persistNewAuthCode($authCode);
|
||||||
|
|
||||||
|
return $authCode;
|
||||||
|
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
|
||||||
|
if ($maxGenerationAttempts === 0) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AccessTokenEntityInterface $accessToken
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||||
|
*
|
||||||
|
* @return RefreshTokenEntityInterface
|
||||||
|
*/
|
||||||
|
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
|
||||||
|
{
|
||||||
|
$maxGenerationAttempts = self::MAX_RANDOM_TOKEN_GENERATION_ATTEMPTS;
|
||||||
|
|
||||||
|
$refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
|
||||||
|
$refreshToken->setExpiryDateTime((new \DateTime())->add($this->refreshTokenTTL));
|
||||||
|
$refreshToken->setAccessToken($accessToken);
|
||||||
|
|
||||||
|
while ($maxGenerationAttempts-- > 0) {
|
||||||
|
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
|
||||||
|
try {
|
||||||
|
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
|
||||||
|
|
||||||
|
return $refreshToken;
|
||||||
|
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
|
||||||
|
if ($maxGenerationAttempts === 0) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new unique identifier.
|
||||||
|
*
|
||||||
|
* @param int $length
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function generateUniqueIdentifier($length = 40)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return bin2hex(random_bytes($length));
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
} catch (\TypeError $e) {
|
||||||
|
throw OAuthServerException::serverError('An unexpected error has occurred');
|
||||||
|
} catch (\Error $e) {
|
||||||
|
throw OAuthServerException::serverError('An unexpected error has occurred');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// If you get this message, the CSPRNG failed hard.
|
||||||
|
throw OAuthServerException::serverError('Could not generate a random string');
|
||||||
|
}
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
$requestParameters = (array) $request->getParsedBody();
|
||||||
|
|
||||||
|
return (
|
||||||
|
array_key_exists('grant_type', $requestParameters)
|
||||||
|
&& $requestParameters['grant_type'] === $this->getIdentifier()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
throw new \LogicException('This grant cannot validate an authorization request');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||||
|
{
|
||||||
|
throw new \LogicException('This grant cannot complete an authorization request');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,354 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Grant;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\RequestEvent;
|
||||||
|
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \DateInterval
|
||||||
|
*/
|
||||||
|
private $authCodeTTL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $enableCodeExchangeProof = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AuthCodeRepositoryInterface $authCodeRepository
|
||||||
|
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||||
|
* @param \DateInterval $authCodeTTL
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
AuthCodeRepositoryInterface $authCodeRepository,
|
||||||
|
RefreshTokenRepositoryInterface $refreshTokenRepository,
|
||||||
|
\DateInterval $authCodeTTL
|
||||||
|
) {
|
||||||
|
$this->setAuthCodeRepository($authCodeRepository);
|
||||||
|
$this->setRefreshTokenRepository($refreshTokenRepository);
|
||||||
|
$this->authCodeTTL = $authCodeTTL;
|
||||||
|
$this->refreshTokenTTL = new \DateInterval('P1M');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enableCodeExchangeProof()
|
||||||
|
{
|
||||||
|
$this->enableCodeExchangeProof = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond to an access token request.
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param ResponseTypeInterface $responseType
|
||||||
|
* @param \DateInterval $accessTokenTTL
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
*
|
||||||
|
* @return ResponseTypeInterface
|
||||||
|
*/
|
||||||
|
public function respondToAccessTokenRequest(
|
||||||
|
ServerRequestInterface $request,
|
||||||
|
ResponseTypeInterface $responseType,
|
||||||
|
\DateInterval $accessTokenTTL
|
||||||
|
) {
|
||||||
|
// Validate request
|
||||||
|
$client = $this->validateClient($request);
|
||||||
|
$encryptedAuthCode = $this->getRequestParameter('code', $request, null);
|
||||||
|
|
||||||
|
if ($encryptedAuthCode === null) {
|
||||||
|
throw OAuthServerException::invalidRequest('code');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the authorization code
|
||||||
|
try {
|
||||||
|
$authCodePayload = json_decode($this->decrypt($encryptedAuthCode));
|
||||||
|
if (time() > $authCodePayload->expire_time) {
|
||||||
|
throw OAuthServerException::invalidRequest('code', 'Authorization code has expired');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->authCodeRepository->isAuthCodeRevoked($authCodePayload->auth_code_id) === true) {
|
||||||
|
throw OAuthServerException::invalidRequest('code', 'Authorization code has been revoked');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($authCodePayload->client_id !== $client->getIdentifier()) {
|
||||||
|
throw OAuthServerException::invalidRequest('code', 'Authorization code was not issued to this client');
|
||||||
|
}
|
||||||
|
|
||||||
|
// The redirect URI is required in this request
|
||||||
|
$redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
|
||||||
|
if (empty($authCodePayload->redirect_uri) === false && $redirectUri === null) {
|
||||||
|
throw OAuthServerException::invalidRequest('redirect_uri');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($authCodePayload->redirect_uri !== $redirectUri) {
|
||||||
|
throw OAuthServerException::invalidRequest('redirect_uri', 'Invalid redirect URI');
|
||||||
|
}
|
||||||
|
|
||||||
|
$scopes = [];
|
||||||
|
foreach ($authCodePayload->scopes as $scopeId) {
|
||||||
|
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId);
|
||||||
|
|
||||||
|
if ($scope instanceof ScopeEntityInterface === false) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
throw OAuthServerException::invalidScope($scopeId);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
$scopes[] = $scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalize the requested scopes
|
||||||
|
$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);
|
||||||
|
|
||||||
|
// Inject tokens into response type
|
||||||
|
$responseType->setAccessToken($accessToken);
|
||||||
|
$responseType->setRefreshToken($refreshToken);
|
||||||
|
|
||||||
|
// Revoke used auth code
|
||||||
|
$this->authCodeRepository->revokeAuthCode($authCodePayload->auth_code_id);
|
||||||
|
|
||||||
|
return $responseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the grant identifier that can be used in matching up requests.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIdentifier()
|
||||||
|
{
|
||||||
|
return 'authorization_code';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
array_key_exists('response_type', $request->getQueryParams())
|
||||||
|
&& $request->getQueryParams()['response_type'] === 'code'
|
||||||
|
&& isset($request->getQueryParams()['client_id'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
$clientId = $this->getQueryStringParameter(
|
||||||
|
'client_id',
|
||||||
|
$request,
|
||||||
|
$this->getServerParameter('PHP_AUTH_USER', $request)
|
||||||
|
);
|
||||||
|
if (is_null($clientId)) {
|
||||||
|
throw OAuthServerException::invalidRequest('client_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = $this->clientRepository->getClientEntity(
|
||||||
|
$clientId,
|
||||||
|
$this->getIdentifier(),
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($client instanceof ClientEntityInterface === false) {
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
|
throw OAuthServerException::invalidClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
|
||||||
|
if ($redirectUri !== null) {
|
||||||
|
if (
|
||||||
|
is_string($client->getRedirectUri())
|
||||||
|
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||||
|
) {
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
|
throw OAuthServerException::invalidClient();
|
||||||
|
} elseif (
|
||||||
|
is_array($client->getRedirectUri())
|
||||||
|
&& in_array($redirectUri, $client->getRedirectUri()) === false
|
||||||
|
) {
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
|
throw OAuthServerException::invalidClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scopes = $this->validateScopes(
|
||||||
|
$this->getQueryStringParameter('scope', $request),
|
||||||
|
is_array($client->getRedirectUri())
|
||||||
|
? $client->getRedirectUri()[0]
|
||||||
|
: $client->getRedirectUri()
|
||||||
|
);
|
||||||
|
|
||||||
|
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||||
|
|
||||||
|
$authorizationRequest = new AuthorizationRequest();
|
||||||
|
$authorizationRequest->setGrantTypeId($this->getIdentifier());
|
||||||
|
$authorizationRequest->setClient($client);
|
||||||
|
$authorizationRequest->setRedirectUri($redirectUri);
|
||||||
|
$authorizationRequest->setState($stateParameter);
|
||||||
|
$authorizationRequest->setScopes($scopes);
|
||||||
|
|
||||||
|
if ($this->enableCodeExchangeProof === true) {
|
||||||
|
$codeChallenge = $this->getQueryStringParameter('code_challenge', $request);
|
||||||
|
if ($codeChallenge === null) {
|
||||||
|
throw OAuthServerException::invalidRequest('code_challenge');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) {
|
||||||
|
throw OAuthServerException::invalidRequest(
|
||||||
|
'code_challenge',
|
||||||
|
'The code_challenge must be between 43 and 128 characters'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||||
|
{
|
||||||
|
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
|
||||||
|
throw new \LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
|
||||||
|
}
|
||||||
|
|
||||||
|
$finalRedirectUri = ($authorizationRequest->getRedirectUri() === null)
|
||||||
|
? is_array($authorizationRequest->getClient()->getRedirectUri())
|
||||||
|
? $authorizationRequest->getClient()->getRedirectUri()[0]
|
||||||
|
: $authorizationRequest->getClient()->getRedirectUri()
|
||||||
|
: $authorizationRequest->getRedirectUri();
|
||||||
|
|
||||||
|
// The user approved the client, redirect them back with an auth code
|
||||||
|
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
||||||
|
$authCode = $this->issueAuthCode(
|
||||||
|
$this->authCodeTTL,
|
||||||
|
$authorizationRequest->getClient(),
|
||||||
|
$authorizationRequest->getUser()->getIdentifier(),
|
||||||
|
$authorizationRequest->getRedirectUri(),
|
||||||
|
$authorizationRequest->getScopes()
|
||||||
|
);
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'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(),
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = new RedirectResponse();
|
||||||
|
$response->setRedirectUri(
|
||||||
|
$this->makeRedirectUri(
|
||||||
|
$finalRedirectUri,
|
||||||
|
[
|
||||||
|
'code' => $this->encrypt(
|
||||||
|
json_encode(
|
||||||
|
$payload
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'state' => $authorizationRequest->getState(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The user denied the client, redirect them back with an error
|
||||||
|
throw OAuthServerException::accessDenied(
|
||||||
|
'The user denied the request',
|
||||||
|
$this->makeRedirectUri(
|
||||||
|
$finalRedirectUri,
|
||||||
|
[
|
||||||
|
'state' => $authorizationRequest->getState(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* OAuth 2.0 Client credentials grant.
|
||||||
|
*
|
||||||
|
* @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\Grant;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client credentials grant class.
|
||||||
|
*/
|
||||||
|
class ClientCredentialsGrant extends AbstractGrant
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function respondToAccessTokenRequest(
|
||||||
|
ServerRequestInterface $request,
|
||||||
|
ResponseTypeInterface $responseType,
|
||||||
|
\DateInterval $accessTokenTTL
|
||||||
|
) {
|
||||||
|
// Validate request
|
||||||
|
$client = $this->validateClient($request);
|
||||||
|
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request));
|
||||||
|
|
||||||
|
// Finalize the requested scopes
|
||||||
|
$scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client);
|
||||||
|
|
||||||
|
// Issue and persist access token
|
||||||
|
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $scopes);
|
||||||
|
|
||||||
|
// Inject access token into response type
|
||||||
|
$responseType->setAccessToken($accessToken);
|
||||||
|
|
||||||
|
return $responseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getIdentifier()
|
||||||
|
{
|
||||||
|
return 'client_credentials';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* OAuth 2.0 Grant type interface.
|
||||||
|
*
|
||||||
|
* @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\Grant;
|
||||||
|
|
||||||
|
use League\Event\EmitterAwareInterface;
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grant type interface.
|
||||||
|
*/
|
||||||
|
interface GrantTypeInterface extends EmitterAwareInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set refresh token TTL.
|
||||||
|
*
|
||||||
|
* @param \DateInterval $refreshTokenTTL
|
||||||
|
*/
|
||||||
|
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the grant identifier that can be used in matching up requests.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIdentifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond to an incoming request.
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param ResponseTypeInterface $responseType
|
||||||
|
* @param \DateInterval $accessTokenTTL
|
||||||
|
*
|
||||||
|
* @return ResponseTypeInterface
|
||||||
|
*/
|
||||||
|
public function respondToAccessTokenRequest(
|
||||||
|
ServerRequestInterface $request,
|
||||||
|
ResponseTypeInterface $responseType,
|
||||||
|
\DateInterval $accessTokenTTL
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The grant type should return true if it is able to response to an authorization request
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function canRespondToAuthorizationRequest(ServerRequestInterface $request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the grant can respond to an authorization request this method should be called to validate the parameters of
|
||||||
|
* the request.
|
||||||
|
*
|
||||||
|
* 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 ServerRequestInterface $request
|
||||||
|
*
|
||||||
|
* @return AuthorizationRequest
|
||||||
|
*/
|
||||||
|
public function validateAuthorizationRequest(ServerRequestInterface $request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once a user has authenticated and authorized the client the grant can complete the authorization request.
|
||||||
|
* 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 AuthorizationRequest $authorizationRequest
|
||||||
|
*
|
||||||
|
* @return ResponseTypeInterface
|
||||||
|
*/
|
||||||
|
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The grant type should return true if it is able to respond to this request.
|
||||||
|
*
|
||||||
|
* For example most grant types will check that the $_POST['grant_type'] property matches it's identifier property.
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function canRespondToAccessTokenRequest(ServerRequestInterface $request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the client repository.
|
||||||
|
*
|
||||||
|
* @param ClientRepositoryInterface $clientRepository
|
||||||
|
*/
|
||||||
|
public function setClientRepository(ClientRepositoryInterface $clientRepository);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the access token repository.
|
||||||
|
*
|
||||||
|
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||||
|
*/
|
||||||
|
public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the scope repository.
|
||||||
|
*
|
||||||
|
* @param ScopeRepositoryInterface $scopeRepository
|
||||||
|
*/
|
||||||
|
public function setScopeRepository(ScopeRepositoryInterface $scopeRepository);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the path to the private key.
|
||||||
|
*
|
||||||
|
* @param CryptKey $privateKey
|
||||||
|
*/
|
||||||
|
public function setPrivateKey(CryptKey $privateKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the encryption key
|
||||||
|
*
|
||||||
|
* @param string|null $key
|
||||||
|
*/
|
||||||
|
public function setEncryptionKey($key = null);
|
||||||
|
}
|
||||||
@ -0,0 +1,225 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Grant;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\RequestEvent;
|
||||||
|
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class ImplicitGrant extends AbstractAuthorizeGrant
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \DateInterval
|
||||||
|
*/
|
||||||
|
private $accessTokenTTL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DateInterval $accessTokenTTL
|
||||||
|
*/
|
||||||
|
public function __construct(\DateInterval $accessTokenTTL)
|
||||||
|
{
|
||||||
|
$this->accessTokenTTL = $accessTokenTTL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DateInterval $refreshTokenTTL
|
||||||
|
*
|
||||||
|
* @throw \LogicException
|
||||||
|
*/
|
||||||
|
public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL)
|
||||||
|
{
|
||||||
|
throw new \LogicException('The Implicit Grant does not return refresh tokens');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||||
|
*
|
||||||
|
* @throw \LogicException
|
||||||
|
*/
|
||||||
|
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
||||||
|
{
|
||||||
|
throw new \LogicException('The Implicit Grant does not return refresh tokens');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the grant identifier that can be used in matching up requests.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getIdentifier()
|
||||||
|
{
|
||||||
|
return 'implicit';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond to an incoming request.
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param ResponseTypeInterface $responseType
|
||||||
|
* @param \DateInterval $accessTokenTTL
|
||||||
|
*
|
||||||
|
* @return ResponseTypeInterface
|
||||||
|
*/
|
||||||
|
public function respondToAccessTokenRequest(
|
||||||
|
ServerRequestInterface $request,
|
||||||
|
ResponseTypeInterface $responseType,
|
||||||
|
\DateInterval $accessTokenTTL
|
||||||
|
) {
|
||||||
|
throw new \LogicException('This grant does not used this method');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
isset($request->getQueryParams()['response_type'])
|
||||||
|
&& $request->getQueryParams()['response_type'] === 'token'
|
||||||
|
&& isset($request->getQueryParams()['client_id'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function validateAuthorizationRequest(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
$clientId = $this->getQueryStringParameter(
|
||||||
|
'client_id',
|
||||||
|
$request,
|
||||||
|
$this->getServerParameter('PHP_AUTH_USER', $request)
|
||||||
|
);
|
||||||
|
if (is_null($clientId)) {
|
||||||
|
throw OAuthServerException::invalidRequest('client_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = $this->clientRepository->getClientEntity(
|
||||||
|
$clientId,
|
||||||
|
$this->getIdentifier(),
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($client instanceof ClientEntityInterface === false) {
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
|
throw OAuthServerException::invalidClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
$redirectUri = $this->getQueryStringParameter('redirect_uri', $request);
|
||||||
|
if ($redirectUri !== null) {
|
||||||
|
if (
|
||||||
|
is_string($client->getRedirectUri())
|
||||||
|
&& (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
|
||||||
|
) {
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
|
throw OAuthServerException::invalidClient();
|
||||||
|
} elseif (
|
||||||
|
is_array($client->getRedirectUri())
|
||||||
|
&& in_array($redirectUri, $client->getRedirectUri()) === false
|
||||||
|
) {
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
|
||||||
|
throw OAuthServerException::invalidClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scopes = $this->validateScopes(
|
||||||
|
$this->getQueryStringParameter('scope', $request),
|
||||||
|
is_array($client->getRedirectUri())
|
||||||
|
? $client->getRedirectUri()[0]
|
||||||
|
: $client->getRedirectUri()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Finalize the requested scopes
|
||||||
|
$scopes = $this->scopeRepository->finalizeScopes(
|
||||||
|
$scopes,
|
||||||
|
$this->getIdentifier(),
|
||||||
|
$client
|
||||||
|
);
|
||||||
|
|
||||||
|
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||||
|
|
||||||
|
$authorizationRequest = new AuthorizationRequest();
|
||||||
|
$authorizationRequest->setGrantTypeId($this->getIdentifier());
|
||||||
|
$authorizationRequest->setClient($client);
|
||||||
|
$authorizationRequest->setRedirectUri($redirectUri);
|
||||||
|
$authorizationRequest->setState($stateParameter);
|
||||||
|
$authorizationRequest->setScopes($scopes);
|
||||||
|
|
||||||
|
return $authorizationRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
|
||||||
|
{
|
||||||
|
if ($authorizationRequest->getUser() instanceof UserEntityInterface === false) {
|
||||||
|
throw new \LogicException('An instance of UserEntityInterface should be set on the AuthorizationRequest');
|
||||||
|
}
|
||||||
|
|
||||||
|
$finalRedirectUri = ($authorizationRequest->getRedirectUri() === null)
|
||||||
|
? is_array($authorizationRequest->getClient()->getRedirectUri())
|
||||||
|
? $authorizationRequest->getClient()->getRedirectUri()[0]
|
||||||
|
: $authorizationRequest->getClient()->getRedirectUri()
|
||||||
|
: $authorizationRequest->getRedirectUri();
|
||||||
|
|
||||||
|
// The user approved the client, redirect them back with an access token
|
||||||
|
if ($authorizationRequest->isAuthorizationApproved() === true) {
|
||||||
|
$accessToken = $this->issueAccessToken(
|
||||||
|
$this->accessTokenTTL,
|
||||||
|
$authorizationRequest->getClient(),
|
||||||
|
$authorizationRequest->getUser()->getIdentifier(),
|
||||||
|
$authorizationRequest->getScopes()
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = new RedirectResponse();
|
||||||
|
$response->setRedirectUri(
|
||||||
|
$this->makeRedirectUri(
|
||||||
|
$finalRedirectUri,
|
||||||
|
[
|
||||||
|
'access_token' => (string) $accessToken->convertToJWT($this->privateKey),
|
||||||
|
'token_type' => 'Bearer',
|
||||||
|
'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(),
|
||||||
|
'state' => $authorizationRequest->getState(),
|
||||||
|
],
|
||||||
|
'#'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The user denied the client, redirect them back with an error
|
||||||
|
throw OAuthServerException::accessDenied(
|
||||||
|
'The user denied the request',
|
||||||
|
$this->makeRedirectUri(
|
||||||
|
$finalRedirectUri,
|
||||||
|
[
|
||||||
|
'state' => $authorizationRequest->getState(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* OAuth 2.0 Password grant.
|
||||||
|
*
|
||||||
|
* @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\Grant;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\RequestEvent;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password grant class.
|
||||||
|
*/
|
||||||
|
class PasswordGrant extends AbstractGrant
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param UserRepositoryInterface $userRepository
|
||||||
|
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
UserRepositoryInterface $userRepository,
|
||||||
|
RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||||
|
) {
|
||||||
|
$this->setUserRepository($userRepository);
|
||||||
|
$this->setRefreshTokenRepository($refreshTokenRepository);
|
||||||
|
|
||||||
|
$this->refreshTokenTTL = new \DateInterval('P1M');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function respondToAccessTokenRequest(
|
||||||
|
ServerRequestInterface $request,
|
||||||
|
ResponseTypeInterface $responseType,
|
||||||
|
\DateInterval $accessTokenTTL
|
||||||
|
) {
|
||||||
|
// Validate request
|
||||||
|
$client = $this->validateClient($request);
|
||||||
|
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request));
|
||||||
|
$user = $this->validateUser($request, $client);
|
||||||
|
|
||||||
|
// Finalize the requested scopes
|
||||||
|
$scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $user->getIdentifier());
|
||||||
|
|
||||||
|
// Issue and persist new tokens
|
||||||
|
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $user->getIdentifier(), $scopes);
|
||||||
|
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||||
|
|
||||||
|
// Inject tokens into response
|
||||||
|
$responseType->setAccessToken($accessToken);
|
||||||
|
$responseType->setRefreshToken($refreshToken);
|
||||||
|
|
||||||
|
return $responseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param ClientEntityInterface $client
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
*
|
||||||
|
* @return UserEntityInterface
|
||||||
|
*/
|
||||||
|
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
|
||||||
|
{
|
||||||
|
$username = $this->getRequestParameter('username', $request);
|
||||||
|
if (is_null($username)) {
|
||||||
|
throw OAuthServerException::invalidRequest('username');
|
||||||
|
}
|
||||||
|
|
||||||
|
$password = $this->getRequestParameter('password', $request);
|
||||||
|
if (is_null($password)) {
|
||||||
|
throw OAuthServerException::invalidRequest('password');
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $this->userRepository->getUserEntityByUserCredentials(
|
||||||
|
$username,
|
||||||
|
$password,
|
||||||
|
$this->getIdentifier(),
|
||||||
|
$client
|
||||||
|
);
|
||||||
|
if ($user instanceof UserEntityInterface === false) {
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
|
||||||
|
|
||||||
|
throw OAuthServerException::invalidCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getIdentifier()
|
||||||
|
{
|
||||||
|
return 'password';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,133 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* OAuth 2.0 Refresh token grant.
|
||||||
|
*
|
||||||
|
* @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\Grant;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\RequestEvent;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh token grant.
|
||||||
|
*/
|
||||||
|
class RefreshTokenGrant extends AbstractGrant
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
|
||||||
|
*/
|
||||||
|
public function __construct(RefreshTokenRepositoryInterface $refreshTokenRepository)
|
||||||
|
{
|
||||||
|
$this->setRefreshTokenRepository($refreshTokenRepository);
|
||||||
|
|
||||||
|
$this->refreshTokenTTL = new \DateInterval('P1M');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function respondToAccessTokenRequest(
|
||||||
|
ServerRequestInterface $request,
|
||||||
|
ResponseTypeInterface $responseType,
|
||||||
|
\DateInterval $accessTokenTTL
|
||||||
|
) {
|
||||||
|
// Validate request
|
||||||
|
$client = $this->validateClient($request);
|
||||||
|
$oldRefreshToken = $this->validateOldRefreshToken($request, $client->getIdentifier());
|
||||||
|
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request));
|
||||||
|
|
||||||
|
// If no new scopes are requested then give the access token the original session scopes
|
||||||
|
if (count($scopes) === 0) {
|
||||||
|
$scopes = array_map(function ($scopeId) use ($client) {
|
||||||
|
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId);
|
||||||
|
|
||||||
|
if ($scope instanceof ScopeEntityInterface === false) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
throw OAuthServerException::invalidScope($scopeId);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
return $scope;
|
||||||
|
}, $oldRefreshToken['scopes']);
|
||||||
|
} else {
|
||||||
|
// The OAuth spec says that a refreshed access token can have the original scopes or fewer so ensure
|
||||||
|
// the request doesn't include any new scopes
|
||||||
|
foreach ($scopes as $scope) {
|
||||||
|
if (in_array($scope->getIdentifier(), $oldRefreshToken['scopes']) === false) {
|
||||||
|
throw OAuthServerException::invalidScope($scope->getIdentifier());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expire old tokens
|
||||||
|
$this->accessTokenRepository->revokeAccessToken($oldRefreshToken['access_token_id']);
|
||||||
|
$this->refreshTokenRepository->revokeRefreshToken($oldRefreshToken['refresh_token_id']);
|
||||||
|
|
||||||
|
// Issue and persist new tokens
|
||||||
|
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $oldRefreshToken['user_id'], $scopes);
|
||||||
|
$refreshToken = $this->issueRefreshToken($accessToken);
|
||||||
|
|
||||||
|
// Inject tokens into response
|
||||||
|
$responseType->setAccessToken($accessToken);
|
||||||
|
$responseType->setRefreshToken($refreshToken);
|
||||||
|
|
||||||
|
return $responseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param string $clientId
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function validateOldRefreshToken(ServerRequestInterface $request, $clientId)
|
||||||
|
{
|
||||||
|
$encryptedRefreshToken = $this->getRequestParameter('refresh_token', $request);
|
||||||
|
if (is_null($encryptedRefreshToken)) {
|
||||||
|
throw OAuthServerException::invalidRequest('refresh_token');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate refresh token
|
||||||
|
try {
|
||||||
|
$refreshToken = $this->decrypt($encryptedRefreshToken);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token');
|
||||||
|
}
|
||||||
|
|
||||||
|
$refreshTokenData = json_decode($refreshToken, true);
|
||||||
|
if ($refreshTokenData['client_id'] !== $clientId) {
|
||||||
|
$this->getEmitter()->emit(new RequestEvent(RequestEvent::REFRESH_TOKEN_CLIENT_FAILED, $request));
|
||||||
|
throw OAuthServerException::invalidRefreshToken('Token is not linked to client');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($refreshTokenData['expire_time'] < time()) {
|
||||||
|
throw OAuthServerException::invalidRefreshToken('Token has expired');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->refreshTokenRepository->isRefreshTokenRevoked($refreshTokenData['refresh_token_id']) === true) {
|
||||||
|
throw OAuthServerException::invalidRefreshToken('Token has been revoked');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $refreshTokenData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getIdentifier()
|
||||||
|
{
|
||||||
|
return 'refresh_token';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Middleware;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\AuthorizationServer;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class AuthorizationServerMiddleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var AuthorizationServer
|
||||||
|
*/
|
||||||
|
private $server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AuthorizationServer $server
|
||||||
|
*/
|
||||||
|
public function __construct(AuthorizationServer $server)
|
||||||
|
{
|
||||||
|
$this->server = $server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @param callable $next
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$response = $this->server->respondToAccessTokenRequest($request, $response);
|
||||||
|
} catch (OAuthServerException $exception) {
|
||||||
|
return $exception->generateHttpResponse($response);
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
|
||||||
|
->generateHttpResponse($response);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass the request and response on to the next responder in the chain
|
||||||
|
return $next($request, $response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Middleware;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\ResourceServer;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
|
||||||
|
class ResourceServerMiddleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ResourceServer
|
||||||
|
*/
|
||||||
|
private $server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ResourceServer $server
|
||||||
|
*/
|
||||||
|
public function __construct(ResourceServer $server)
|
||||||
|
{
|
||||||
|
$this->server = $server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
* @param callable $next
|
||||||
|
*
|
||||||
|
* @return \Psr\Http\Message\ResponseInterface
|
||||||
|
*/
|
||||||
|
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$request = $this->server->validateAuthenticatedRequest($request);
|
||||||
|
} catch (OAuthServerException $exception) {
|
||||||
|
return $exception->generateHttpResponse($response);
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
|
||||||
|
->generateHttpResponse($response);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass the request and response on to the next responder in the chain
|
||||||
|
return $next($request, $response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Repositories;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||||
|
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access token interface.
|
||||||
|
*/
|
||||||
|
interface AccessTokenRepositoryInterface extends RepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new access token
|
||||||
|
*
|
||||||
|
* @param ClientEntityInterface $clientEntity
|
||||||
|
* @param ScopeEntityInterface[] $scopes
|
||||||
|
* @param mixed $userIdentifier
|
||||||
|
*
|
||||||
|
* @return AccessTokenEntityInterface
|
||||||
|
*/
|
||||||
|
public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persists a new access token to permanent storage.
|
||||||
|
*
|
||||||
|
* @param AccessTokenEntityInterface $accessTokenEntity
|
||||||
|
*
|
||||||
|
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||||
|
*/
|
||||||
|
public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoke an access token.
|
||||||
|
*
|
||||||
|
* @param string $tokenId
|
||||||
|
*/
|
||||||
|
public function revokeAccessToken($tokenId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the access token has been revoked.
|
||||||
|
*
|
||||||
|
* @param string $tokenId
|
||||||
|
*
|
||||||
|
* @return bool Return true if this token has been revoked
|
||||||
|
*/
|
||||||
|
public function isAccessTokenRevoked($tokenId);
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Repositories;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||||
|
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auth code storage interface.
|
||||||
|
*/
|
||||||
|
interface AuthCodeRepositoryInterface extends RepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a new AuthCode
|
||||||
|
*
|
||||||
|
* @return AuthCodeEntityInterface
|
||||||
|
*/
|
||||||
|
public function getNewAuthCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persists a new auth code to permanent storage.
|
||||||
|
*
|
||||||
|
* @param AuthCodeEntityInterface $authCodeEntity
|
||||||
|
*
|
||||||
|
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||||
|
*/
|
||||||
|
public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoke an auth code.
|
||||||
|
*
|
||||||
|
* @param string $codeId
|
||||||
|
*/
|
||||||
|
public function revokeAuthCode($codeId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the auth code has been revoked.
|
||||||
|
*
|
||||||
|
* @param string $codeId
|
||||||
|
*
|
||||||
|
* @return bool Return true if this code has been revoked
|
||||||
|
*/
|
||||||
|
public function isAuthCodeRevoked($codeId);
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Repositories;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client storage interface.
|
||||||
|
*/
|
||||||
|
interface ClientRepositoryInterface extends RepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get a client.
|
||||||
|
*
|
||||||
|
* @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 if the client
|
||||||
|
* is confidential
|
||||||
|
*
|
||||||
|
* @return ClientEntityInterface
|
||||||
|
*/
|
||||||
|
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true);
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Repositories;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh token interface.
|
||||||
|
*/
|
||||||
|
interface RefreshTokenRepositoryInterface extends RepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a new refresh token
|
||||||
|
*
|
||||||
|
* @return RefreshTokenEntityInterface
|
||||||
|
*/
|
||||||
|
public function getNewRefreshToken();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new refresh token_name.
|
||||||
|
*
|
||||||
|
* @param RefreshTokenEntityInterface $refreshTokenEntity
|
||||||
|
*
|
||||||
|
* @throws UniqueTokenIdentifierConstraintViolationException
|
||||||
|
*/
|
||||||
|
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoke the refresh token.
|
||||||
|
*
|
||||||
|
* @param string $tokenId
|
||||||
|
*/
|
||||||
|
public function revokeRefreshToken($tokenId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the refresh token has been revoked.
|
||||||
|
*
|
||||||
|
* @param string $tokenId
|
||||||
|
*
|
||||||
|
* @return bool Return true if this token has been revoked
|
||||||
|
*/
|
||||||
|
public function isRefreshTokenRevoked($tokenId);
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Repositories;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository interface.
|
||||||
|
*/
|
||||||
|
interface RepositoryInterface
|
||||||
|
{
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\Repositories;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope interface.
|
||||||
|
*/
|
||||||
|
interface ScopeRepositoryInterface extends RepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return information about a scope.
|
||||||
|
*
|
||||||
|
* @param string $identifier The scope identifier
|
||||||
|
*
|
||||||
|
* @return ScopeEntityInterface
|
||||||
|
*/
|
||||||
|
public function getScopeEntityByIdentifier($identifier);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 ClientEntityInterface $clientEntity
|
||||||
|
* @param null|string $userIdentifier
|
||||||
|
*
|
||||||
|
* @return ScopeEntityInterface[]
|
||||||
|
*/
|
||||||
|
public function finalizeScopes(
|
||||||
|
array $scopes,
|
||||||
|
$grantType,
|
||||||
|
ClientEntityInterface $clientEntity,
|
||||||
|
$userIdentifier = null
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\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 ClientEntityInterface $clientEntity
|
||||||
|
*
|
||||||
|
* @return UserEntityInterface
|
||||||
|
*/
|
||||||
|
public function getUserEntityByUserCredentials(
|
||||||
|
$username,
|
||||||
|
$password,
|
||||||
|
$grantType,
|
||||||
|
ClientEntityInterface $clientEntity
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
use League\Event\Event;
|
||||||
|
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 ServerRequestInterface
|
||||||
|
*/
|
||||||
|
private $request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RequestEvent constructor.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
*/
|
||||||
|
public function __construct($name, ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
parent::__construct($name);
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ServerRequestInterface
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function getRequest()
|
||||||
|
{
|
||||||
|
return $this->request;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,224 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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\RequestTypes;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||||
|
|
||||||
|
class AuthorizationRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The grant type identifier
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $grantTypeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The client identifier
|
||||||
|
*
|
||||||
|
* @var ClientEntityInterface
|
||||||
|
*/
|
||||||
|
protected $client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user identifier
|
||||||
|
*
|
||||||
|
* @var UserEntityInterface
|
||||||
|
*/
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of scope identifiers
|
||||||
|
*
|
||||||
|
* @var ScopeEntityInterface[]
|
||||||
|
*/
|
||||||
|
protected $scopes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has the user authorized the authorization request
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $authorizationApproved = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The redirect URI used in the request
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $redirectUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state parameter on the authorization request
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The code challenge (if provided)
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $codeChallenge;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The code challenge method (if provided)
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $codeChallengeMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getGrantTypeId()
|
||||||
|
{
|
||||||
|
return $this->grantTypeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $grantTypeId
|
||||||
|
*/
|
||||||
|
public function setGrantTypeId($grantTypeId)
|
||||||
|
{
|
||||||
|
$this->grantTypeId = $grantTypeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ClientEntityInterface
|
||||||
|
*/
|
||||||
|
public function getClient()
|
||||||
|
{
|
||||||
|
return $this->client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ClientEntityInterface $client
|
||||||
|
*/
|
||||||
|
public function setClient(ClientEntityInterface $client)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return UserEntityInterface
|
||||||
|
*/
|
||||||
|
public function getUser()
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UserEntityInterface $user
|
||||||
|
*/
|
||||||
|
public function setUser(UserEntityInterface $user)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ScopeEntityInterface[]
|
||||||
|
*/
|
||||||
|
public function getScopes()
|
||||||
|
{
|
||||||
|
return $this->scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ScopeEntityInterface[] $scopes
|
||||||
|
*/
|
||||||
|
public function setScopes(array $scopes)
|
||||||
|
{
|
||||||
|
$this->scopes = $scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAuthorizationApproved()
|
||||||
|
{
|
||||||
|
return $this->authorizationApproved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $authorizationApproved
|
||||||
|
*/
|
||||||
|
public function setAuthorizationApproved($authorizationApproved)
|
||||||
|
{
|
||||||
|
$this->authorizationApproved = $authorizationApproved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRedirectUri()
|
||||||
|
{
|
||||||
|
return $this->redirectUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $redirectUri
|
||||||
|
*/
|
||||||
|
public function setRedirectUri($redirectUri)
|
||||||
|
{
|
||||||
|
$this->redirectUri = $redirectUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getState()
|
||||||
|
{
|
||||||
|
return $this->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $state
|
||||||
|
*/
|
||||||
|
public function setState($state)
|
||||||
|
{
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
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 AccessTokenRepositoryInterface
|
||||||
|
*/
|
||||||
|
private $accessTokenRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CryptKey
|
||||||
|
*/
|
||||||
|
private $publicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|AuthorizationValidatorInterface
|
||||||
|
*/
|
||||||
|
private $authorizationValidator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New server instance.
|
||||||
|
*
|
||||||
|
* @param AccessTokenRepositoryInterface $accessTokenRepository
|
||||||
|
* @param CryptKey|string $publicKey
|
||||||
|
* @param null|AuthorizationValidatorInterface $authorizationValidator
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
AccessTokenRepositoryInterface $accessTokenRepository,
|
||||||
|
$publicKey,
|
||||||
|
AuthorizationValidatorInterface $authorizationValidator = null
|
||||||
|
) {
|
||||||
|
$this->accessTokenRepository = $accessTokenRepository;
|
||||||
|
|
||||||
|
if ($publicKey instanceof CryptKey === false) {
|
||||||
|
$publicKey = new CryptKey($publicKey);
|
||||||
|
}
|
||||||
|
$this->publicKey = $publicKey;
|
||||||
|
|
||||||
|
$this->authorizationValidator = $authorizationValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return AuthorizationValidatorInterface
|
||||||
|
*/
|
||||||
|
protected function getAuthorizationValidator()
|
||||||
|
{
|
||||||
|
if ($this->authorizationValidator instanceof AuthorizationValidatorInterface === false) {
|
||||||
|
$this->authorizationValidator = new BearerTokenValidator($this->accessTokenRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->authorizationValidator->setPublicKey($this->publicKey);
|
||||||
|
|
||||||
|
return $this->authorizationValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the access token validity.
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
*
|
||||||
|
* @throws OAuthServerException
|
||||||
|
*
|
||||||
|
* @return ServerRequestInterface
|
||||||
|
*/
|
||||||
|
public function validateAuthenticatedRequest(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
return $this->getAuthorizationValidator()->validateAuthorization($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* OAuth 2.0 Abstract Response Type.
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
use League\OAuth2\Server\CryptTrait;
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||||
|
|
||||||
|
abstract class AbstractResponseType implements ResponseTypeInterface
|
||||||
|
{
|
||||||
|
use CryptTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var AccessTokenEntityInterface
|
||||||
|
*/
|
||||||
|
protected $accessToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RefreshTokenEntityInterface
|
||||||
|
*/
|
||||||
|
protected $refreshToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CryptKey
|
||||||
|
*/
|
||||||
|
protected $privateKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setAccessToken(AccessTokenEntityInterface $accessToken)
|
||||||
|
{
|
||||||
|
$this->accessToken = $accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setRefreshToken(RefreshTokenEntityInterface $refreshToken)
|
||||||
|
{
|
||||||
|
$this->refreshToken = $refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the private key
|
||||||
|
*
|
||||||
|
* @param \League\OAuth2\Server\CryptKey $key
|
||||||
|
*/
|
||||||
|
public function setPrivateKey(CryptKey $key)
|
||||||
|
{
|
||||||
|
$this->privateKey = $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* OAuth 2.0 Bearer Token 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;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
class BearerTokenResponse extends AbstractResponseType
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function generateHttpResponse(ResponseInterface $response)
|
||||||
|
{
|
||||||
|
$expireDateTime = $this->accessToken->getExpiryDateTime()->getTimestamp();
|
||||||
|
|
||||||
|
$jwtAccessToken = $this->accessToken->convertToJWT($this->privateKey);
|
||||||
|
|
||||||
|
$responseParams = [
|
||||||
|
'token_type' => 'Bearer',
|
||||||
|
'expires_in' => $expireDateTime - (new \DateTime())->getTimestamp(),
|
||||||
|
'access_token' => (string) $jwtAccessToken,
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->refreshToken instanceof RefreshTokenEntityInterface) {
|
||||||
|
$refreshToken = $this->encrypt(
|
||||||
|
json_encode(
|
||||||
|
[
|
||||||
|
'client_id' => $this->accessToken->getClient()->getIdentifier(),
|
||||||
|
'refresh_token_id' => $this->refreshToken->getIdentifier(),
|
||||||
|
'access_token_id' => $this->accessToken->getIdentifier(),
|
||||||
|
'scopes' => $this->accessToken->getScopes(),
|
||||||
|
'user_id' => $this->accessToken->getUserIdentifier(),
|
||||||
|
'expire_time' => $this->refreshToken->getExpiryDateTime()->getTimestamp(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseParams['refresh_token'] = $refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseParams = array_merge($this->getExtraParams($this->accessToken), $responseParams);
|
||||||
|
|
||||||
|
$response = $response
|
||||||
|
->withStatus(200)
|
||||||
|
->withHeader('pragma', 'no-cache')
|
||||||
|
->withHeader('cache-control', 'no-store')
|
||||||
|
->withHeader('content-type', 'application/json; charset=UTF-8');
|
||||||
|
|
||||||
|
$response->getBody()->write(json_encode($responseParams));
|
||||||
|
|
||||||
|
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 [];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
class RedirectResponse extends AbstractResponseType
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $redirectUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $redirectUri
|
||||||
|
*/
|
||||||
|
public function setRedirectUri($redirectUri)
|
||||||
|
{
|
||||||
|
$this->redirectUri = $redirectUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function generateHttpResponse(ResponseInterface $response)
|
||||||
|
{
|
||||||
|
return $response->withStatus(302)->withHeader('Location', $this->redirectUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* OAuth 2.0 Response Type Interface.
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
|
interface ResponseTypeInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param AccessTokenEntityInterface $accessToken
|
||||||
|
*/
|
||||||
|
public function setAccessToken(AccessTokenEntityInterface $accessToken);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RefreshTokenEntityInterface $refreshToken
|
||||||
|
*/
|
||||||
|
public function setRefreshToken(RefreshTokenEntityInterface $refreshToken);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function generateHttpResponse(ResponseInterface $response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the encryption key
|
||||||
|
*
|
||||||
|
* @param string|null $key
|
||||||
|
*/
|
||||||
|
public function setEncryptionKey($key = null);
|
||||||
|
}
|
||||||
@ -0,0 +1,208 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\AuthorizationServer;
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\Grant\AuthCodeGrant;
|
||||||
|
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
|
||||||
|
use LeagueTests\Stubs\AccessTokenEntity;
|
||||||
|
use LeagueTests\Stubs\AuthCodeEntity;
|
||||||
|
use LeagueTests\Stubs\ClientEntity;
|
||||||
|
use LeagueTests\Stubs\StubResponseType;
|
||||||
|
use LeagueTests\Stubs\UserEntity;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Zend\Diactoros\Response;
|
||||||
|
use Zend\Diactoros\ServerRequest;
|
||||||
|
use Zend\Diactoros\ServerRequestFactory;
|
||||||
|
|
||||||
|
class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
// Make sure the keys have the correct permissions.
|
||||||
|
chmod(__DIR__ . '/Stubs/private.key', 0600);
|
||||||
|
chmod(__DIR__ . '/Stubs/public.key', 0600);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRespondToRequestInvalidGrantType()
|
||||||
|
{
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$this->getMockBuilder(ClientRepositoryInterface::class)->getMock(),
|
||||||
|
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||||
|
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||||
|
'file://' . __DIR__ . '/Stubs/private.key',
|
||||||
|
base64_encode(random_bytes(36)),
|
||||||
|
new StubResponseType()
|
||||||
|
);
|
||||||
|
|
||||||
|
$server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response);
|
||||||
|
} catch (OAuthServerException $e) {
|
||||||
|
$this->assertEquals('unsupported_grant_type', $e->getErrorType());
|
||||||
|
$this->assertEquals(400, $e->getHttpStatusCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRespondToRequest()
|
||||||
|
{
|
||||||
|
$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->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepository,
|
||||||
|
$accessTokenRepositoryMock,
|
||||||
|
$scopeRepositoryMock,
|
||||||
|
'file://' . __DIR__ . '/Stubs/private.key',
|
||||||
|
base64_encode(random_bytes(36)),
|
||||||
|
new StubResponseType()
|
||||||
|
);
|
||||||
|
|
||||||
|
$server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M'));
|
||||||
|
|
||||||
|
$_POST['grant_type'] = 'client_credentials';
|
||||||
|
$_POST['client_id'] = 'foo';
|
||||||
|
$_POST['client_secret'] = 'bar';
|
||||||
|
$response = $server->respondToAccessTokenRequest(ServerRequestFactory::fromGlobals(), new Response);
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetResponseType()
|
||||||
|
{
|
||||||
|
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepository,
|
||||||
|
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||||
|
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||||
|
'file://' . __DIR__ . '/Stubs/private.key',
|
||||||
|
'file://' . __DIR__ . '/Stubs/public.key'
|
||||||
|
);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($server);
|
||||||
|
$method = $abstractGrantReflection->getMethod('getResponseType');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$this->assertTrue($method->invoke($server) instanceof BearerTokenResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCompleteAuthorizationRequest()
|
||||||
|
{
|
||||||
|
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepository,
|
||||||
|
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||||
|
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||||
|
'file://' . __DIR__ . '/Stubs/private.key',
|
||||||
|
'file://' . __DIR__ . '/Stubs/public.key'
|
||||||
|
);
|
||||||
|
|
||||||
|
$authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
|
||||||
|
$authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
|
||||||
|
|
||||||
|
$grant = new AuthCodeGrant(
|
||||||
|
$authCodeRepository,
|
||||||
|
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
|
||||||
|
new \DateInterval('PT10M')
|
||||||
|
);
|
||||||
|
|
||||||
|
$server->enableGrantType($grant);
|
||||||
|
|
||||||
|
$authRequest = new AuthorizationRequest();
|
||||||
|
$authRequest->setAuthorizationApproved(true);
|
||||||
|
$authRequest->setClient(new ClientEntity());
|
||||||
|
$authRequest->setGrantTypeId('authorization_code');
|
||||||
|
$authRequest->setUser(new UserEntity());
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
$server->completeAuthorizationRequest($authRequest, new Response) instanceof ResponseInterface
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateAuthorizationRequest()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$grant = new AuthCodeGrant(
|
||||||
|
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
|
||||||
|
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
|
||||||
|
new \DateInterval('PT10M')
|
||||||
|
);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepositoryMock,
|
||||||
|
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||||
|
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||||
|
'file://' . __DIR__ . '/Stubs/private.key',
|
||||||
|
'file://' . __DIR__ . '/Stubs/public.key'
|
||||||
|
);
|
||||||
|
$server->enableGrantType($grant);
|
||||||
|
|
||||||
|
$request = new ServerRequest(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'php://input',
|
||||||
|
$headers = [],
|
||||||
|
$cookies = [],
|
||||||
|
$queryParams = [
|
||||||
|
'response_type' => 'code',
|
||||||
|
'client_id' => 'foo',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertTrue($server->validateAuthorizationRequest($request) instanceof AuthorizationRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 2
|
||||||
|
*/
|
||||||
|
public function testValidateAuthorizationRequestUnregistered()
|
||||||
|
{
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$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'
|
||||||
|
);
|
||||||
|
|
||||||
|
$request = new ServerRequest(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'php://input',
|
||||||
|
$headers = [],
|
||||||
|
$cookies = [],
|
||||||
|
$queryParams = [
|
||||||
|
'response_type' => 'code',
|
||||||
|
'client_id' => 'foo',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$server->validateAuthorizationRequest($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!@include_once __DIR__ . '/../vendor/autoload.php') {
|
||||||
|
$message = <<<MSG
|
||||||
|
You must set up the project dependencies, run the following commands:
|
||||||
|
> wget http://getcomposer.org/composer.phar
|
||||||
|
> php composer.phar install
|
||||||
|
MSG;
|
||||||
|
|
||||||
|
exit($message);
|
||||||
|
}
|
||||||
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Utils;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
use LeagueTests\Stubs\CryptTraitStub;
|
||||||
|
|
||||||
|
class CryptTraitTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \LeagueTests\Stubs\CryptTraitStub
|
||||||
|
*/
|
||||||
|
protected $cryptStub;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->cryptStub = new CryptTraitStub;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEncryptDecrypt()
|
||||||
|
{
|
||||||
|
$payload = 'alex loves whisky';
|
||||||
|
$encrypted = $this->cryptStub->doEncrypt($payload);
|
||||||
|
$plainText = $this->cryptStub->doDecrypt($encrypted);
|
||||||
|
|
||||||
|
$this->assertNotEquals($payload, $encrypted);
|
||||||
|
$this->assertEquals($payload, $plainText);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,496 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Grant;
|
||||||
|
|
||||||
|
use League\Event\Emitter;
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Grant\AbstractGrant;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||||
|
use LeagueTests\Stubs\AccessTokenEntity;
|
||||||
|
use LeagueTests\Stubs\AuthCodeEntity;
|
||||||
|
use LeagueTests\Stubs\ClientEntity;
|
||||||
|
use LeagueTests\Stubs\RefreshTokenEntity;
|
||||||
|
use LeagueTests\Stubs\ScopeEntity;
|
||||||
|
use Zend\Diactoros\ServerRequest;
|
||||||
|
|
||||||
|
class AbstractGrantTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testGetSet()
|
||||||
|
{
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$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();
|
||||||
|
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
|
||||||
|
$validateClientMethod->setAccessible(true);
|
||||||
|
|
||||||
|
$result = $validateClientMethod->invoke($grantMock, $serverRequest, true, true);
|
||||||
|
$this->assertEquals($client, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateClientConfidential()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'redirect_uri' => 'http://foo/bar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
|
||||||
|
$validateClientMethod->setAccessible(true);
|
||||||
|
|
||||||
|
$result = $validateClientMethod->invoke($grantMock, $serverRequest, true, true);
|
||||||
|
$this->assertEquals($client, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
*/
|
||||||
|
public function testValidateClientMissingClientId()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
|
||||||
|
$validateClientMethod->setAccessible(true);
|
||||||
|
|
||||||
|
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
*/
|
||||||
|
public function testValidateClientMissingClientSecret()
|
||||||
|
{
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn(null);
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody([
|
||||||
|
'client_id' => 'foo',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
|
||||||
|
$validateClientMethod->setAccessible(true);
|
||||||
|
|
||||||
|
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
*/
|
||||||
|
public function testValidateClientInvalidClientSecret()
|
||||||
|
{
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn(null);
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody([
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'foo',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
|
||||||
|
$validateClientMethod->setAccessible(true);
|
||||||
|
|
||||||
|
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
*/
|
||||||
|
public function testValidateClientInvalidRedirectUri()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setRedirectUri('http://foo/bar');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody([
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'redirect_uri' => 'http://bar/foo',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
|
||||||
|
$validateClientMethod->setAccessible(true);
|
||||||
|
|
||||||
|
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
*/
|
||||||
|
public function testValidateClientInvalidRedirectUriArray()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setRedirectUri(['http://foo/bar']);
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody([
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'redirect_uri' => 'http://bar/foo',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
|
||||||
|
$validateClientMethod->setAccessible(true);
|
||||||
|
|
||||||
|
$validateClientMethod->invoke($grantMock, $serverRequest, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
*/
|
||||||
|
public function testValidateClientBadClient()
|
||||||
|
{
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn(null);
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody([
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$validateClientMethod = $abstractGrantReflection->getMethod('validateClient');
|
||||||
|
$validateClientMethod->setAccessible(true);
|
||||||
|
|
||||||
|
$validateClientMethod->invoke($grantMock, $serverRequest, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanRespondToRequest()
|
||||||
|
{
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->method('getIdentifier')->willReturn('foobar');
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody([
|
||||||
|
'grant_type' => 'foobar',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertTrue($grantMock->canRespondToAccessTokenRequest($serverRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIssueRefreshToken()
|
||||||
|
{
|
||||||
|
$refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepoMock
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getNewRefreshToken')
|
||||||
|
->willReturn(new RefreshTokenEntity());
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setRefreshTokenTTL(new \DateInterval('PT1M'));
|
||||||
|
$grantMock->setRefreshTokenRepository($refreshTokenRepoMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
$issueRefreshTokenMethod = $abstractGrantReflection->getMethod('issueRefreshToken');
|
||||||
|
$issueRefreshTokenMethod->setAccessible(true);
|
||||||
|
|
||||||
|
$accessToken = new AccessTokenEntity();
|
||||||
|
/** @var RefreshTokenEntityInterface $refreshToken */
|
||||||
|
$refreshToken = $issueRefreshTokenMethod->invoke($grantMock, $accessToken);
|
||||||
|
$this->assertTrue($refreshToken instanceof RefreshTokenEntityInterface);
|
||||||
|
$this->assertEquals($accessToken, $refreshToken->getAccessToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIssueAccessToken()
|
||||||
|
{
|
||||||
|
$accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepoMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setAccessTokenRepository($accessTokenRepoMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
$issueAccessTokenMethod = $abstractGrantReflection->getMethod('issueAccessToken');
|
||||||
|
$issueAccessTokenMethod->setAccessible(true);
|
||||||
|
|
||||||
|
/** @var AccessTokenEntityInterface $accessToken */
|
||||||
|
$accessToken = $issueAccessTokenMethod->invoke(
|
||||||
|
$grantMock,
|
||||||
|
new \DateInterval('PT1H'),
|
||||||
|
new ClientEntity(),
|
||||||
|
123,
|
||||||
|
[new ScopeEntity()]
|
||||||
|
);
|
||||||
|
$this->assertTrue($accessToken instanceof AccessTokenEntityInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIssueAuthCode()
|
||||||
|
{
|
||||||
|
$authCodeRepoMock = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
|
||||||
|
$authCodeRepoMock->expects($this->once())->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setAuthCodeRepository($authCodeRepoMock);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
$issueAuthCodeMethod = $abstractGrantReflection->getMethod('issueAuthCode');
|
||||||
|
$issueAuthCodeMethod->setAccessible(true);
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
$issueAuthCodeMethod->invoke(
|
||||||
|
$grantMock,
|
||||||
|
new \DateInterval('PT1H'),
|
||||||
|
new ClientEntity(),
|
||||||
|
123,
|
||||||
|
'http://foo/bar',
|
||||||
|
[new ScopeEntity()]
|
||||||
|
) instanceof AuthCodeEntityInterface
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetCookieParameter()
|
||||||
|
{
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->method('getIdentifier')->willReturn('foobar');
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
$method = $abstractGrantReflection->getMethod('getCookieParameter');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withCookieParams([
|
||||||
|
'foo' => 'bar',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest));
|
||||||
|
$this->assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetQueryStringParameter()
|
||||||
|
{
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->method('getIdentifier')->willReturn('foobar');
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
$method = $abstractGrantReflection->getMethod('getQueryStringParameter');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withQueryParams([
|
||||||
|
'foo' => 'bar',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals('bar', $method->invoke($grantMock, 'foo', $serverRequest));
|
||||||
|
$this->assertEquals('foo', $method->invoke($grantMock, 'bar', $serverRequest, 'foo'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateScopes()
|
||||||
|
{
|
||||||
|
$scope = new ScopeEntity();
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setScopeRepository($scopeRepositoryMock);
|
||||||
|
|
||||||
|
$this->assertEquals([$scope], $grantMock->validateScopes('basic '));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
*/
|
||||||
|
public function testValidateScopesBadScope()
|
||||||
|
{
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn(null);
|
||||||
|
|
||||||
|
/** @var AbstractGrant $grantMock */
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->setScopeRepository($scopeRepositoryMock);
|
||||||
|
|
||||||
|
$grantMock->validateScopes('basic ');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGenerateUniqueIdentifier()
|
||||||
|
{
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
|
||||||
|
$abstractGrantReflection = new \ReflectionClass($grantMock);
|
||||||
|
$method = $abstractGrantReflection->getMethod('generateUniqueIdentifier');
|
||||||
|
$method->setAccessible(true);
|
||||||
|
|
||||||
|
$this->assertTrue(is_string($method->invoke($grantMock)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanRespondToAuthorizationRequest()
|
||||||
|
{
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$this->assertFalse($grantMock->canRespondToAuthorizationRequest(new ServerRequest()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \LogicException
|
||||||
|
*/
|
||||||
|
public function testValidateAuthorizationRequest()
|
||||||
|
{
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->validateAuthorizationRequest(new ServerRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \LogicException
|
||||||
|
*/
|
||||||
|
public function testCompleteAuthorizationRequest()
|
||||||
|
{
|
||||||
|
$grantMock = $this->getMockForAbstractClass(AbstractGrant::class);
|
||||||
|
$grantMock->completeAuthorizationRequest(new AuthorizationRequest());
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Grant;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||||
|
use LeagueTests\Stubs\AccessTokenEntity;
|
||||||
|
use LeagueTests\Stubs\ClientEntity;
|
||||||
|
use LeagueTests\Stubs\StubResponseType;
|
||||||
|
use Zend\Diactoros\ServerRequest;
|
||||||
|
|
||||||
|
class ClientCredentialsGrantTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testGetIdentifier()
|
||||||
|
{
|
||||||
|
$grant = new ClientCredentialsGrant();
|
||||||
|
$this->assertEquals('client_credentials', $grant->getIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRespondToRequest()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$grant = new ClientCredentialsGrant();
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
|
||||||
|
$this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,410 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
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\Repositories\ScopeRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
|
||||||
|
use LeagueTests\Stubs\AccessTokenEntity;
|
||||||
|
use LeagueTests\Stubs\ClientEntity;
|
||||||
|
use LeagueTests\Stubs\CryptTraitStub;
|
||||||
|
use LeagueTests\Stubs\ScopeEntity;
|
||||||
|
use LeagueTests\Stubs\StubResponseType;
|
||||||
|
use LeagueTests\Stubs\UserEntity;
|
||||||
|
use Zend\Diactoros\ServerRequest;
|
||||||
|
|
||||||
|
class ImplicitGrantTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* CryptTrait stub
|
||||||
|
*/
|
||||||
|
protected $cryptStub;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->cryptStub = new CryptTraitStub();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetIdentifier()
|
||||||
|
{
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
$this->assertEquals('implicit', $grant->getIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanRespondToAccessTokenRequest()
|
||||||
|
{
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
|
||||||
|
$this->assertFalse(
|
||||||
|
$grant->canRespondToAccessTokenRequest(new ServerRequest())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \LogicException
|
||||||
|
*/
|
||||||
|
public function testRespondToAccessTokenRequest()
|
||||||
|
{
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
$grant->respondToAccessTokenRequest(
|
||||||
|
new ServerRequest(),
|
||||||
|
new StubResponseType(),
|
||||||
|
new \DateInterval('PT10M')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanRespondToAuthorizationRequest()
|
||||||
|
{
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
|
||||||
|
$request = new ServerRequest(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'php://input',
|
||||||
|
$headers = [],
|
||||||
|
$cookies = [],
|
||||||
|
$queryParams = [
|
||||||
|
'response_type' => 'token',
|
||||||
|
'client_id' => 'foo',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertTrue($grant->canRespondToAuthorizationRequest($request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateAuthorizationRequest()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setRedirectUri('http://foo/bar');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeEntity = new ScopeEntity();
|
||||||
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
|
||||||
|
$request = new ServerRequest(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'php://input',
|
||||||
|
$headers = [],
|
||||||
|
$cookies = [],
|
||||||
|
$queryParams = [
|
||||||
|
'response_type' => 'code',
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'redirect_uri' => 'http://foo/bar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateAuthorizationRequestRedirectUriArray()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setRedirectUri(['http://foo/bar']);
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeEntity = new ScopeEntity();
|
||||||
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
|
||||||
|
$request = new ServerRequest(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'php://input',
|
||||||
|
$headers = [],
|
||||||
|
$cookies = [],
|
||||||
|
$queryParams = [
|
||||||
|
'response_type' => 'code',
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'redirect_uri' => 'http://foo/bar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 3
|
||||||
|
*/
|
||||||
|
public function testValidateAuthorizationRequestMissingClientId()
|
||||||
|
{
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$request = new ServerRequest(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'php://input',
|
||||||
|
$headers = [],
|
||||||
|
$cookies = [],
|
||||||
|
$queryParams = [
|
||||||
|
'response_type' => 'code',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$grant->validateAuthorizationRequest($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 4
|
||||||
|
*/
|
||||||
|
public function testValidateAuthorizationRequestInvalidClientId()
|
||||||
|
{
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn(null);
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$request = new ServerRequest(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'php://input',
|
||||||
|
$headers = [],
|
||||||
|
$cookies = [],
|
||||||
|
$queryParams = [
|
||||||
|
'response_type' => 'code',
|
||||||
|
'client_id' => 'foo',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$grant->validateAuthorizationRequest($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 4
|
||||||
|
*/
|
||||||
|
public function testValidateAuthorizationRequestBadRedirectUriString()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setRedirectUri('http://foo/bar');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$request = new ServerRequest(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'php://input',
|
||||||
|
$headers = [],
|
||||||
|
$cookies = [],
|
||||||
|
$queryParams = [
|
||||||
|
'response_type' => 'code',
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'redirect_uri' => 'http://bar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$grant->validateAuthorizationRequest($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 4
|
||||||
|
*/
|
||||||
|
public function testValidateAuthorizationRequestBadRedirectUriArray()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setRedirectUri(['http://foo/bar']);
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
|
||||||
|
$request = new ServerRequest(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'php://input',
|
||||||
|
$headers = [],
|
||||||
|
$cookies = [],
|
||||||
|
$queryParams = [
|
||||||
|
'response_type' => 'code',
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'redirect_uri' => 'http://bar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$grant->validateAuthorizationRequest($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCompleteAuthorizationRequest()
|
||||||
|
{
|
||||||
|
$authRequest = new AuthorizationRequest();
|
||||||
|
$authRequest->setAuthorizationApproved(true);
|
||||||
|
$authRequest->setClient(new ClientEntity());
|
||||||
|
$authRequest->setGrantTypeId('authorization_code');
|
||||||
|
$authRequest->setUser(new UserEntity());
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
|
||||||
|
$this->assertTrue($grant->completeAuthorizationRequest($authRequest) instanceof RedirectResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 9
|
||||||
|
*/
|
||||||
|
public function testCompleteAuthorizationRequestDenied()
|
||||||
|
{
|
||||||
|
$authRequest = new AuthorizationRequest();
|
||||||
|
$authRequest->setAuthorizationApproved(false);
|
||||||
|
$authRequest->setClient(new ClientEntity());
|
||||||
|
$authRequest->setGrantTypeId('authorization_code');
|
||||||
|
$authRequest->setUser(new UserEntity());
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
|
||||||
|
$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->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->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->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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,170 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Grant;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Grant\PasswordGrant;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
|
||||||
|
use LeagueTests\Stubs\AccessTokenEntity;
|
||||||
|
use LeagueTests\Stubs\ClientEntity;
|
||||||
|
use LeagueTests\Stubs\RefreshTokenEntity;
|
||||||
|
use LeagueTests\Stubs\StubResponseType;
|
||||||
|
use LeagueTests\Stubs\UserEntity;
|
||||||
|
use Zend\Diactoros\ServerRequest;
|
||||||
|
|
||||||
|
class PasswordGrantTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testGetIdentifier()
|
||||||
|
{
|
||||||
|
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
|
||||||
|
$this->assertEquals('password', $grant->getIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRespondToRequest()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
|
||||||
|
$userEntity = new UserEntity();
|
||||||
|
$userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn($userEntity);
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
|
||||||
|
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
|
||||||
|
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
|
||||||
|
|
||||||
|
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'username' => 'foo',
|
||||||
|
'password' => 'bar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
|
||||||
|
$this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface);
|
||||||
|
$this->assertTrue($responseType->getRefreshToken() instanceof RefreshTokenEntityInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
*/
|
||||||
|
public function testRespondToRequestMissingUsername()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
*/
|
||||||
|
public function testRespondToRequestMissingPassword()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'username' => 'alex',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
*/
|
||||||
|
public function testRespondToRequestBadCredentials()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
|
||||||
|
$userRepositoryMock->method('getUserEntityByUserCredentials')->willReturn(null);
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'username' => 'alex',
|
||||||
|
'password' => 'whisky',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,421 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Grant;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Grant\RefreshTokenGrant;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||||
|
use LeagueTests\Stubs\AccessTokenEntity;
|
||||||
|
use LeagueTests\Stubs\ClientEntity;
|
||||||
|
use LeagueTests\Stubs\CryptTraitStub;
|
||||||
|
use LeagueTests\Stubs\RefreshTokenEntity;
|
||||||
|
use LeagueTests\Stubs\ScopeEntity;
|
||||||
|
use LeagueTests\Stubs\StubResponseType;
|
||||||
|
use Zend\Diactoros\ServerRequest;
|
||||||
|
|
||||||
|
class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CryptTraitStub
|
||||||
|
*/
|
||||||
|
protected $cryptStub;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->cryptStub = new CryptTraitStub();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetIdentifier()
|
||||||
|
{
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||||
|
$this->assertEquals('refresh_token', $grant->getIdentifier());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRespondToRequest()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('foo');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeEntity = new ScopeEntity();
|
||||||
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
$accessTokenRepositoryMock
|
||||||
|
->expects($this->once())
|
||||||
|
->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
|
||||||
|
$refreshTokenRepositoryMock
|
||||||
|
->expects($this->once())
|
||||||
|
->method('persistNewRefreshToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$oldRefreshToken = $this->cryptStub->doEncrypt(
|
||||||
|
json_encode(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'refresh_token_id' => 'zyxwvu',
|
||||||
|
'access_token_id' => 'abcdef',
|
||||||
|
'scopes' => ['foo'],
|
||||||
|
'user_id' => 123,
|
||||||
|
'expire_time' => time() + 3600,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'refresh_token' => $oldRefreshToken,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
|
||||||
|
$this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface);
|
||||||
|
$this->assertTrue($responseType->getRefreshToken() instanceof RefreshTokenEntityInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRespondToReducedScopes()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('foo');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
|
||||||
|
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
|
||||||
|
|
||||||
|
$scope = new ScopeEntity();
|
||||||
|
$scope->setIdentifier('foo');
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
|
||||||
|
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$oldRefreshToken = $this->cryptStub->doEncrypt(
|
||||||
|
json_encode(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'refresh_token_id' => 'zyxwvu',
|
||||||
|
'access_token_id' => 'abcdef',
|
||||||
|
'scopes' => ['foo', 'bar'],
|
||||||
|
'user_id' => 123,
|
||||||
|
'expire_time' => time() + 3600,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'refresh_token' => $oldRefreshToken,
|
||||||
|
'scope' => 'foo',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
|
||||||
|
$this->assertTrue($responseType->getAccessToken() instanceof AccessTokenEntityInterface);
|
||||||
|
$this->assertTrue($responseType->getRefreshToken() instanceof RefreshTokenEntityInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 5
|
||||||
|
*/
|
||||||
|
public function testRespondToUnexpectedScope()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('foo');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$scope = new ScopeEntity();
|
||||||
|
$scope->setIdentifier('foobar');
|
||||||
|
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
|
||||||
|
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scope);
|
||||||
|
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setScopeRepository($scopeRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$oldRefreshToken = $this->cryptStub->doEncrypt(
|
||||||
|
json_encode(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'refresh_token_id' => 'zyxwvu',
|
||||||
|
'access_token_id' => 'abcdef',
|
||||||
|
'scopes' => ['foo', 'bar'],
|
||||||
|
'user_id' => 123,
|
||||||
|
'expire_time' => time() + 3600,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'refresh_token' => $oldRefreshToken,
|
||||||
|
'scope' => 'foobar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 3
|
||||||
|
*/
|
||||||
|
public function testRespondToRequestMissingOldToken()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('foo');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 8
|
||||||
|
*/
|
||||||
|
public function testRespondToRequestInvalidOldToken()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('foo');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$oldRefreshToken = 'foobar';
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'refresh_token' => $oldRefreshToken,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 8
|
||||||
|
*/
|
||||||
|
public function testRespondToRequestClientMismatch()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('foo');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('persistNewAccessToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
|
||||||
|
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$oldRefreshToken = $this->cryptStub->doEncrypt(
|
||||||
|
json_encode(
|
||||||
|
[
|
||||||
|
'client_id' => 'bar',
|
||||||
|
'refresh_token_id' => 'zyxwvu',
|
||||||
|
'access_token_id' => 'abcdef',
|
||||||
|
'scopes' => ['foo'],
|
||||||
|
'user_id' => 123,
|
||||||
|
'expire_time' => time() + 3600,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'refresh_token' => $oldRefreshToken,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 8
|
||||||
|
*/
|
||||||
|
public function testRespondToRequestExpiredToken()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('foo');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$oldRefreshToken = $this->cryptStub->doEncrypt(
|
||||||
|
json_encode(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'refresh_token_id' => 'zyxwvu',
|
||||||
|
'access_token_id' => 'abcdef',
|
||||||
|
'scopes' => ['foo'],
|
||||||
|
'user_id' => 123,
|
||||||
|
'expire_time' => time() - 3600,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'refresh_token' => $oldRefreshToken,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
* @expectedExceptionCode 8
|
||||||
|
*/
|
||||||
|
public function testRespondToRequestRevokedToken()
|
||||||
|
{
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('foo');
|
||||||
|
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
|
||||||
|
$refreshTokenRepositoryMock->method('isRefreshTokenRevoked')->willReturn(true);
|
||||||
|
|
||||||
|
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
|
||||||
|
$grant->setClientRepository($clientRepositoryMock);
|
||||||
|
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
|
||||||
|
$grant->setEncryptionKey($this->cryptStub->getKey());
|
||||||
|
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$oldRefreshToken = $this->cryptStub->doEncrypt(
|
||||||
|
json_encode(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'refresh_token_id' => 'zyxwvu',
|
||||||
|
'access_token_id' => 'abcdef',
|
||||||
|
'scopes' => ['foo'],
|
||||||
|
'user_id' => 123,
|
||||||
|
'expire_time' => time() + 3600,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest();
|
||||||
|
$serverRequest = $serverRequest->withParsedBody(
|
||||||
|
[
|
||||||
|
'client_id' => 'foo',
|
||||||
|
'client_secret' => 'bar',
|
||||||
|
'refresh_token' => $oldRefreshToken,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$responseType = new StubResponseType();
|
||||||
|
$grant->respondToAccessTokenRequest($serverRequest, $responseType, new \DateInterval('PT5M'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Middleware;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\AuthorizationServer;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\Grant\ClientCredentialsGrant;
|
||||||
|
use League\OAuth2\Server\Middleware\AuthorizationServerMiddleware;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
|
||||||
|
use LeagueTests\Stubs\AccessTokenEntity;
|
||||||
|
use LeagueTests\Stubs\ClientEntity;
|
||||||
|
use LeagueTests\Stubs\StubResponseType;
|
||||||
|
use Zend\Diactoros\Response;
|
||||||
|
use Zend\Diactoros\ServerRequestFactory;
|
||||||
|
|
||||||
|
class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testValidResponse()
|
||||||
|
{
|
||||||
|
$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->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
|
||||||
|
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepository,
|
||||||
|
$accessRepositoryMock,
|
||||||
|
$scopeRepositoryMock,
|
||||||
|
'file://' . __DIR__ . '/../Stubs/private.key',
|
||||||
|
base64_encode(random_bytes(36)),
|
||||||
|
new StubResponseType()
|
||||||
|
);
|
||||||
|
|
||||||
|
$server->enableGrantType(new ClientCredentialsGrant());
|
||||||
|
|
||||||
|
$_POST['grant_type'] = 'client_credentials';
|
||||||
|
$_POST['client_id'] = 'foo';
|
||||||
|
$_POST['client_secret'] = 'bar';
|
||||||
|
|
||||||
|
$request = ServerRequestFactory::fromGlobals();
|
||||||
|
|
||||||
|
$middleware = new AuthorizationServerMiddleware($server);
|
||||||
|
$response = $middleware->__invoke(
|
||||||
|
$request,
|
||||||
|
new Response(),
|
||||||
|
function () {
|
||||||
|
return func_get_args()[1];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOAuthErrorResponse()
|
||||||
|
{
|
||||||
|
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
|
||||||
|
$clientRepository->method('getClientEntity')->willReturn(null);
|
||||||
|
|
||||||
|
$server = new AuthorizationServer(
|
||||||
|
$clientRepository,
|
||||||
|
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||||
|
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
|
||||||
|
'file://' . __DIR__ . '/../Stubs/private.key',
|
||||||
|
base64_encode(random_bytes(36)),
|
||||||
|
new StubResponseType()
|
||||||
|
);
|
||||||
|
|
||||||
|
$server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M'));
|
||||||
|
|
||||||
|
$_POST['grant_type'] = 'client_credentials';
|
||||||
|
$_POST['client_id'] = 'foo';
|
||||||
|
$_POST['client_secret'] = 'bar';
|
||||||
|
|
||||||
|
$request = ServerRequestFactory::fromGlobals();
|
||||||
|
|
||||||
|
$middleware = new AuthorizationServerMiddleware($server);
|
||||||
|
|
||||||
|
$response = $middleware->__invoke(
|
||||||
|
$request,
|
||||||
|
new Response(),
|
||||||
|
function () {
|
||||||
|
return func_get_args()[1];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(401, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOAuthErrorResponseRedirectUri()
|
||||||
|
{
|
||||||
|
$exception = OAuthServerException::invalidScope('test', 'http://foo/bar');
|
||||||
|
$response = $exception->generateHttpResponse(new Response());
|
||||||
|
|
||||||
|
$this->assertEquals(302, $response->getStatusCode());
|
||||||
|
$this->assertEquals('http://foo/bar?error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope',
|
||||||
|
$response->getHeader('location')[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOAuthErrorResponseRedirectUriFragment()
|
||||||
|
{
|
||||||
|
$exception = OAuthServerException::invalidScope('test', 'http://foo/bar');
|
||||||
|
$response = $exception->generateHttpResponse(new Response(), true);
|
||||||
|
|
||||||
|
$this->assertEquals(302, $response->getStatusCode());
|
||||||
|
$this->assertEquals('http://foo/bar#error=invalid_scope&message=The+requested+scope+is+invalid%2C+unknown%2C+or+malformed&hint=Check+the+%60test%60+scope',
|
||||||
|
$response->getHeader('location')[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Middleware;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
use League\OAuth2\Server\Middleware\ResourceServerMiddleware;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\ResourceServer;
|
||||||
|
use LeagueTests\Stubs\AccessTokenEntity;
|
||||||
|
use LeagueTests\Stubs\ClientEntity;
|
||||||
|
use Zend\Diactoros\Response;
|
||||||
|
use Zend\Diactoros\ServerRequest;
|
||||||
|
|
||||||
|
class ResourceServerMiddlewareTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testValidResponse()
|
||||||
|
{
|
||||||
|
$server = new ResourceServer(
|
||||||
|
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||||
|
'file://' . __DIR__ . '/../Stubs/public.key'
|
||||||
|
);
|
||||||
|
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('clientName');
|
||||||
|
|
||||||
|
$accessToken = new AccessTokenEntity();
|
||||||
|
$accessToken->setIdentifier('test');
|
||||||
|
$accessToken->setUserIdentifier(123);
|
||||||
|
$accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H')));
|
||||||
|
$accessToken->setClient($client);
|
||||||
|
|
||||||
|
$token = $accessToken->convertToJWT(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$request = new ServerRequest();
|
||||||
|
$request = $request->withHeader('authorization', sprintf('Bearer %s', $token));
|
||||||
|
|
||||||
|
$middleware = new ResourceServerMiddleware($server);
|
||||||
|
$response = $middleware->__invoke(
|
||||||
|
$request,
|
||||||
|
new Response(),
|
||||||
|
function () {
|
||||||
|
$this->assertEquals('test', func_get_args()[0]->getAttribute('oauth_access_token_id'));
|
||||||
|
|
||||||
|
return func_get_args()[1];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidResponseExpiredToken()
|
||||||
|
{
|
||||||
|
$server = new ResourceServer(
|
||||||
|
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||||
|
'file://' . __DIR__ . '/../Stubs/public.key'
|
||||||
|
);
|
||||||
|
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('clientName');
|
||||||
|
|
||||||
|
$accessToken = new AccessTokenEntity();
|
||||||
|
$accessToken->setIdentifier('test');
|
||||||
|
$accessToken->setUserIdentifier(123);
|
||||||
|
$accessToken->setExpiryDateTime((new \DateTime())->sub(new \DateInterval('PT1H')));
|
||||||
|
$accessToken->setClient($client);
|
||||||
|
|
||||||
|
$token = $accessToken->convertToJWT(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
|
||||||
|
$request = new ServerRequest();
|
||||||
|
$request = $request->withHeader('authorization', sprintf('Bearer %s', $token));
|
||||||
|
|
||||||
|
$middleware = new ResourceServerMiddleware($server);
|
||||||
|
$response = $middleware->__invoke(
|
||||||
|
$request,
|
||||||
|
new Response(),
|
||||||
|
function () {
|
||||||
|
$this->assertEquals('test', func_get_args()[0]->getAttribute('oauth_access_token_id'));
|
||||||
|
|
||||||
|
return func_get_args()[1];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(401, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testErrorResponse()
|
||||||
|
{
|
||||||
|
$server = new ResourceServer(
|
||||||
|
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||||
|
'file://' . __DIR__ . '/../Stubs/public.key'
|
||||||
|
);
|
||||||
|
|
||||||
|
$request = new ServerRequest();
|
||||||
|
$request = $request->withHeader('authorization', '');
|
||||||
|
|
||||||
|
$middleware = new ResourceServerMiddleware($server);
|
||||||
|
$response = $middleware->__invoke(
|
||||||
|
$request,
|
||||||
|
new Response(),
|
||||||
|
function () {
|
||||||
|
return func_get_args()[1];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(401, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace LeagueTests;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\ResourceServer;
|
||||||
|
use Zend\Diactoros\ServerRequestFactory;
|
||||||
|
|
||||||
|
class ResourceServerTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testValidateAuthenticatedRequest()
|
||||||
|
{
|
||||||
|
$server = new ResourceServer(
|
||||||
|
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
|
||||||
|
'file://' . __DIR__ . '/Stubs/public.key'
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$server->validateAuthenticatedRequest(ServerRequestFactory::fromGlobals());
|
||||||
|
} catch (OAuthServerException $e) {
|
||||||
|
$this->assertEquals('Missing "Authorization" header', $e->getHint());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,298 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\ResponseTypes;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator;
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
|
||||||
|
use LeagueTests\Stubs\AccessTokenEntity;
|
||||||
|
use LeagueTests\Stubs\ClientEntity;
|
||||||
|
use LeagueTests\Stubs\RefreshTokenEntity;
|
||||||
|
use LeagueTests\Stubs\ScopeEntity;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Zend\Diactoros\Response;
|
||||||
|
use Zend\Diactoros\ServerRequest;
|
||||||
|
|
||||||
|
class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testGenerateHttpResponse()
|
||||||
|
{
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
|
||||||
|
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
|
||||||
|
|
||||||
|
$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));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGenerateHttpResponseWithExtraParams()
|
||||||
|
{
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$responseType = new BearerTokenResponseWithParams($accessTokenRepositoryMock);
|
||||||
|
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
|
||||||
|
|
||||||
|
$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();
|
||||||
|
$accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false);
|
||||||
|
|
||||||
|
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
|
||||||
|
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
|
||||||
|
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('clientName');
|
||||||
|
|
||||||
|
$accessToken = new AccessTokenEntity();
|
||||||
|
$accessToken->setIdentifier('abcdef');
|
||||||
|
$accessToken->setUserIdentifier(123);
|
||||||
|
$accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H')));
|
||||||
|
$accessToken->setClient($client);
|
||||||
|
|
||||||
|
$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());
|
||||||
|
$json = json_decode((string) $response->getBody());
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false);
|
||||||
|
|
||||||
|
$authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock);
|
||||||
|
$authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
|
||||||
|
|
||||||
|
$request = new ServerRequest();
|
||||||
|
$request = $request->withHeader('authorization', sprintf('Bearer %s', $json->access_token));
|
||||||
|
|
||||||
|
$request = $authorizationValidator->validateAuthorization($request);
|
||||||
|
|
||||||
|
$this->assertEquals('abcdef', $request->getAttribute('oauth_access_token_id'));
|
||||||
|
$this->assertEquals('clientName', $request->getAttribute('oauth_client_id'));
|
||||||
|
$this->assertEquals('123', $request->getAttribute('oauth_user_id'));
|
||||||
|
$this->assertEquals([], $request->getAttribute('oauth_scopes'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDetermineAccessTokenInHeaderInvalidJWT()
|
||||||
|
{
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(false);
|
||||||
|
|
||||||
|
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
|
||||||
|
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
|
||||||
|
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('clientName');
|
||||||
|
|
||||||
|
$accessToken = new AccessTokenEntity();
|
||||||
|
$accessToken->setIdentifier('abcdef');
|
||||||
|
$accessToken->setUserIdentifier(123);
|
||||||
|
$accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H')));
|
||||||
|
$accessToken->setClient($client);
|
||||||
|
|
||||||
|
$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());
|
||||||
|
$json = json_decode((string) $response->getBody());
|
||||||
|
|
||||||
|
$authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock);
|
||||||
|
$authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
|
||||||
|
|
||||||
|
$request = new ServerRequest();
|
||||||
|
$request = $request->withHeader('authorization', sprintf('Bearer %s', $json->access_token . 'foo'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$authorizationValidator->validateAuthorization($request);
|
||||||
|
} catch (OAuthServerException $e) {
|
||||||
|
$this->assertEquals(
|
||||||
|
'Access token could not be verified',
|
||||||
|
$e->getHint()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDetermineAccessTokenInHeaderRevokedToken()
|
||||||
|
{
|
||||||
|
$responseType = new BearerTokenResponse();
|
||||||
|
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
|
||||||
|
|
||||||
|
$client = new ClientEntity();
|
||||||
|
$client->setIdentifier('clientName');
|
||||||
|
|
||||||
|
$accessToken = new AccessTokenEntity();
|
||||||
|
$accessToken->setIdentifier('abcdef');
|
||||||
|
$accessToken->setUserIdentifier(123);
|
||||||
|
$accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H')));
|
||||||
|
$accessToken->setClient($client);
|
||||||
|
|
||||||
|
$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());
|
||||||
|
$json = json_decode((string) $response->getBody());
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
$accessTokenRepositoryMock->method('isAccessTokenRevoked')->willReturn(true);
|
||||||
|
|
||||||
|
$authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock);
|
||||||
|
$authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
|
||||||
|
|
||||||
|
$request = new ServerRequest();
|
||||||
|
$request = $request->withHeader('authorization', sprintf('Bearer %s', $json->access_token));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$authorizationValidator->validateAuthorization($request);
|
||||||
|
} catch (OAuthServerException $e) {
|
||||||
|
$this->assertEquals(
|
||||||
|
'Access token has been revoked',
|
||||||
|
$e->getHint()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDetermineAccessTokenInHeaderInvalidToken()
|
||||||
|
{
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
|
||||||
|
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock);
|
||||||
|
$authorizationValidator->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
|
||||||
|
|
||||||
|
$request = new ServerRequest();
|
||||||
|
$request = $request->withHeader('authorization', 'Bearer blah');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$authorizationValidator->validateAuthorization($request);
|
||||||
|
} catch (OAuthServerException $e) {
|
||||||
|
$this->assertEquals(
|
||||||
|
'The JWT string must have two dots',
|
||||||
|
$e->getHint()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDetermineMissingBearerInHeader()
|
||||||
|
{
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$responseType = new BearerTokenResponse($accessTokenRepositoryMock);
|
||||||
|
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
|
||||||
|
$responseType->setEncryptionKey(base64_encode(random_bytes(36)));
|
||||||
|
|
||||||
|
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
|
||||||
|
|
||||||
|
$authorizationValidator = new BearerTokenValidator($accessTokenRepositoryMock);
|
||||||
|
$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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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'];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Stubs;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\AccessTokenTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
|
||||||
|
|
||||||
|
class AccessTokenEntity implements AccessTokenEntityInterface
|
||||||
|
{
|
||||||
|
use AccessTokenTrait, TokenEntityTrait, EntityTrait;
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Stubs;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\AuthCodeTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
|
||||||
|
|
||||||
|
class AuthCodeEntity implements AuthCodeEntityInterface
|
||||||
|
{
|
||||||
|
use EntityTrait, TokenEntityTrait, AuthCodeTrait;
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Stubs;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\ClientTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
|
||||||
|
class ClientEntity implements ClientEntityInterface
|
||||||
|
{
|
||||||
|
use EntityTrait, ClientTrait;
|
||||||
|
|
||||||
|
public function setRedirectUri($uri)
|
||||||
|
{
|
||||||
|
$this->redirectUri = $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setName($name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Stubs;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\CryptKey;
|
||||||
|
use League\OAuth2\Server\CryptTrait;
|
||||||
|
|
||||||
|
class CryptTraitStub
|
||||||
|
{
|
||||||
|
use CryptTrait;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->setEncryptionKey(base64_encode(random_bytes(36)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey()
|
||||||
|
{
|
||||||
|
return $this->encryptionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doEncrypt($unencryptedData)
|
||||||
|
{
|
||||||
|
return $this->encrypt($unencryptedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doDecrypt($encryptedData)
|
||||||
|
{
|
||||||
|
return $this->decrypt($encryptedData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Stubs;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\RefreshTokenTrait;
|
||||||
|
|
||||||
|
class RefreshTokenEntity implements RefreshTokenEntityInterface
|
||||||
|
{
|
||||||
|
use RefreshTokenTrait, EntityTrait;
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Stubs;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\ScopeEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
|
||||||
|
class ScopeEntity implements ScopeEntityInterface
|
||||||
|
{
|
||||||
|
use EntityTrait;
|
||||||
|
|
||||||
|
public function jsonSerialize()
|
||||||
|
{
|
||||||
|
return $this->getIdentifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Stubs;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
|
||||||
|
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||||
|
use League\OAuth2\Server\ResponseTypes\AbstractResponseType;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Zend\Diactoros\Response;
|
||||||
|
|
||||||
|
class StubResponseType extends AbstractResponseType
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAccessToken()
|
||||||
|
{
|
||||||
|
return $this->accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRefreshToken()
|
||||||
|
{
|
||||||
|
return $this->refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
|
||||||
|
*/
|
||||||
|
public function setAccessToken(AccessTokenEntityInterface $accessToken)
|
||||||
|
{
|
||||||
|
$this->accessToken = $accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshToken
|
||||||
|
*/
|
||||||
|
public function setRefreshToken(RefreshTokenEntityInterface $refreshToken)
|
||||||
|
{
|
||||||
|
$this->refreshToken = $refreshToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
*
|
||||||
|
* @throws \League\OAuth2\Server\Exception\OAuthServerException
|
||||||
|
*
|
||||||
|
* @return \Psr\Http\Message\ServerRequestInterface
|
||||||
|
*/
|
||||||
|
public function validateAccessToken(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
if ($request->getHeader('authorization')[0] === 'Basic test') {
|
||||||
|
return $request->withAttribute('oauth_access_token_id', 'test');
|
||||||
|
}
|
||||||
|
|
||||||
|
throw OAuthServerException::accessDenied();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ResponseInterface $response
|
||||||
|
*
|
||||||
|
* @return ResponseInterface
|
||||||
|
*/
|
||||||
|
public function generateHttpResponse(ResponseInterface $response)
|
||||||
|
{
|
||||||
|
return new Response();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace LeagueTests\Stubs;
|
||||||
|
|
||||||
|
use League\OAuth2\Server\Entities\Traits\EntityTrait;
|
||||||
|
use League\OAuth2\Server\Entities\UserEntityInterface;
|
||||||
|
|
||||||
|
class UserEntity implements UserEntityInterface
|
||||||
|
{
|
||||||
|
use EntityTrait;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->setIdentifier(123);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXgIBAAKBgQDOBcFjGUlo3BJ9zjwQLgAHn6Oy5Si0uB7MublTiPob8rWTiCE4
|
||||||
|
weAFqzPoAB07vB0t0f8c1R8rmwHMD5ljWPBgJ8FewtwAUzprOBcau6DWukd/TKxX
|
||||||
|
WeVLAl/NZxijI+jR5QDBYLNBtj1G4LBVHMmINd3ryCycbf9ac3rcC8zhrQIDAQAB
|
||||||
|
AoGADfOJ0wIlXHp6rhZHLvlOezWuSjEGfqZxP3/cMvH1rerTrPfs+AD5AKlFTJKl
|
||||||
|
aCQm/bFYy0ULZVKL3pu30Wh2bo1nh/wLuLSI9Nz3O8jqAP3z0i07SoRoQmb8fRnn
|
||||||
|
dwoDFqnk3uGqcOenheSqheIgl9vdW/3avhD6nkMKZGxPYwECQQDoSj/xHogEzMqB
|
||||||
|
1Z2E5H/exeE9GQ7+dGITRR2MSgo9WvcKdRhGaQ44dsnTmqiZWAfqAPJjTQIIA/Cn
|
||||||
|
YRRTeBbNAkEA4w0iEvCIygGQOAnWuvVzlh+pxIB+BTeGkbiBG7nkYYc9b6B/Tw1B
|
||||||
|
GWGRddBr/FIfPvy1X2ip/TBpH+9bHnE2YQJBAIbZw/EYhmIy+UUSW9WwSUNsoOu1
|
||||||
|
Rm0V53HEZ/jvaq5fxpa9j5AgoO7KlzROzp3m6wE/93cKV6mLkAO7ae9jAekCQQCf
|
||||||
|
B6DZIS6+RrAMACAt3SOzf8P6BYG/B7Ayusd7cw2ang4S9JiW9xKkw2kN2wj3t1F5
|
||||||
|
XalwBTAjTdgj7ROmU+ehAkEAkOyXKONGBoVfaixRHgBP6jIBSSPbB2Aosi0QAURX
|
||||||
|
6GOY7wOS1pCSntTOBQxV7wVjqFwYAR10MSxFSNfpJ7RkzA==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOBcFjGUlo3BJ9zjwQLgAHn6Oy
|
||||||
|
5Si0uB7MublTiPob8rWTiCE4weAFqzPoAB07vB0t0f8c1R8rmwHMD5ljWPBgJ8Fe
|
||||||
|
wtwAUzprOBcau6DWukd/TKxXWeVLAl/NZxijI+jR5QDBYLNBtj1G4LBVHMmINd3r
|
||||||
|
yCycbf9ac3rcC8zhrQIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
Loading…
Reference in new issue