From 83912c804e0e2e1d4405bdca8728bccad1b46ca2 Mon Sep 17 00:00:00 2001 From: Moeen Basra Date: Sat, 30 Sep 2017 04:32:29 +0400 Subject: [PATCH] upgrade to passport 4 --- .gitignore | 1 + composer.json | 13 ++- src/ApiTokenCookieFactory.php | 12 +- src/AuthCode.php | 4 +- src/Bridge/AccessTokenRepository.php | 40 ++++--- src/Bridge/AuthCode.php | 2 +- src/Bridge/Client.php | 2 +- src/Bridge/FormatsScopesForStorage.php | 15 ++- src/Bridge/RefreshTokenRepository.php | 40 +++++-- src/Bridge/UserRepository.php | 11 +- src/Client.php | 8 +- src/ClientRepository.php | 49 +++++--- src/Console/ClientCommand.php | 2 - src/Console/InstallCommand.php | 5 +- src/Console/KeysCommand.php | 17 ++- src/Events/RefreshTokenCreated.php | 5 +- src/Guards/TokenGuard.php | 38 +++--- src/HasApiTokens.php | 4 +- .../Controllers/AccessTokenController.php | 23 ++-- .../ApproveAuthorizationController.php | 12 +- .../Controllers/AuthorizationController.php | 31 ++--- .../AuthorizedAccessTokenController.php | 38 +++++- src/Http/Controllers/ClientController.php | 35 +++--- src/Http/Controllers/ConvertsPsrResponses.php | 23 ++++ .../DenyAuthorizationController.php | 20 ++-- src/Http/Controllers/HandlesOAuthErrors.php | 10 +- .../PersonalAccessTokenController.php | 37 ++++-- .../RetrievesAuthRequestFromSession.php | 6 +- src/Http/Controllers/ScopeController.php | 2 +- .../Controllers/TransientTokenController.php | 8 +- .../Middleware/CheckClientCredentials.php | 40 +++++-- src/Http/Middleware/CheckForAnyScope.php | 3 +- src/Http/Middleware/CheckScopes.php | 4 +- src/Http/Middleware/CreateFreshApiToken.php | 13 +-- src/Passport.php | 54 ++++++--- src/PassportServiceProvider.php | 50 +++++--- src/PersonalAccessTokenFactory.php | 24 ++-- src/PersonalAccessTokenResult.php | 4 +- src/RouteRegistrar.php | 36 +++--- src/Token.php | 8 +- src/TokenRepository.php | 108 ++++++++++++++---- 41 files changed, 562 insertions(+), 295 deletions(-) create mode 100644 src/Http/Controllers/ConvertsPsrResponses.php diff --git a/.gitignore b/.gitignore index 935630a..9686786 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea /vendor composer.phar composer.lock diff --git a/composer.json b/composer.json index 3f3d206..57daf9a 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "require": { "php": ">=5.6.4", - "firebase/php-jwt": "~3.0|~4.0", + "firebase/php-jwt": "~3.0|~4.0|~5.0", "guzzlehttp/guzzle": "~6.0", "illuminate/auth": "~5.4", "illuminate/console": "~5.4", @@ -22,9 +22,9 @@ "illuminate/http": "~5.4", "illuminate/support": "~5.4", "league/oauth2-server": "^6.0", + "phpseclib/phpseclib": "^2.0", "symfony/psr-http-message-bridge": "~1.0", - "zendframework/zend-diactoros": "~1.0", - "phpseclib/phpseclib": "^2.0" + "zendframework/zend-diactoros": "~1.0" }, "require-dev": { "mockery/mockery": "~0.9", @@ -37,7 +37,12 @@ }, "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "3.0-dev" + }, + "laravel": { + "providers": [ + "MoeenBasra\\LaravelPassportMongoDB\\PassportServiceProvider" + ] } }, "minimum-stability": "dev", diff --git a/src/ApiTokenCookieFactory.php b/src/ApiTokenCookieFactory.php index a32bbc6..c28a16b 100644 --- a/src/ApiTokenCookieFactory.php +++ b/src/ApiTokenCookieFactory.php @@ -13,22 +13,22 @@ class ApiTokenCookieFactory /** * The configuration repository implementation. * - * @var Config + * @var \Illuminate\Contracts\Config\Repository */ protected $config; /** * The encrypter implementation. * - * @var Encrypter + * @var \Illuminate\Contracts\Encryption\Encrypter */ protected $encrypter; /** * Create an API token cookie factory instance. * - * @param Config $config - * @param Encrypter $encrypter + * @param \Illuminate\Contracts\Config\Repository $config + * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter * @return void */ public function __construct(Config $config, Encrypter $encrypter) @@ -42,7 +42,7 @@ public function __construct(Config $config, Encrypter $encrypter) * * @param mixed $userId * @param string $csrfToken - * @return Cookie + * @return \Symfony\Component\HttpFoundation\Cookie */ public function make($userId, $csrfToken) { @@ -66,7 +66,7 @@ public function make($userId, $csrfToken) * * @param mixed $userId * @param string $csrfToken - * @param Carbon $expiration + * @param \Carbon\Carbon $expiration * @return string */ protected function createToken($userId, $csrfToken, Carbon $expiration) diff --git a/src/AuthCode.php b/src/AuthCode.php index 3dd5cdf..7eae576 100644 --- a/src/AuthCode.php +++ b/src/AuthCode.php @@ -41,10 +41,10 @@ class AuthCode extends Model /** * Get the client that owns the authentication code. * - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * @return \Jenssegers\Mongodb\Relations\BelongsTo */ public function client() { - return $this->hasMany(Client::class); + return $this->belongTo(Client::class); } } diff --git a/src/Bridge/AccessTokenRepository.php b/src/Bridge/AccessTokenRepository.php index eb5b9a4..b2acdaf 100644 --- a/src/Bridge/AccessTokenRepository.php +++ b/src/Bridge/AccessTokenRepository.php @@ -3,7 +3,7 @@ namespace MoeenBasra\LaravelPassportMongoDB\Bridge; use DateTime; -use Illuminate\Database\Connection; +use MoeenBasra\LaravelPassportMongoDB\TokenRepository; use Illuminate\Contracts\Events\Dispatcher; use MoeenBasra\LaravelPassportMongoDB\Events\AccessTokenCreated; use League\OAuth2\Server\Entities\ClientEntityInterface; @@ -15,29 +15,29 @@ class AccessTokenRepository implements AccessTokenRepositoryInterface use FormatsScopesForStorage; /** - * The database connection. + * The token repository instance. * - * @var \Illuminate\Database\Connection + * @var \MoeenBasra\LaravelPassportMongoDB\TokenRepository */ - protected $database; + protected $tokenRepository; /** * The event dispatcher instance. * - * @var \Illuminate\Events\Dispatcher + * @var \Illuminate\Contracts\Events\Dispatcher */ private $events; /** * Create a new repository instance. * - * @param \Illuminate\Database\Connection $database - * @return void + * @param \MoeenBasra\LaravelPassportMongoDB\TokenRepository $tokenRepository + * @param \Illuminate\Contracts\Events\Dispatcher $events */ - public function __construct(Connection $database, Dispatcher $events) + public function __construct(TokenRepository $tokenRepository, Dispatcher $events) { $this->events = $events; - $this->database = $database; + $this->tokenRepository = $tokenRepository; } /** @@ -53,18 +53,22 @@ public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, */ public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity) { - $this->database->table('oauth_access_tokens')->insert([ - 'id' => $id = $accessTokenEntity->getIdentifier(), - 'user_id' => $userId = $accessTokenEntity->getUserIdentifier(), - 'client_id' => $clientId = $accessTokenEntity->getClient()->getIdentifier(), - 'scopes' => $this->formatScopesForStorage($accessTokenEntity->getScopes()), + $this->tokenRepository->create([ + '_id' => $accessTokenEntity->getIdentifier(), + 'user_id' => $accessTokenEntity->getUserIdentifier(), + 'client_id' => $accessTokenEntity->getClient()->getIdentifier(), + 'scopes' => $this->scopesToArray($accessTokenEntity->getScopes()), 'revoked' => false, 'created_at' => new DateTime, 'updated_at' => new DateTime, 'expires_at' => $accessTokenEntity->getExpiryDateTime(), ]); - $this->events->fire(new AccessTokenCreated($id, $userId, $clientId)); + $this->events->dispatch(new AccessTokenCreated( + $accessTokenEntity->getIdentifier(), + $accessTokenEntity->getUserIdentifier(), + $accessTokenEntity->getClient()->getIdentifier() + )); } /** @@ -72,8 +76,7 @@ public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEnt */ public function revokeAccessToken($tokenId) { - $this->database->table('oauth_access_tokens') - ->where('id', $tokenId)->update(['revoked' => true]); + $this->tokenRepository->revokeAccessToken($tokenId); } /** @@ -81,7 +84,6 @@ public function revokeAccessToken($tokenId) */ public function isAccessTokenRevoked($tokenId) { - return ! $this->database->table('oauth_access_tokens') - ->where('id', $tokenId)->where('revoked', false)->exists(); + return $this->tokenRepository->isAccessTokenRevoked($tokenId); } } diff --git a/src/Bridge/AuthCode.php b/src/Bridge/AuthCode.php index 8ae66eb..1fa2bdc 100644 --- a/src/Bridge/AuthCode.php +++ b/src/Bridge/AuthCode.php @@ -4,8 +4,8 @@ use League\OAuth2\Server\Entities\Traits\EntityTrait; use League\OAuth2\Server\Entities\Traits\AuthCodeTrait; -use League\OAuth2\Server\Entities\Traits\TokenEntityTrait; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; +use League\OAuth2\Server\Entities\Traits\TokenEntityTrait; class AuthCode implements AuthCodeEntityInterface { diff --git a/src/Bridge/Client.php b/src/Bridge/Client.php index 7c6810a..ad6a4fc 100644 --- a/src/Bridge/Client.php +++ b/src/Bridge/Client.php @@ -23,6 +23,6 @@ public function __construct($identifier, $name, $redirectUri) $this->setIdentifier($identifier); $this->name = $name; - $this->redirectUri = $redirectUri; + $this->redirectUri = explode(',', $redirectUri); } } diff --git a/src/Bridge/FormatsScopesForStorage.php b/src/Bridge/FormatsScopesForStorage.php index d2b9ec4..507c22a 100644 --- a/src/Bridge/FormatsScopesForStorage.php +++ b/src/Bridge/FormatsScopesForStorage.php @@ -12,8 +12,19 @@ trait FormatsScopesForStorage */ public function formatScopesForStorage(array $scopes) { - return json_encode(array_map(function ($scope) { + return json_encode($this->scopesToArray($scopes)); + } + + /** + * Get an array of scope identifiers for storage. + * + * @param array $scopes + * @return array + */ + public function scopesToArray(array $scopes) + { + return array_map(function ($scope) { return $scope->getIdentifier(); - }, $scopes)); + }, $scopes); } } diff --git a/src/Bridge/RefreshTokenRepository.php b/src/Bridge/RefreshTokenRepository.php index 4e3b361..064430d 100644 --- a/src/Bridge/RefreshTokenRepository.php +++ b/src/Bridge/RefreshTokenRepository.php @@ -2,7 +2,7 @@ namespace MoeenBasra\LaravelPassportMongoDB\Bridge; -use Illuminate\Database\Connection; +use Jenssegers\Mongodb\Connection; use Illuminate\Contracts\Events\Dispatcher; use MoeenBasra\LaravelPassportMongoDB\Events\RefreshTokenCreated; use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; @@ -10,29 +10,41 @@ class RefreshTokenRepository implements RefreshTokenRepositoryInterface { + /** + * The access token repository instance. + * + * @var \MoeenBasra\LaravelPassportMongoDB\Bridge\AccessTokenRepository + */ + protected $tokens; + /** * The database connection. * - * @var \Illuminate\Database\Connection + * @var \Jenssegers\Mongodb\Connection */ protected $database; /** * The event dispatcher instance. * - * @var \Illuminate\Events\Dispatcher + * @var \Illuminate\Contracts\Events\Dispatcher */ - private $events; + protected $events; /** * Create a new repository instance. * - * @param \Illuminate\Database\Connection $database + * @param \MoeenBasra\LaravelPassportMongoDB\Bridge\AccessTokenRepository $tokens + * @param \Jenssegers\Mongodb\Connection $database + * @param \Illuminate\Contracts\Events\Dispatcher $events * @return void */ - public function __construct(Connection $database, Dispatcher $events) + public function __construct(AccessTokenRepository $tokens, + Connection $database, + Dispatcher $events) { $this->events = $events; + $this->tokens = $tokens; $this->database = $database; } @@ -50,7 +62,7 @@ public function getNewRefreshToken() public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity) { $this->database->table('oauth_refresh_tokens')->insert([ - 'id' => $id = $refreshTokenEntity->getIdentifier(), + '_id' => $id = $refreshTokenEntity->getIdentifier(), 'access_token_id' => $accessTokenId = $refreshTokenEntity->getAccessToken()->getIdentifier(), 'revoked' => false, 'expires_at' => $refreshTokenEntity->getExpiryDateTime(), @@ -65,7 +77,7 @@ public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshToken public function revokeRefreshToken($tokenId) { $this->database->table('oauth_refresh_tokens') - ->where('id', $tokenId)->update(['revoked' => true]); + ->where('_id', $tokenId)->update(['revoked' => true]); } /** @@ -73,7 +85,15 @@ public function revokeRefreshToken($tokenId) */ public function isRefreshTokenRevoked($tokenId) { - return $this->database->table('oauth_refresh_tokens') - ->where('id', $tokenId)->where('revoked', true)->exists(); + $refreshToken = $this->database->table('oauth_refresh_tokens') + ->where('_id', $tokenId)->first(); + + if ($refreshToken === null || $refreshToken->revoked) { + return true; + } + + return $this->tokens->isAccessTokenRevoked( + $refreshToken->access_token_id + ); } } diff --git a/src/Bridge/UserRepository.php b/src/Bridge/UserRepository.php index 99cd7e7..fc105f1 100644 --- a/src/Bridge/UserRepository.php +++ b/src/Bridge/UserRepository.php @@ -32,8 +32,10 @@ public function __construct(Hasher $hasher) */ public function getUserEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity) { - if (is_null($model = config('auth.providers.users.model'))) { - throw new RuntimeException('Unable to determine user model from configuration.'); + $provider = config('auth.guards.api.provider'); + + if (is_null($model = config('auth.providers.'.$provider.'.model'))) { + throw new RuntimeException('Unable to determine authentication model from configuration.'); } if (method_exists($model, 'findForPassport')) { @@ -42,14 +44,13 @@ public function getUserEntityByUserCredentials($username, $password, $grantType, $user = (new $model)->where('email', $username)->first(); } - - if (! $user ) { + if (! $user) { return; } elseif (method_exists($user, 'validateForPassportPasswordGrant')) { if (! $user->validateForPassportPasswordGrant($password)) { return; } - } elseif (! $this->hasher->check($password, $user->password)) { + } elseif (! $this->hasher->check($password, $user->getAuthPassword())) { return; } diff --git a/src/Client.php b/src/Client.php index 3d87afc..edc7661 100644 --- a/src/Client.php +++ b/src/Client.php @@ -43,21 +43,21 @@ class Client extends Model /** * Get all of the authentication codes for the client. * - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * @return \Jenssegers\Mongodb\Relations\HasMany */ public function authCodes() { - return $this->hasMany(AuthCode::class); + return $this->hasMany(AuthCode::class, 'client_id'); } /** * Get all of the tokens that belong to the client. * - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * @return \Jenssegers\Mongodb\Relations\HasMany */ public function tokens() { - return $this->hasMany(Token::class); + return $this->hasMany(Token::class, 'client_id'); } /** diff --git a/src/ClientRepository.php b/src/ClientRepository.php index e561e2e..d5b54be 100644 --- a/src/ClientRepository.php +++ b/src/ClientRepository.php @@ -8,7 +8,7 @@ class ClientRepository * Get a client by the given ID. * * @param int $id - * @return Client|null + * @return \MoeenBasra\LaravelPassportMongoDB\Client|null */ public function find($id) { @@ -19,7 +19,7 @@ public function find($id) * Get an active client by the given ID. * * @param int $id - * @return Client|null + * @return \MoeenBasra\LaravelPassportMongoDB\Client|null */ public function findActive($id) { @@ -28,11 +28,25 @@ public function findActive($id) return $client && ! $client->revoked ? $client : null; } + /** + * Get a client instance for the given ID and user ID. + * + * @param int $clientId + * @param mixed $userId + * @return \MoeenBasra\LaravelPassportMongoDB\Client|null + */ + public function findForUser($clientId, $userId) + { + return Client::where('_id', $clientId) + ->where('user_id', $userId) + ->first(); + } + /** * Get the client instances for the given user ID. * * @param mixed $userId - * @return \Illuminate\Database\Eloquent\Collection + * @return \Jenssegers\Mongodb\Collection */ public function forUser($userId) { @@ -44,7 +58,7 @@ public function forUser($userId) * Get the active client instances for the given user ID. * * @param mixed $userId - * @return \Illuminate\Database\Eloquent\Collection + * @return \Jenssegers\Mongodb\Collection */ public function activeForUser($userId) { @@ -56,15 +70,15 @@ public function activeForUser($userId) /** * Get the personal access token client for the application. * - * @return Client + * @return \MoeenBasra\LaravelPassportMongoDB\Client */ public function personalAccessClient() { if (Passport::$personalAccessClient) { - return Client::find(Passport::$personalAccessClient); - } else { - return PersonalAccessClient::orderBy('id', 'desc')->first()->client; + return $this->find(Passport::$personalAccessClient); } + + return PersonalAccessClient::orderBy('_id', 'desc')->first()->client; } /** @@ -75,7 +89,7 @@ public function personalAccessClient() * @param string $redirect * @param bool $personalAccess * @param bool $password - * @return Client + * @return \MoeenBasra\LaravelPassportMongoDB\Client */ public function create($userId, $name, $redirect, $personalAccess = false, $password = false) { @@ -100,7 +114,7 @@ public function create($userId, $name, $redirect, $personalAccess = false, $pass * @param int $userId * @param string $name * @param string $redirect - * @return Client + * @return \MoeenBasra\LaravelPassportMongoDB\Client */ public function createPersonalAccessClient($userId, $name, $redirect) { @@ -113,7 +127,7 @@ public function createPersonalAccessClient($userId, $name, $redirect) * @param int $userId * @param string $name * @param string $redirect - * @return Client + * @return \MoeenBasra\LaravelPassportMongoDB\Client */ public function createPasswordGrantClient($userId, $name, $redirect) { @@ -126,7 +140,7 @@ public function createPasswordGrantClient($userId, $name, $redirect) * @param Client $client * @param string $name * @param string $redirect - * @return Client + * @return \MoeenBasra\LaravelPassportMongoDB\Client */ public function update(Client $client, $name, $redirect) { @@ -140,8 +154,8 @@ public function update(Client $client, $name, $redirect) /** * Regenerate the client secret. * - * @param Client $client - * @return Client + * @param \MoeenBasra\LaravelPassportMongoDB\Client $client + * @return \MoeenBasra\LaravelPassportMongoDB\Client */ public function regenerateSecret(Client $client) { @@ -160,14 +174,15 @@ public function regenerateSecret(Client $client) */ public function revoked($id) { - return Client::where('id', $id) - ->where('revoked', true)->exists(); + $client = $this->find($id); + + return is_null($client) || $client->revoked; } /** * Delete the given client. * - * @param Client $client + * @param \MoeenBasra\LaravelPassportMongoDB\Client $client * @return void */ public function delete(Client $client) diff --git a/src/Console/ClientCommand.php b/src/Console/ClientCommand.php index 4aee33d..dda90b3 100644 --- a/src/Console/ClientCommand.php +++ b/src/Console/ClientCommand.php @@ -2,9 +2,7 @@ namespace MoeenBasra\LaravelPassportMongoDB\Console; -use DateTime; use Illuminate\Console\Command; -use Illuminate\Support\Facades\DB; use MoeenBasra\LaravelPassportMongoDB\ClientRepository; use MoeenBasra\LaravelPassportMongoDB\PersonalAccessClient; diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index b0cc371..1477d48 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -11,7 +11,7 @@ class InstallCommand extends Command * * @var string */ - protected $signature = 'passport:install'; + protected $signature = 'passport:install {--force : Overwrite keys if they already exist}'; /** * The console command description. @@ -27,7 +27,8 @@ class InstallCommand extends Command */ public function handle() { - $this->call('passport:keys'); + $this->call('passport:keys', ['--force' => $this->option('force')]); + $this->call('passport:client', ['--personal' => true, '--name' => config('app.name').' Personal Access Client']); $this->call('passport:client', ['--password' => true, '--name' => config('app.name').' Password Grant Client']); } diff --git a/src/Console/KeysCommand.php b/src/Console/KeysCommand.php index 781d415..09d8478 100644 --- a/src/Console/KeysCommand.php +++ b/src/Console/KeysCommand.php @@ -13,7 +13,7 @@ class KeysCommand extends Command * * @var string */ - protected $signature = 'passport:keys'; + protected $signature = 'passport:keys {--force : Overwrite keys they already exist}'; /** * The console command description. @@ -25,15 +25,24 @@ class KeysCommand extends Command /** * Execute the console command. * - * @param RSA $rsa + * @param \phpseclib\Crypt\RSA $rsa * @return mixed */ public function handle(RSA $rsa) { $keys = $rsa->createKey(4096); - file_put_contents(Passport::keyPath('oauth-private.key'), array_get($keys, 'privatekey')); - file_put_contents(Passport::keyPath('oauth-public.key'), array_get($keys, 'publickey')); + list($publicKey, $privateKey) = [ + Passport::keyPath('oauth-public.key'), + Passport::keyPath('oauth-private.key'), + ]; + + if ((file_exists($publicKey) || file_exists($privateKey)) && ! $this->option('force')) { + return $this->error('Encryption keys already exist. Use the --force option to overwrite them.'); + } + + file_put_contents($publicKey, array_get($keys, 'publickey')); + file_put_contents($privateKey, array_get($keys, 'privatekey')); $this->info('Encryption keys generated successfully.'); } diff --git a/src/Events/RefreshTokenCreated.php b/src/Events/RefreshTokenCreated.php index d52d84f..dc2e54d 100644 --- a/src/Events/RefreshTokenCreated.php +++ b/src/Events/RefreshTokenCreated.php @@ -21,9 +21,8 @@ class RefreshTokenCreated /** * Create a new event instance. * - * @param string $tokenId - * @param string $userId - * @param string $clientId + * @param string $refreshTokenId + * @param string $accessTokenId * @return void */ public function __construct($refreshTokenId, $accessTokenId) diff --git a/src/Guards/TokenGuard.php b/src/Guards/TokenGuard.php index cd071e2..21a6dea 100644 --- a/src/Guards/TokenGuard.php +++ b/src/Guards/TokenGuard.php @@ -4,7 +4,6 @@ use Exception; use Firebase\JWT\JWT; -use MoeenBasra\LaravelPassportMongoDB\Token; use Illuminate\Http\Request; use MoeenBasra\LaravelPassportMongoDB\Passport; use Illuminate\Container\Container; @@ -23,46 +22,46 @@ class TokenGuard /** * The resource server instance. * - * @var ResourceServer + * @var \League\OAuth2\Server\ResourceServer */ protected $server; /** * The user provider implementation. * - * @var UserProvider + * @var \Illuminate\Contracts\Auth\UserProvider */ protected $provider; /** * The token repository instance. * - * @var TokenRepository + * @var \MoeenBasra\LaravelPassportMongoDB\TokenRepository */ protected $tokens; /** * The client repository instance. * - * @var ClientRepository + * @var \MoeenBasra\LaravelPassportMongoDB\ClientRepository */ protected $clients; /** * The encrypter implementation. * - * @var Encrypter + * @var \Illuminate\Contracts\Encryption\Encrypter */ protected $encrypter; /** * Create a new token guard instance. * - * @param ResourceServer $server - * @param UserProvider $provider - * @param TokenRepository $tokens - * @param ClientRepository $clients - * @param Encrypter $encrypter + * @param \League\OAuth2\Server\ResourceServer $server + * @param \Illuminate\Contracts\Auth\UserProvider $provider + * @param \MoeenBasra\LaravelPassportMongoDB\TokenRepository $tokens + * @param \MoeenBasra\LaravelPassportMongoDB\ClientRepository $clients + * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter * @return void */ public function __construct(ResourceServer $server, @@ -81,6 +80,7 @@ public function __construct(ResourceServer $server, /** * Get the user for the incoming request. * + * @param \Illuminate\Http\Request $request * @param Request $request * @return mixed */ @@ -96,7 +96,7 @@ public function user(Request $request) /** * Authenticate the incoming request via the Bearer token. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return mixed */ protected function authenticateViaBearerToken($request) @@ -112,11 +112,11 @@ protected function authenticateViaBearerToken($request) // If the access token is valid we will retrieve the user according to the user ID // associated with the token. We will use the provider implementation which may // be used to retrieve users from Eloquent. Next, we'll be ready to continue. - if (! $userId = $psr->getAttribute('oauth_user_id')) { - return; - } + $user = $this->provider->retrieveById( + $psr->getAttribute('oauth_user_id') + ); - if (! $user = $this->provider->retrieveById($userId)) { + if (! $user) { return; } @@ -147,7 +147,7 @@ protected function authenticateViaBearerToken($request) /** * Authenticate the incoming request via the token cookie. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return mixed */ protected function authenticateViaCookie($request) @@ -180,7 +180,7 @@ protected function authenticateViaCookie($request) /** * Decode and decrypt the JWT token cookie. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @return array */ protected function decodeJwtTokenCookie($request) @@ -195,7 +195,7 @@ protected function decodeJwtTokenCookie($request) * Determine if the CSRF / header are valid and match. * * @param array $token - * @param Request $request + * @param \Illuminate\Http\Request $request * @return bool */ protected function validCsrf($token, $request) diff --git a/src/HasApiTokens.php b/src/HasApiTokens.php index 6cd003c..3d4ea20 100644 --- a/src/HasApiTokens.php +++ b/src/HasApiTokens.php @@ -16,7 +16,7 @@ trait HasApiTokens /** * Get all of the user's registered OAuth clients. * - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * @return \Jenssegers\Mongodb\Relations\\HasMany */ public function clients() { @@ -26,7 +26,7 @@ public function clients() /** * Get all of the access tokens for the user. * - * @return \Illuminate\Database\Eloquent\Collection + * @return \Jenssegers\Mongodb\Collection */ public function tokens() { diff --git a/src/Http/Controllers/AccessTokenController.php b/src/Http/Controllers/AccessTokenController.php index baefcbc..c8479fe 100644 --- a/src/Http/Controllers/AccessTokenController.php +++ b/src/Http/Controllers/AccessTokenController.php @@ -2,11 +2,10 @@ namespace MoeenBasra\LaravelPassportMongoDB\Http\Controllers; -use MoeenBasra\LaravelPassportMongoDB\Passport; use MoeenBasra\LaravelPassportMongoDB\TokenRepository; use Lcobucci\JWT\Parser as JwtParser; -use Zend\Diactoros\Response as Psr7Response; use Psr\Http\Message\ServerRequestInterface; +use Zend\Diactoros\Response as Psr7Response; use League\OAuth2\Server\AuthorizationServer; class AccessTokenController @@ -16,30 +15,30 @@ class AccessTokenController /** * The authorization server. * - * @var AuthorizationServer + * @var \League\OAuth2\Server\AuthorizationServer */ protected $server; /** * The token repository instance. * - * @var TokenRepository + * @var \MoeenBasra\LaravelPassportMongoDB\TokenRepository */ protected $tokens; /** * The JWT parser instance. * - * @var JwtParser + * @var \Lcobucci\JWT\Parser */ protected $jwt; /** * Create a new controller instance. * - * @param AuthorizationServer $server - * @param TokenRepository $tokens - * @param JwtParser $jwt + * @param \League\OAuth2\Server\AuthorizationServer $server + * @param \MoeenBasra\LaravelPassportMongoDB\TokenRepository $tokens + * @param \Lcobucci\JWT\Parser $jwt * @return void */ public function __construct(AuthorizationServer $server, @@ -54,13 +53,15 @@ public function __construct(AuthorizationServer $server, /** * Authorize a client to access the user's account. * - * @param ServerRequestInterface $request - * @return Response + * @param \Psr\Http\Message\ServerRequestInterface $request + * @return \Illuminate\Http\Response */ public function issueToken(ServerRequestInterface $request) { return $this->withErrorHandling(function () use ($request) { - return $this->server->respondToAccessTokenRequest($request, new Psr7Response); + return $this->convertResponse( + $this->server->respondToAccessTokenRequest($request, new Psr7Response) + ); }); } } diff --git a/src/Http/Controllers/ApproveAuthorizationController.php b/src/Http/Controllers/ApproveAuthorizationController.php index 2fb0d43..ccce5dc 100644 --- a/src/Http/Controllers/ApproveAuthorizationController.php +++ b/src/Http/Controllers/ApproveAuthorizationController.php @@ -13,14 +13,14 @@ class ApproveAuthorizationController /** * The authorization server. * - * @var AuthorizationServer + * @var \League\OAuth2\Server\AuthorizationServer */ protected $server; /** * Create a new controller instance. * - * @param AuthorizationServer $server + * @param \League\OAuth2\Server\AuthorizationServer $server * @return void */ public function __construct(AuthorizationServer $server) @@ -31,16 +31,16 @@ public function __construct(AuthorizationServer $server) /** * Approve the authorization request. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function approve(Request $request) { return $this->withErrorHandling(function () use ($request) { $authRequest = $this->getAuthRequestFromSession($request); - return $this->server->completeAuthorizationRequest( - $authRequest, new Psr7Response + return $this->convertResponse( + $this->server->completeAuthorizationRequest($authRequest, new Psr7Response) ); }); } diff --git a/src/Http/Controllers/AuthorizationController.php b/src/Http/Controllers/AuthorizationController.php index efc3433..af4c194 100644 --- a/src/Http/Controllers/AuthorizationController.php +++ b/src/Http/Controllers/AuthorizationController.php @@ -21,22 +21,22 @@ class AuthorizationController /** * The authorization server. * - * @var AuthorizationServer + * @var \League\OAuth2\Server\AuthorizationServer */ protected $server; /** * The response factory implementation. * - * @var ResponseFactory + * @var \Illuminate\Contracts\Routing\ResponseFactory */ protected $response; /** * Create a new controller instance. * - * @param AuthorizationServer $server - * @param ResponseFactory $response + * @param \League\OAuth2\Server\AuthorizationServer $server + * @param \Illuminate\Contracts\Routing\ResponseFactory $response * @return void */ public function __construct(AuthorizationServer $server, ResponseFactory $response) @@ -48,10 +48,11 @@ public function __construct(AuthorizationServer $server, ResponseFactory $respon /** * Authorize a client to access the user's account. * - * @param ServerRequestInterface $psrRequest - * @param Request $request - * @param ClientRepository $clients - * @return Response + * @param \Psr\Http\Message\ServerRequestInterface $psrRequest + * @param \Illuminate\Http\Request $request + * @param \MoeenBasra\LaravelPassportMongoDB\ClientRepository $clients + * @param \MoeenBasra\LaravelPassportMongoDB\TokenRepository $tokens + * @return \Illuminate\Http\Response */ public function authorize(ServerRequestInterface $psrRequest, Request $request, @@ -86,7 +87,7 @@ public function authorize(ServerRequestInterface $psrRequest, /** * Transform the authorization requests's scopes into Scope instances. * - * @param AuthRequest $request + * @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest * @return array */ protected function parseScopes($authRequest) @@ -101,18 +102,18 @@ protected function parseScopes($authRequest) /** * Approve the authorization request. * - * @param AuthorizationRequest $authRequest - * @param Model $user - * @return \Psr\Http\Message\ResponseInterface + * @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest + * @param \Jenssegers\Mongodb\Eloquent\Model $user + * @return \Illuminate\Http\Response */ - private function approveRequest($authRequest, $user) + protected function approveRequest($authRequest, $user) { $authRequest->setUser(new User($user->getKey())); $authRequest->setAuthorizationApproved(true); - return $this->server->completeAuthorizationRequest( - $authRequest, new Psr7Response + return $this->convertResponse( + $this->server->completeAuthorizationRequest($authRequest, new Psr7Response) ); } } diff --git a/src/Http/Controllers/AuthorizedAccessTokenController.php b/src/Http/Controllers/AuthorizedAccessTokenController.php index 6b6e0bb..55bd932 100644 --- a/src/Http/Controllers/AuthorizedAccessTokenController.php +++ b/src/Http/Controllers/AuthorizedAccessTokenController.php @@ -4,18 +4,40 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; +use MoeenBasra\LaravelPassportMongoDB\TokenRepository; class AuthorizedAccessTokenController { + /** + * The token repository implementation. + * + * @var \MoeenBasra\LaravelPassportMongoDB\TokenRepository + */ + protected $tokenRepository; + + /** + * Create a new controller instance. + * + * @param \MoeenBasra\LaravelPassportMongoDB\TokenRepository $tokenRepository + * @return void + */ + public function __construct(TokenRepository $tokenRepository) + { + $this->tokenRepository = $tokenRepository; + } + /** * Get all of the authorized tokens for the authenticated user. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function forUser(Request $request) { - return $request->user()->tokens->load('client')->filter(function ($token) { + $tokens = $this->tokenRepository->forUser($request->user()->getKey()); + + return $tokens->load('client')->filter(function ($token) { return ! $token->client->firstParty() && ! $token->revoked; })->values(); } @@ -23,13 +45,17 @@ public function forUser(Request $request) /** * Delete the given token. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @param string $tokenId - * @return Response + * @return \Illuminate\Http\Response */ public function destroy(Request $request, $tokenId) { - if (is_null($token = $request->user()->tokens->find($tokenId))) { + $token = $this->tokenRepository->findForUser( + $tokenId, $request->user()->getKey() + ); + + if (is_null($token)) { return new Response('', 404); } diff --git a/src/Http/Controllers/ClientController.php b/src/Http/Controllers/ClientController.php index 7da3e95..464c316 100644 --- a/src/Http/Controllers/ClientController.php +++ b/src/Http/Controllers/ClientController.php @@ -4,9 +4,7 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; -use MoeenBasra\LaravelPassportMongoDB\Client; use MoeenBasra\LaravelPassportMongoDB\ClientRepository; -use Jenssegers\Mongodb\Eloquent\Model; use Illuminate\Contracts\Validation\Factory as ValidationFactory; class ClientController @@ -14,22 +12,22 @@ class ClientController /** * The client repository instance. * - * @var ClientRepository + * @var \MoeenBasra\LaravelPassportMongoDB\ClientRepository */ protected $clients; /** * The validation factory implementation. * - * @var ValidationFactory + * @var \Illuminate\Contracts\Validation\Factory */ protected $validation; /** * Create a client controller instance. * - * @param ClientRepository $clients - * @param ValidationFactory $validation + * @param \MoeenBasra\LaravelPassportMongoDB\ClientRepository $clients + * @param \Illuminate\Contracts\Validation\Factory $validation * @return void */ public function __construct(ClientRepository $clients, @@ -42,8 +40,8 @@ public function __construct(ClientRepository $clients, /** * Get all of the clients for the authenticated user. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function forUser(Request $request) { @@ -55,8 +53,8 @@ public function forUser(Request $request) /** * Store a new client. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function store(Request $request) { @@ -73,13 +71,15 @@ public function store(Request $request) /** * Update the given client. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @param string $clientId - * @return Response + * @return \Illuminate\Http\Response|\MoeenBasra\LaravelPassportMongoDB\Client */ public function update(Request $request, $clientId) { - if (! $request->user()->clients->find($clientId)) { + $client = $this->clients->findForUser($clientId, $request->user()->getKey()); + + if (! $client) { return new Response('', 404); } @@ -89,8 +89,7 @@ public function update(Request $request, $clientId) ])->validate(); return $this->clients->update( - $request->user()->clients->find($clientId), - $request->name, $request->redirect + $client, $request->name, $request->redirect ); } @@ -103,12 +102,14 @@ public function update(Request $request, $clientId) */ public function destroy(Request $request, $clientId) { - if (! $request->user()->clients->find($clientId)) { + $client = $this->clients->findForUser($clientId, $request->user()->getKey()); + + if (! $client) { return new Response('', 404); } $this->clients->delete( - $request->user()->clients->find($clientId) + $client ); } } diff --git a/src/Http/Controllers/ConvertsPsrResponses.php b/src/Http/Controllers/ConvertsPsrResponses.php new file mode 100644 index 0000000..7547e45 --- /dev/null +++ b/src/Http/Controllers/ConvertsPsrResponses.php @@ -0,0 +1,23 @@ +getBody(), + $psrResponse->getStatusCode(), + $psrResponse->getHeaders() + ); + } +} diff --git a/src/Http/Controllers/DenyAuthorizationController.php b/src/Http/Controllers/DenyAuthorizationController.php index de809e3..d82957a 100644 --- a/src/Http/Controllers/DenyAuthorizationController.php +++ b/src/Http/Controllers/DenyAuthorizationController.php @@ -2,6 +2,7 @@ namespace MoeenBasra\LaravelPassportMongoDB\Http\Controllers; +use Illuminate\Support\Arr; use Illuminate\Http\Request; use Illuminate\Contracts\Routing\ResponseFactory; @@ -12,14 +13,14 @@ class DenyAuthorizationController /** * The response factory implementation. * - * @var ResponseFactory + * @var \Illuminate\Contracts\Routing\ResponseFactory */ protected $response; /** * Create a new controller instance. * - * @param ResponseFactory $response + * @param \Illuminate\Contracts\Routing\ResponseFactory $response * @return void */ public function __construct(ResponseFactory $response) @@ -30,16 +31,21 @@ public function __construct(ResponseFactory $response) /** * Deny the authorization request. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\RedirectResponse */ public function deny(Request $request) { - $redirect = $this->getAuthRequestFromSession($request) - ->getClient()->getRedirectUri(); + $authRequest = $this->getAuthRequestFromSession($request); + + if (is_array($uri = $authRequest->getClient()->getRedirectUri())) { + $uri = Arr::first($uri); + } + + $separator = $authRequest->getGrantTypeId() === 'implicit' ? '#' : '?'; return $this->response->redirectTo( - $redirect.'?error=access_denied&state='.$request->input('state') + $uri.$separator.'error=access_denied&state='.$request->input('state') ); } } diff --git a/src/Http/Controllers/HandlesOAuthErrors.php b/src/Http/Controllers/HandlesOAuthErrors.php index 16142c2..7671f10 100644 --- a/src/Http/Controllers/HandlesOAuthErrors.php +++ b/src/Http/Controllers/HandlesOAuthErrors.php @@ -2,8 +2,8 @@ namespace MoeenBasra\LaravelPassportMongoDB\Http\Controllers; -use Throwable; use Exception; +use Throwable; use Illuminate\Http\Response; use Illuminate\Container\Container; use Zend\Diactoros\Response as Psr7Response; @@ -13,11 +13,13 @@ trait HandlesOAuthErrors { + use ConvertsPsrResponses; + /** * Perform the given callback with exception handling. * * @param \Closure $callback - * @return Response + * @return \Illuminate\Http\Response */ protected function withErrorHandling($callback) { @@ -26,7 +28,9 @@ protected function withErrorHandling($callback) } catch (OAuthServerException $e) { $this->exceptionHandler()->report($e); - return $e->generateHttpResponse(new Psr7Response); + return $this->convertResponse( + $e->generateHttpResponse(new Psr7Response) + ); } catch (Exception $e) { $this->exceptionHandler()->report($e); diff --git a/src/Http/Controllers/PersonalAccessTokenController.php b/src/Http/Controllers/PersonalAccessTokenController.php index 785f36a..fabdd43 100644 --- a/src/Http/Controllers/PersonalAccessTokenController.php +++ b/src/Http/Controllers/PersonalAccessTokenController.php @@ -5,38 +5,49 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; use MoeenBasra\LaravelPassportMongoDB\Passport; -use MoeenBasra\LaravelPassportMongoDB\PersonalAccessTokenResult; +use MoeenBasra\LaravelPassportMongoDB\TokenRepository; use Illuminate\Contracts\Validation\Factory as ValidationFactory; class PersonalAccessTokenController { + /** + * The token repository implementation. + * + * @var \MoeenBasra\LaravelPassportMongoDB\TokenRepository + */ + protected $tokenRepository; + /** * The validation factory implementation. * - * @var ValidationFactory + * @var \Illuminate\Contracts\Validation\Factory */ protected $validation; /** * Create a controller instance. * - * @param ValidationFactory $validation + * @param \MoeenBasra\LaravelPassportMongoDB\TokenRepository $tokenRepository + * @param \Illuminate\Contracts\Validation\Factory $validation * @return void */ - public function __construct(ValidationFactory $validation) + public function __construct(TokenRepository $tokenRepository, ValidationFactory $validation) { $this->validation = $validation; + $this->tokenRepository = $tokenRepository; } /** * Get all of the personal access tokens for the authenticated user. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function forUser(Request $request) { - return $request->user()->tokens->load('client')->filter(function ($token) { + $tokens = $this->tokenRepository->forUser($request->user()->getKey()); + + return $tokens->load('client')->filter(function ($token) { return $token->client->personal_access_client && ! $token->revoked; })->values(); } @@ -44,8 +55,8 @@ public function forUser(Request $request) /** * Create a new personal access token for the user. * - * @param Request $request - * @return PersonalAccessTokenResult + * @param \Illuminate\Http\Request $request + * @return \MoeenBasra\LaravelPassportMongoDB\PersonalAccessTokenResult */ public function store(Request $request) { @@ -64,11 +75,15 @@ public function store(Request $request) * * @param Request $request * @param string $tokenId - * @return Response + * @return \Illuminate\Http\Response */ public function destroy(Request $request, $tokenId) { - if (is_null($token = $request->user()->tokens->find($tokenId))) { + $token = $this->tokenRepository->findForUser( + $tokenId, $request->user()->getKey() + ); + + if (is_null($token)) { return new Response('', 404); } diff --git a/src/Http/Controllers/RetrievesAuthRequestFromSession.php b/src/Http/Controllers/RetrievesAuthRequestFromSession.php index 691c87e..b45ad6b 100644 --- a/src/Http/Controllers/RetrievesAuthRequestFromSession.php +++ b/src/Http/Controllers/RetrievesAuthRequestFromSession.php @@ -3,7 +3,6 @@ namespace MoeenBasra\LaravelPassportMongoDB\Http\Controllers; use Exception; -use Jenssegers\Mongodb\Eloquent\Model; use Illuminate\Http\Request; use MoeenBasra\LaravelPassportMongoDB\Bridge\User; @@ -12,8 +11,9 @@ trait RetrievesAuthRequestFromSession /** * Get the authorization request from the session. * - * @param Request $request - * @return AuthorizationRequest + * @param \Illuminate\Http\Request $request + * @return \League\OAuth2\Server\RequestTypes\AuthorizationRequest + * @throws \Exception */ protected function getAuthRequestFromSession(Request $request) { diff --git a/src/Http/Controllers/ScopeController.php b/src/Http/Controllers/ScopeController.php index 232c269..4fb0384 100644 --- a/src/Http/Controllers/ScopeController.php +++ b/src/Http/Controllers/ScopeController.php @@ -9,7 +9,7 @@ class ScopeController /** * Get all of the available scopes for the application. * - * @return Response + * @return \Illuminate\Support\Collection */ public function all() { diff --git a/src/Http/Controllers/TransientTokenController.php b/src/Http/Controllers/TransientTokenController.php index 192f1d0..aaf3c01 100644 --- a/src/Http/Controllers/TransientTokenController.php +++ b/src/Http/Controllers/TransientTokenController.php @@ -11,14 +11,14 @@ class TransientTokenController /** * The cookie factory instance. * - * @var ApiTokenCookieFactory + * @var \MoeenBasra\LaravelPassportMongoDB\ApiTokenCookieFactory */ protected $cookieFactory; /** * Create a new controller instance. * - * @param ApiTokenCookieFactory $cookieFactory + * @param \MoeenBasra\LaravelPassportMongoDB\ApiTokenCookieFactory $cookieFactory * @return void */ public function __construct(ApiTokenCookieFactory $cookieFactory) @@ -29,8 +29,8 @@ public function __construct(ApiTokenCookieFactory $cookieFactory) /** * Get a fresh transient token cookie for the authenticated user. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function refresh(Request $request) { diff --git a/src/Http/Middleware/CheckClientCredentials.php b/src/Http/Middleware/CheckClientCredentials.php index 081d4a0..61baf24 100644 --- a/src/Http/Middleware/CheckClientCredentials.php +++ b/src/Http/Middleware/CheckClientCredentials.php @@ -5,6 +5,7 @@ use Closure; use League\OAuth2\Server\ResourceServer; use Illuminate\Auth\AuthenticationException; +use MoeenBasra\LaravelPassportMongoDB\Exceptions\MissingScopeException; use League\OAuth2\Server\Exception\OAuthServerException; use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory; @@ -13,14 +14,14 @@ class CheckClientCredentials /** * The Resource Server instance. * - * @var ResourceServer + * @var \League\OAuth2\Server\ResourceServer */ private $server; /** * Create a new middleware instance. * - * @param ResourceServer $server + * @param \League\OAuth2\Server\ResourceServer $server * @return void */ public function __construct(ResourceServer $server) @@ -31,28 +32,45 @@ public function __construct(ResourceServer $server) /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @param mixed ...$scopes * @return mixed - * * @throws \Illuminate\Auth\AuthenticationException */ public function handle($request, Closure $next, ...$scopes) { $psr = (new DiactorosFactory)->createRequest($request); - try{ + try { $psr = $this->server->validateAuthenticatedRequest($psr); } catch (OAuthServerException $e) { throw new AuthenticationException; } - foreach ($scopes as $scope) { - if (!in_array($scope,$psr->getAttribute('oauth_scopes'))) { - throw new AuthenticationException; - } - } + $this->validateScopes($psr, $scopes); return $next($request); } + + /** + * Validate the scopes on the incoming request. + * + * @param \Psr\Http\Message\ResponseInterface $psr + * @param array $scopes + * @return void + * @throws \MoeenBasra\LaravelPassportMongoDB\Exceptions\MissingScopeException + */ + protected function validateScopes($psr, $scopes) + { + if (in_array('*', $tokenScopes = $psr->getAttribute('oauth_scopes'))) { + return; + } + + foreach ($scopes as $scope) { + if (! in_array($scope, $tokenScopes)) { + throw new MissingScopeException($scope); + } + } + } } diff --git a/src/Http/Middleware/CheckForAnyScope.php b/src/Http/Middleware/CheckForAnyScope.php index 9195546..84b40f7 100644 --- a/src/Http/Middleware/CheckForAnyScope.php +++ b/src/Http/Middleware/CheckForAnyScope.php @@ -12,8 +12,9 @@ class CheckForAnyScope * * @param \Illuminate\Http\Request $request * @param \Closure $next - * @param array $scopes + * @param mixed ...$scopes * @return \Illuminate\Http\Response + * @throws \Illuminate\Auth\AuthenticationException|\Laravel\Passport\Exceptions\MissingScopeException */ public function handle($request, $next, ...$scopes) { diff --git a/src/Http/Middleware/CheckScopes.php b/src/Http/Middleware/CheckScopes.php index c984cde..8a25c72 100644 --- a/src/Http/Middleware/CheckScopes.php +++ b/src/Http/Middleware/CheckScopes.php @@ -12,8 +12,10 @@ class CheckScopes * * @param \Illuminate\Http\Request $request * @param \Closure $next - * @param array $scopes + * @param mixed ...$scopes * @return \Illuminate\Http\Response + * @throws \Illuminate\Auth\AuthenticationException + * @throws \MoeenBasra\LaravelPassportMongoDB\Exceptions\MissingScopeException */ public function handle($request, $next, ...$scopes) { diff --git a/src/Http/Middleware/CreateFreshApiToken.php b/src/Http/Middleware/CreateFreshApiToken.php index 9c52caa..78fc67e 100644 --- a/src/Http/Middleware/CreateFreshApiToken.php +++ b/src/Http/Middleware/CreateFreshApiToken.php @@ -12,7 +12,7 @@ class CreateFreshApiToken /** * The API token cookie factory instance. * - * @var ApiTokenCookieFactory + * @var \MoeenBasra\LaravelPassportMongoDB\ApiTokenCookieFactory */ protected $cookieFactory; @@ -26,7 +26,7 @@ class CreateFreshApiToken /** * Create a new middleware instance. * - * @param ApiTokenCookieFactory $cookieFactory + * @param \MoeenBasra\LaravelPassportMongoDB\ApiTokenCookieFactory $cookieFactory * @return void */ public function __construct(ApiTokenCookieFactory $cookieFactory) @@ -60,8 +60,8 @@ public function handle($request, Closure $next, $guard = null) /** * Determine if the given request should receive a fresh token. * - * @param \Illuminate\Http\Request $request - * @param \Illuminate\Http\Response $response + * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Response $response * @return bool */ protected function shouldReceiveFreshToken($request, $response) @@ -84,13 +84,12 @@ protected function requestShouldReceiveFreshToken($request) /** * Determine if the response should receive a fresh token. * - * @param \Illuminate\Http\Response $request + * @param \Illuminate\Http\Response $response * @return bool */ protected function responseShouldReceiveFreshToken($response) { - return $response instanceof Response && - ! $this->alreadyContainsToken($response); + return $response instanceof Response && ! $this->alreadyContainsToken($response); } /** diff --git a/src/Passport.php b/src/Passport.php index 6c99e26..b36b9a1 100644 --- a/src/Passport.php +++ b/src/Passport.php @@ -2,6 +2,7 @@ namespace MoeenBasra\LaravelPassportMongoDB; +use Mockery; use DateInterval; use Carbon\Carbon; use DateTimeInterface; @@ -12,7 +13,7 @@ class Passport /** * Indicates if the implicit grant type is enabled. * - * @var boolean|null + * @var bool|null */ public static $implicitGrantEnabled = false; @@ -94,10 +95,11 @@ public static function enableImplicitGrant() } /** - * Get a Passport route registrar. + * Binds the Passport routes into the controller. * + * @param callable|null $callback * @param array $options - * @return RouteRegistrar + * @return void */ public static function routes($callback = null, array $options = []) { @@ -105,9 +107,12 @@ public static function routes($callback = null, array $options = []) $router->all(); }; - $options = array_merge($options, [ + $defaultOptions = [ + 'prefix' => 'oauth', 'namespace' => '\MoeenBasra\LaravelPassportMongoDB\Http\Controllers', - ]); + ]; + + $options = array_merge($defaultOptions, $options); Route::group($options, function ($router) use ($callback) { $callback(new RouteRegistrar($router)); @@ -224,10 +229,10 @@ public static function tokensExpireIn(DateTimeInterface $date = null) return static::$tokensExpireAt ? Carbon::now()->diff(static::$tokensExpireAt) : new DateInterval('P1Y'); - } else { - static::$tokensExpireAt = $date; } + static::$tokensExpireAt = $date; + return new static; } @@ -243,10 +248,10 @@ public static function refreshTokensExpireIn(DateTimeInterface $date = null) return static::$refreshTokensExpireAt ? Carbon::now()->diff(static::$refreshTokensExpireAt) : new DateInterval('P1Y'); - } else { - static::$refreshTokensExpireAt = $date; } + static::$refreshTokensExpireAt = $date; + return new static; } @@ -260,13 +265,36 @@ public static function cookie($cookie = null) { if (is_null($cookie)) { return static::$cookie; - } else { - static::$cookie = $cookie; } + static::$cookie = $cookie; + return new static; } + /** + * Set the current user for the application with the given scopes. + * + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param array $scopes + * @param string $guard + * @return void + */ + public static function actingAs($user, $scopes = [], $guard = 'api') + { + $token = Mockery::mock(Token::class)->shouldIgnoreMissing(false); + + foreach ($scopes as $scope) { + $token->shouldReceive('can')->with($scope)->andReturn(true); + } + + $user->withAccessToken($token); + + app('auth')->guard($guard)->setUser($user); + + app('auth')->shouldUse($guard); + } + /** * Set the storage location of the encryption keys. * @@ -286,10 +314,10 @@ public static function loadKeysFrom($path) */ public static function keyPath($file) { - $file = ltrim($file, "/\\"); + $file = ltrim($file, '/\\'); return static::$keyPath - ? rtrim(static::$keyPath, "/\\").DIRECTORY_SEPARATOR.$file + ? rtrim(static::$keyPath, '/\\').DIRECTORY_SEPARATOR.$file : storage_path($file); } diff --git a/src/PassportServiceProvider.php b/src/PassportServiceProvider.php index 6e6da48..ff26b45 100644 --- a/src/PassportServiceProvider.php +++ b/src/PassportServiceProvider.php @@ -9,8 +9,9 @@ use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Cookie; use Illuminate\Support\Facades\Request; -use MoeenBasra\LaravelPassportMongoDB\Guards\TokenGuard; use Illuminate\Support\ServiceProvider; +use MoeenBasra\LaravelPassportMongoDB\Guards\TokenGuard; +use League\OAuth2\Server\CryptKey; use League\OAuth2\Server\ResourceServer; use League\OAuth2\Server\AuthorizationServer; use League\OAuth2\Server\Grant\AuthCodeGrant; @@ -30,7 +31,7 @@ class PassportServiceProvider extends ServiceProvider */ public function boot() { - $this->loadViewsFrom(__DIR__ . '/../resources/views', 'passport'); + $this->loadViewsFrom(__DIR__.'/../resources/views', 'passport'); $this->deleteCookieOnLogout(); @@ -38,11 +39,11 @@ public function boot() $this->registerMigrations(); $this->publishes([ - __DIR__ . '/../resources/views' => base_path('resources/views/vendor/passport'), + __DIR__.'/../resources/views' => base_path('resources/views/vendor/passport'), ], 'passport-views'); $this->publishes([ - __DIR__ . '/../resources/assets/js/components' => base_path('resources/assets/js/components/passport'), + __DIR__.'/../resources/assets/js/components' => base_path('resources/assets/js/components/passport'), ], 'passport-components'); $this->commands([ @@ -61,11 +62,11 @@ public function boot() protected function registerMigrations() { if (Passport::$runsMigrations) { - return $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); + return $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); } $this->publishes([ - __DIR__ . '/../database/migrations' => database_path('migrations'), + __DIR__.'/../database/migrations' => database_path('migrations'), ], 'passport-migrations'); } @@ -105,7 +106,7 @@ protected function registerAuthorizationServer() ); $server->enableGrantType( - new PersonalAccessGrant, new DateInterval('P10Y') + new PersonalAccessGrant, new DateInterval('P1Y') ); $server->enableGrantType( @@ -124,7 +125,7 @@ protected function registerAuthorizationServer() /** * Create and configure an instance of the Auth Code grant. * - * @return AuthCodeGrant + * @return \League\OAuth2\Server\Grant\AuthCodeGrant */ protected function makeAuthCodeGrant() { @@ -136,7 +137,7 @@ protected function makeAuthCodeGrant() /** * Build the Auth Code grant instance. * - * @return AuthCodeGrant + * @return \League\OAuth2\Server\Grant\AuthCodeGrant */ protected function buildAuthCodeGrant() { @@ -150,7 +151,7 @@ protected function buildAuthCodeGrant() /** * Create and configure a Refresh Token grant instance. * - * @return RefreshTokenGrant + * @return \League\OAuth2\Server\Grant\RefreshTokenGrant */ protected function makeRefreshTokenGrant() { @@ -164,7 +165,7 @@ protected function makeRefreshTokenGrant() /** * Create and configure a Password grant instance. * - * @return PasswordGrant + * @return \League\OAuth2\Server\Grant\PasswordGrant */ protected function makePasswordGrant() { @@ -181,7 +182,7 @@ protected function makePasswordGrant() /** * Create and configure an instance of the Implicit grant. * - * @return ImplicitGrant + * @return \League\OAuth2\Server\Grant\ImplicitGrant */ protected function makeImplicitGrant() { @@ -191,7 +192,7 @@ protected function makeImplicitGrant() /** * Make the authorization service instance. * - * @return AuthorizationServer + * @return \League\OAuth2\Server\AuthorizationServer */ public function makeAuthorizationServer() { @@ -199,7 +200,7 @@ public function makeAuthorizationServer() $this->app->make(Bridge\ClientRepository::class), $this->app->make(Bridge\AccessTokenRepository::class), $this->app->make(Bridge\ScopeRepository::class), - 'file://'.Passport::keyPath('oauth-private.key'), + $this->makeCryptKey('oauth-private.key'), app('encrypter')->getKey() ); } @@ -214,11 +215,26 @@ protected function registerResourceServer() $this->app->singleton(ResourceServer::class, function () { return new ResourceServer( $this->app->make(Bridge\AccessTokenRepository::class), - 'file://'.Passport::keyPath('oauth-public.key') + $this->makeCryptKey('oauth-public.key') ); }); } + /** + * Create a CryptKey instance without permissions check + * + * @param string $key + * @return \League\OAuth2\Server\CryptKey + */ + protected function makeCryptKey($key) + { + return new CryptKey( + 'file://'.Passport::keyPath($key), + null, + false + ); + } + /** * Register the token guard. * @@ -237,7 +253,7 @@ protected function registerGuard() * Make an instance of the token guard. * * @param array $config - * @return RequestGuard + * @return \Illuminate\Auth\RequestGuard */ protected function makeGuard(array $config) { @@ -245,7 +261,7 @@ protected function makeGuard(array $config) return (new TokenGuard( $this->app->make(ResourceServer::class), Auth::createUserProvider($config['provider']), - new TokenRepository, + $this->app->make(TokenRepository::class), $this->app->make(ClientRepository::class), $this->app->make('encrypter') ))->user($request); diff --git a/src/PersonalAccessTokenFactory.php b/src/PersonalAccessTokenFactory.php index 030ddb5..0e0e6e7 100644 --- a/src/PersonalAccessTokenFactory.php +++ b/src/PersonalAccessTokenFactory.php @@ -12,38 +12,38 @@ class PersonalAccessTokenFactory /** * The authorization server instance. * - * @var AuthorizationServer + * @var \League\OAuth2\Server\AuthorizationServer */ protected $server; /** * The client repository instance. * - * @var ClientRepository + * @var \Laravel\Passport\ClientRepository */ protected $clients; /** * The token repository instance. * - * @var TokenRepository + * @var \Laravel\Passport\TokenRepository */ protected $tokens; /** * The JWT token parser instance. * - * @var JwtParser + * @var \Lcobucci\JWT\Parser */ protected $jwt; /** * Create a new personal access token factory instance. * - * @param AuthorizationServer $server - * @param ClientRepository $clients - * @param TokenRepository $tokens - * @param JwtParser $jwt + * @param \League\OAuth2\Server\AuthorizationServer $server + * @param \Laravel\Passport\ClientRepository $clients + * @param \Laravel\Passport\TokenRepository $tokens + * @param \Lcobucci\JWT\Parser $jwt * @return void */ public function __construct(AuthorizationServer $server, @@ -63,7 +63,7 @@ public function __construct(AuthorizationServer $server, * @param mixed $userId * @param string $name * @param array $scopes - * @return PersonalAccessTokenResult + * @return \MoeenBasra\LaravelPassportMongoDB\PersonalAccessTokenResult */ public function make($userId, $name, array $scopes = []) { @@ -86,10 +86,10 @@ public function make($userId, $name, array $scopes = []) /** * Create a request instance for the given client. * - * @param Client $client + * @param \MoeenBasra\LaravelPassportMongoDB\Client $client * @param mixed $userId * @param array $scopes - * @return ServerRequest + * @return \Zend\Diactoros\ServerRequest */ protected function createRequest($client, $userId, array $scopes) { @@ -105,7 +105,7 @@ protected function createRequest($client, $userId, array $scopes) /** * Dispatch the given request to the authorization server. * - * @param ServerRequest $request + * @param \Zend\Diactoros\ServerRequest $request * @return array */ protected function dispatchRequestToAuthorizationServer(ServerRequest $request) diff --git a/src/PersonalAccessTokenResult.php b/src/PersonalAccessTokenResult.php index 36e5152..b12b903 100644 --- a/src/PersonalAccessTokenResult.php +++ b/src/PersonalAccessTokenResult.php @@ -17,7 +17,7 @@ class PersonalAccessTokenResult implements Arrayable, Jsonable /** * The token model instance. * - * @var Token + * @var \MoeenBasra\LaravelPassportMongoDB\Token */ public $token; @@ -25,7 +25,7 @@ class PersonalAccessTokenResult implements Arrayable, Jsonable * Create a new result instance. * * @param string $accessToken - * @param Token $token + * @param \MoeenBasra\LaravelPassportMongoDB\Token $token * @return void */ public function __construct($accessToken, $token) diff --git a/src/RouteRegistrar.php b/src/RouteRegistrar.php index faecbe9..4422cd5 100644 --- a/src/RouteRegistrar.php +++ b/src/RouteRegistrar.php @@ -9,14 +9,14 @@ class RouteRegistrar /** * The router implementation. * - * @var Router + * @var \Illuminate\Contracts\Routing\Registrar */ protected $router; /** * Create a new route registrar instance. * - * @param Router $router + * @param \Illuminate\Contracts\Routing\Registrar $router * @return void */ public function __construct(Router $router) @@ -46,15 +46,15 @@ public function all() public function forAuthorization() { $this->router->group(['middleware' => ['web', 'auth']], function ($router) { - $router->get('/oauth/authorize', [ + $router->get('/authorize', [ 'uses' => 'AuthorizationController@authorize', ]); - $router->post('/oauth/authorize', [ + $router->post('/authorize', [ 'uses' => 'ApproveAuthorizationController@approve', ]); - $router->delete('/oauth/authorize', [ + $router->delete('/authorize', [ 'uses' => 'DenyAuthorizationController@deny', ]); }); @@ -67,17 +67,17 @@ public function forAuthorization() */ public function forAccessTokens() { - $this->router->post('/oauth/token', [ + $this->router->post('/token', [ 'uses' => 'AccessTokenController@issueToken', - 'middleware' => 'throttle' + 'middleware' => 'throttle', ]); $this->router->group(['middleware' => ['web', 'auth']], function ($router) { - $router->get('/oauth/tokens', [ + $router->get('/tokens', [ 'uses' => 'AuthorizedAccessTokenController@forUser', ]); - $router->delete('/oauth/tokens/{token_id}', [ + $router->delete('/tokens/{token_id}', [ 'uses' => 'AuthorizedAccessTokenController@destroy', ]); }); @@ -90,7 +90,7 @@ public function forAccessTokens() */ public function forTransientTokens() { - $this->router->post('/oauth/token/refresh', [ + $this->router->post('/token/refresh', [ 'middleware' => ['web', 'auth'], 'uses' => 'TransientTokenController@refresh', ]); @@ -104,19 +104,19 @@ public function forTransientTokens() public function forClients() { $this->router->group(['middleware' => ['web', 'auth']], function ($router) { - $router->get('/oauth/clients', [ + $router->get('/clients', [ 'uses' => 'ClientController@forUser', ]); - $router->post('/oauth/clients', [ + $router->post('/clients', [ 'uses' => 'ClientController@store', ]); - $router->put('/oauth/clients/{client_id}', [ + $router->put('/clients/{client_id}', [ 'uses' => 'ClientController@update', ]); - $router->delete('/oauth/clients/{client_id}', [ + $router->delete('/clients/{client_id}', [ 'uses' => 'ClientController@destroy', ]); }); @@ -130,19 +130,19 @@ public function forClients() public function forPersonalAccessTokens() { $this->router->group(['middleware' => ['web', 'auth']], function ($router) { - $router->get('/oauth/scopes', [ + $router->get('/scopes', [ 'uses' => 'ScopeController@all', ]); - $router->get('/oauth/personal-access-tokens', [ + $router->get('/personal-access-tokens', [ 'uses' => 'PersonalAccessTokenController@forUser', ]); - $router->post('/oauth/personal-access-tokens', [ + $router->post('/personal-access-tokens', [ 'uses' => 'PersonalAccessTokenController@store', ]); - $router->delete('/oauth/personal-access-tokens/{token_id}', [ + $router->delete('/personal-access-tokens/{token_id}', [ 'uses' => 'PersonalAccessTokenController@destroy', ]); }); diff --git a/src/Token.php b/src/Token.php index e10dd28..7b1ba1f 100644 --- a/src/Token.php +++ b/src/Token.php @@ -56,7 +56,7 @@ class Token extends Model /** * Get the client that the token belongs to. * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * @return \Jenssegers\Mongodb\Eloquent\Relations\BelongsTo */ public function client() { @@ -66,11 +66,13 @@ public function client() /** * Get the user that the token belongs to. * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * @return \Jenssegers\Mongodb\Eloquent\Relations\BelongsTo */ public function user() { - return $this->belongsTo(config('auth.providers.users.model')); + $provider = config('auth.guards.api.provider'); + + return $this->belongsTo(config('auth.providers.'.$provider.'.model')); } /** diff --git a/src/TokenRepository.php b/src/TokenRepository.php index 0150242..8e975b8 100644 --- a/src/TokenRepository.php +++ b/src/TokenRepository.php @@ -7,57 +7,119 @@ class TokenRepository { + /** + * Creates a new Access Token. + * + * @param array $attributes + * @return \MoeenBasra\LaravelPassportMongoDB\Token + */ + public function create($attributes) + { + return Token::create($attributes); + } + /** * Get a token by the given ID. * * @param string $id - * @return Token + * @return \MoeenBasra\LaravelPassportMongoDB\Token */ public function find($id) { - return Token::where('id', $id)->first(); + return Token::find($id); + } + + /** + * Get a token by the given user ID and token ID. + * + * @param string $id + * @param int $userId + * @return \MoeenBasra\LaravelPassportMongoDB\Token|null + */ + public function findForUser($id, $userId) + { + return Token::where('_id', $id)->where('user_id', $userId)->first(); + } + + /** + * Get the token instances for the given user ID. + * + * @param mixed $userId + * @return \Jenssegers\Mongodb\Eloquent\Collection + */ + public function forUser($userId) + { + return Token::where('user_id', $userId)->get(); + } + + /** + * Get a valid token instance for the given user and client. + * + * @param \Jenssegers\Mongodb\Eloquent\Model $user + * @param \MoeenBasra\LaravelPassportMongoDB\Client $client + * @return \MoeenBasra\LaravelPassportMongoDB\Token|null + */ + public function getValidToken($user, $client) + { + return $client->tokens() + ->whereUserId($user->getKey()) + ->whereRevoked(0) + ->where('expires_at', '>', Carbon::now()) + ->first(); } /** * Store the given token instance. * - * @param Token $token + * @param \MoeenBasra\LaravelPassportMongoDB\Token $token * @return void */ - public function save($token) + public function save(Token $token) { $token->save(); } /** - * Find a valid token for the given user and client. + * Revoke an access token. * - * @param Model $userId - * @param Client $client - * @return Token|null + * @param string $id + * @return mixed */ - public function findValidToken($user, $client) + public function revokeAccessToken($id) { - return $client->tokens() - ->whereUserId($user->_id) - ->whereRevoked(0) - ->where('expires_at', '>', Carbon::now()) - ->latest('expires_at') - ->first(); + return Token::where('_id', $id)->update(['revoked' => true]); } /** - * Revoke all of the access tokens for a given user and client. + * Check if the access token has been revoked. * - * @deprecated since 1.0. Listen to Passport events on token creation instead. + * @param string $id * - * @param mixed $clientId - * @param mixed $userId - * @param bool $prune - * @return void + * @return bool Return true if this token has been revoked + */ + public function isAccessTokenRevoked($id) + { + if ($token = $this->find($id)) { + return $token->revoked; + } + + return true; + } + + /** + * Find a valid token for the given user and client. + * + * @param \Jenssegers\Mongodb\Eloquent\Model $user + * @param \MoeenBasra\LaravelPassportMongoDB\Client $client + * @return \MoeenBasra\LaravelPassportMongoDB\Token|null */ - public function revokeOtherAccessTokens($clientId, $userId, $except = null, $prune = false) + public function findValidToken($user, $client) { - // + return $client->tokens() + ->whereUserId($user->getKey()) + ->whereRevoked(0) + ->where('expires_at', '>', Carbon::now()) + ->latest('expires_at') + ->first(); } }