mirror of
https://github.com/elyby/oauth2-server.git
synced 2025-05-31 14:12:07 +05:30
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14b6761c0f | ||
|
|
7c61922f07 | ||
|
|
20535ad95b | ||
|
|
e885114714 | ||
|
|
f80d0d39a4 | ||
|
|
7bfd5b7d0d | ||
|
|
143a2e32f7 | ||
|
|
8f418cff08 | ||
|
|
fcec1f3442 | ||
|
|
46e7eef14e | ||
|
|
51f44fdf17 | ||
|
|
f8b2e80ef3 | ||
|
|
7045785d89 | ||
|
|
301ddc53c7 | ||
|
|
2a6f900323 | ||
|
|
fb8f47e868 | ||
|
|
5b192b3548 |
@@ -2,7 +2,6 @@ filter:
|
||||
excluded_paths:
|
||||
- tests/*
|
||||
- vendor/*
|
||||
- examples/*
|
||||
checks:
|
||||
php:
|
||||
code_rating: true
|
||||
|
||||
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,6 +1,15 @@
|
||||
# Changelog
|
||||
|
||||
## 5.0.0 (release 2016-04-17)
|
||||
## 5.0.2 (released 2016-04-18)
|
||||
|
||||
* `state` parameter is now correctly returned after implicit grant authorization
|
||||
* Small code and docblock improvements
|
||||
|
||||
## 5.0.1 (released 2016-04-18)
|
||||
|
||||
* Fixes an issue (#550) whereby it was unclear whether or not to validate a client's secret during a request.
|
||||
|
||||
## 5.0.0 (released 2016-04-17)
|
||||
|
||||
Version 5 is a complete code rewrite.
|
||||
|
||||
|
||||
@@ -43,11 +43,6 @@ You can contribute to the documentation in the [gh-pages branch](https://github.
|
||||
|
||||
Please see [CONTRIBUTING.md](https://github.com/thephpleague/oauth2-server/blob/master/CONTRIBUTING.md) and [CONDUCT.md](https://github.com/thephpleague/oauth2-server/blob/master/CONDUCT.md) for details.
|
||||
|
||||
## Integration
|
||||
|
||||
- [CakePHP 3](https://github.com/uafrica/oauth-server)
|
||||
- [Laravel](https://github.com/lucadegasperi/oauth2-server-laravel)
|
||||
|
||||
## Support
|
||||
|
||||
Bugs and feature request are tracked on [GitHub](https://github.com/thephpleague/oauth2-server/issues).
|
||||
@@ -56,7 +51,7 @@ If you have any questions about OAuth _please_ open a ticket here; please **don'
|
||||
|
||||
## Security
|
||||
|
||||
If you discover any security related issues, please email hello@alexbilbie.com instead of using the issue tracker.
|
||||
If you discover any security related issues, please email `hello@alexbilbie.com` instead of using the issue tracker.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
use League\OAuth2\Server\ResourceServer;
|
||||
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
|
||||
@@ -16,63 +9,65 @@ use Slim\App;
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
// Add the resource server to the DI container
|
||||
ResourceServer::class => function () {
|
||||
// Setup the authorization server
|
||||
$server = new ResourceServer(
|
||||
new AccessTokenRepository(),
|
||||
'file://' . __DIR__ . '/../public.key'
|
||||
new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
|
||||
'file://' . __DIR__ . '/../public.key' // the authorization server's public key
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
// Add the resource server middleware which will intercept and validate requests
|
||||
$app->add(
|
||||
new \League\OAuth2\Server\Middleware\ResourceServerMiddleware(
|
||||
$app->getContainer()->get(ResourceServer::class)
|
||||
)
|
||||
);
|
||||
|
||||
$app->get('/users', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
// An example endpoint secured with OAuth 2.0
|
||||
$app->get(
|
||||
'/users',
|
||||
function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
|
||||
$users = [
|
||||
[
|
||||
'id' => 123,
|
||||
'name' => 'Alex',
|
||||
'email' => 'alex@thephpleague.com',
|
||||
],
|
||||
[
|
||||
'id' => 124,
|
||||
'name' => 'Frank',
|
||||
'email' => 'frank@thephpleague.com',
|
||||
],
|
||||
[
|
||||
'id' => 125,
|
||||
'name' => 'Phil',
|
||||
'email' => 'phil@thephpleague.com',
|
||||
],
|
||||
];
|
||||
$users = [
|
||||
[
|
||||
'id' => 123,
|
||||
'name' => 'Alex',
|
||||
'email' => 'alex@thephpleague.com',
|
||||
],
|
||||
[
|
||||
'id' => 124,
|
||||
'name' => 'Frank',
|
||||
'email' => 'frank@thephpleague.com',
|
||||
],
|
||||
[
|
||||
'id' => 125,
|
||||
'name' => 'Phil',
|
||||
'email' => 'phil@thephpleague.com',
|
||||
],
|
||||
];
|
||||
|
||||
// If the access token doesn't have the `basic` scope hide users' names
|
||||
if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < count($users); $i++) {
|
||||
unset($users[$i]['name']);
|
||||
// If the access token doesn't have the `basic` scope hide users' names
|
||||
if (in_array('basic', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < count($users); $i++) {
|
||||
unset($users[$i]['name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the access token doesn't have the `emal` scope hide users' email addresses
|
||||
if (in_array('email', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < count($users); $i++) {
|
||||
unset($users[$i]['email']);
|
||||
// If the access token doesn't have the `email` scope hide users' email addresses
|
||||
if (in_array('email', $request->getAttribute('oauth_scopes')) === false) {
|
||||
for ($i = 0; $i < count($users); $i++) {
|
||||
unset($users[$i]['email']);
|
||||
}
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode($users));
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
);
|
||||
|
||||
$response->getBody()->write(json_encode($users));
|
||||
|
||||
return $response->withStatus(200);
|
||||
});
|
||||
|
||||
$app->run();
|
||||
$app->run();
|
||||
@@ -30,9 +30,9 @@ $app = new App([
|
||||
$accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface
|
||||
|
||||
// Path to public and private keys
|
||||
$privateKey = 'file://path/to/private.key';
|
||||
$privateKey = 'file://'.__DIR__.'/../private.key';
|
||||
//$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase
|
||||
$publicKey = 'file://path/to/public.key';
|
||||
$publicKey = 'file://'.__DIR__.'/../public.key';
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Alex Bilbie <hello@alexbilbie.com>
|
||||
* @copyright Copyright (c) Alex Bilbie
|
||||
* @license http://mit-license.org/
|
||||
*
|
||||
* @link https://github.com/thephpleague/oauth2-server
|
||||
*/
|
||||
|
||||
use League\OAuth2\Server\AuthorizationServer;
|
||||
use League\OAuth2\Server\Exception\OAuthServerException;
|
||||
@@ -18,58 +11,64 @@ use OAuth2ServerExamples\Repositories\UserRepository;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
use Zend\Diactoros\Stream;
|
||||
|
||||
include __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$app = new App([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
],
|
||||
// Add the authorization server to the DI container
|
||||
AuthorizationServer::class => function () {
|
||||
// Init our repositories
|
||||
$clientRepository = new ClientRepository();
|
||||
$accessTokenRepository = new AccessTokenRepository();
|
||||
$scopeRepository = new ScopeRepository();
|
||||
$userRepository = new UserRepository();
|
||||
$refreshTokenRepository = new RefreshTokenRepository();
|
||||
|
||||
$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
|
||||
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
|
||||
|
||||
// Setup the authorization server
|
||||
$server = new AuthorizationServer(
|
||||
$clientRepository,
|
||||
$accessTokenRepository,
|
||||
$scopeRepository,
|
||||
$privateKeyPath,
|
||||
$publicKeyPath
|
||||
new ClientRepository(), // instance of ClientRepositoryInterface
|
||||
new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
|
||||
new ScopeRepository(), // instance of ScopeRepositoryInterface
|
||||
'file://'.__DIR__.'/../private.key', // path to private key
|
||||
'file://'.__DIR__.'/../public.key' // path to public key
|
||||
);
|
||||
|
||||
$grant = new PasswordGrant(
|
||||
new UserRepository(), // instance of UserRepositoryInterface
|
||||
new RefreshTokenRepository() // instance of RefreshTokenRepositoryInterface
|
||||
);
|
||||
$grant->setRefreshTokenTTL(new \DateInterval('P1M')); // refresh tokens will expire after 1 month
|
||||
|
||||
// Enable the password grant on the server with a token TTL of 1 hour
|
||||
$server->enableGrantType(
|
||||
new PasswordGrant($userRepository, $refreshTokenRepository),
|
||||
new \DateInterval('PT1H')
|
||||
$grant,
|
||||
new \DateInterval('PT1H') // access tokens will expire after 1 hour
|
||||
);
|
||||
|
||||
return $server;
|
||||
},
|
||||
]);
|
||||
|
||||
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
$app->post(
|
||||
'/access_token',
|
||||
function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
|
||||
|
||||
try {
|
||||
return $server->respondToAccessTokenRequest($request, $response);
|
||||
} catch (OAuthServerException $exception) {
|
||||
return $exception->generateHttpResponse($response);
|
||||
} catch (\Exception $exception) {
|
||||
$body = new Stream('php://temp', 'r+');
|
||||
$body->write($exception->getMessage());
|
||||
/* @var \League\OAuth2\Server\AuthorizationServer $server */
|
||||
$server = $app->getContainer()->get(AuthorizationServer::class);
|
||||
|
||||
return $response->withStatus(500)->withBody($body);
|
||||
try {
|
||||
|
||||
// Try to respond to the access token request
|
||||
return $server->respondToAccessTokenRequest($request, $response);
|
||||
|
||||
} catch (OAuthServerException $exception) {
|
||||
|
||||
// All instances of OAuthServerException can be converted to a PSR-7 response
|
||||
return $exception->generateHttpResponse($response);
|
||||
|
||||
} catch (\Exception $exception) {
|
||||
|
||||
// Catch unexpected exceptions
|
||||
$body = $response->getBody();
|
||||
$body->write($exception->getMessage());
|
||||
return $response->withStatus(500)->withBody($body);
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
$app->run();
|
||||
|
||||
@@ -17,13 +17,14 @@ class ClientRepository implements ClientRepositoryInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getClientEntity($clientIdentifier, $clientSecret = null, $redirectUri = null, $grantType = null)
|
||||
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',
|
||||
'secret' => password_hash('abc123', PASSWORD_BCRYPT),
|
||||
'name' => 'My Awesome App',
|
||||
'redirect_uri' => 'http://foo/bar',
|
||||
'is_confidential' => true,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -32,6 +33,14 @@ class ClientRepository implements ClientRepositoryInterface
|
||||
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']);
|
||||
|
||||
@@ -204,7 +204,7 @@ class AuthorizationServer implements EmitterAwareInterface
|
||||
protected function getResponseType()
|
||||
{
|
||||
if (!$this->responseType instanceof ResponseTypeInterface) {
|
||||
$this->responseType = new BearerTokenResponse($this->accessTokenRepository);
|
||||
$this->responseType = new BearerTokenResponse();
|
||||
}
|
||||
|
||||
$this->responseType->setPrivateKey($this->privateKey);
|
||||
|
||||
@@ -13,6 +13,8 @@ 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
|
||||
{
|
||||
@@ -36,4 +38,24 @@ trait AccessTokenTrait
|
||||
->sign(new Sha256(), new Key($privateKey->getKeyPath(), $privateKey->getPassPhrase()))
|
||||
->getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClientEntityInterface
|
||||
*/
|
||||
abstract public function getClient();
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
abstract public function getExpiryDateTime();
|
||||
|
||||
/**
|
||||
* @return string|int
|
||||
*/
|
||||
abstract public function getUserIdentifier();
|
||||
|
||||
/**
|
||||
* @return ScopeEntityInterface[]
|
||||
*/
|
||||
abstract public function getScopes();
|
||||
}
|
||||
|
||||
@@ -161,7 +161,8 @@ abstract class AbstractGrant implements GrantTypeInterface
|
||||
$client = $this->clientRepository->getClientEntity(
|
||||
$clientId,
|
||||
$this->getIdentifier(),
|
||||
$clientSecret
|
||||
$clientSecret,
|
||||
true
|
||||
);
|
||||
|
||||
if (!$client instanceof ClientEntityInterface) {
|
||||
|
||||
@@ -108,7 +108,12 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
}
|
||||
|
||||
// Finalize the requested scopes
|
||||
$scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client, $authCodePayload->user_id);
|
||||
$scopes = $this->scopeRepository->finalizeScopes(
|
||||
$scopes,
|
||||
$this->getIdentifier(),
|
||||
$client,
|
||||
$authCodePayload->user_id
|
||||
);
|
||||
} catch (\LogicException $e) {
|
||||
throw OAuthServerException::invalidRequest('code', 'Cannot decrypt the authorization code');
|
||||
}
|
||||
@@ -165,7 +170,9 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
|
||||
$client = $this->clientRepository->getClientEntity(
|
||||
$clientId,
|
||||
$this->getIdentifier()
|
||||
$this->getIdentifier(),
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
if ($client instanceof ClientEntityInterface === false) {
|
||||
@@ -192,7 +199,9 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
|
||||
$scopes = $this->validateScopes(
|
||||
$this->getQueryStringParameter('scope', $request),
|
||||
$client->getRedirectUri()
|
||||
is_array($client->getRedirectUri())
|
||||
? $client->getRedirectUri()[0]
|
||||
: $client->getRedirectUri()
|
||||
);
|
||||
|
||||
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||
@@ -232,25 +241,25 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
|
||||
$authorizationRequest->getScopes()
|
||||
);
|
||||
|
||||
$redirectPayload['code'] = $this->encrypt(
|
||||
json_encode(
|
||||
[
|
||||
'client_id' => $authCode->getClient()->getIdentifier(),
|
||||
'redirect_uri' => $authCode->getRedirectUri(),
|
||||
'auth_code_id' => $authCode->getIdentifier(),
|
||||
'scopes' => $authCode->getScopes(),
|
||||
'user_id' => $authCode->getUserIdentifier(),
|
||||
'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'),
|
||||
]
|
||||
)
|
||||
);
|
||||
$redirectPayload['state'] = $authorizationRequest->getState();
|
||||
|
||||
$response = new RedirectResponse();
|
||||
$response->setRedirectUri(
|
||||
$this->makeRedirectUri(
|
||||
$finalRedirectUri,
|
||||
$redirectPayload
|
||||
[
|
||||
'code' => $this->encrypt(
|
||||
json_encode(
|
||||
[
|
||||
'client_id' => $authCode->getClient()->getIdentifier(),
|
||||
'redirect_uri' => $authCode->getRedirectUri(),
|
||||
'auth_code_id' => $authCode->getIdentifier(),
|
||||
'scopes' => $authCode->getScopes(),
|
||||
'user_id' => $authCode->getUserIdentifier(),
|
||||
'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'),
|
||||
]
|
||||
)
|
||||
),
|
||||
'state' => $authorizationRequest->getState(),
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -117,7 +117,9 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
|
||||
$client = $this->clientRepository->getClientEntity(
|
||||
$clientId,
|
||||
$this->getIdentifier()
|
||||
$this->getIdentifier(),
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
if ($client instanceof ClientEntityInterface === false) {
|
||||
@@ -144,7 +146,9 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
|
||||
$scopes = $this->validateScopes(
|
||||
$this->getQueryStringParameter('scope', $request),
|
||||
$client->getRedirectUri()
|
||||
is_array($client->getRedirectUri())
|
||||
? $client->getRedirectUri()[0]
|
||||
: $client->getRedirectUri()
|
||||
);
|
||||
|
||||
$stateParameter = $this->getQueryStringParameter('state', $request);
|
||||
@@ -183,15 +187,16 @@ class ImplicitGrant extends AbstractAuthorizeGrant
|
||||
$authorizationRequest->getScopes()
|
||||
);
|
||||
|
||||
$redirectPayload['access_token'] = (string) $accessToken->convertToJWT($this->privateKey);
|
||||
$redirectPayload['token_type'] = 'bearer';
|
||||
$redirectPayload['expires_in'] = $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp();
|
||||
|
||||
$response = new RedirectResponse();
|
||||
$response->setRedirectUri(
|
||||
$this->makeRedirectUri(
|
||||
$finalRedirectUri,
|
||||
$redirectPayload,
|
||||
[
|
||||
'access_token' => (string) $accessToken->convertToJWT($this->privateKey),
|
||||
'token_type' => 'bearer',
|
||||
'expires_in' => $accessToken->getExpiryDateTime()->getTimestamp() - (new \DateTime())->getTimestamp(),
|
||||
'state' => $authorizationRequest->getState(),
|
||||
],
|
||||
'#'
|
||||
)
|
||||
);
|
||||
|
||||
@@ -16,11 +16,13 @@ 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 string $clientIdentifier The client's identifier
|
||||
* @param string $grantType The grant type used
|
||||
* @param null|string $clientSecret The client's secret (if sent)
|
||||
* @param bool $mustValidateSecret If true the client must attempt to validate the secret unless the client
|
||||
* is confidential
|
||||
*
|
||||
* @return \League\OAuth2\Server\Entities\ClientEntityInterface
|
||||
*/
|
||||
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null);
|
||||
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user