diff --git a/.gitattributes b/.gitattributes index e48cc897..2a212f23 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,7 +7,6 @@ /.travis.yml export-ignore /.scrutinizer.yml export-ignore /phpunit.xml.dist export-ignore -/phpspec.xml.dist export-ignore /CHANGELOG.md export-ignore /CONTRIBUTING.md export-ignore /README.md export-ignore diff --git a/.gitignore b/.gitignore index 6687d80d..81b92580 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ composer.lock phpunit.xml -phpspec.yml vendor diff --git a/.php_cs b/.php_cs new file mode 100644 index 00000000..a7747b02 --- /dev/null +++ b/.php_cs @@ -0,0 +1,89 @@ + + +For the full copyright and license information, please view the LICENSE +file that was distributed with this source code. +EOF; + +$fixers = [ + // PSR-0 + '-psr0', + + // PSR-1 + 'encoding', + 'short_tag', + + // Symfony + 'array_element_white_space_after_comma', + 'blankline_after_open_tag', + 'concat_without_spaces', + 'duplicate_semicolon', + 'empty_return', + 'extra_empty_lines', + 'function_typehint_space', + 'include', + 'join_function', + 'list_commas', + 'multiline_array_trailing_comma', + 'namespace_no_leading_whitespace', + 'new_with_braces', + 'no_blank_lines_after_class_opening', + 'no_empty_lines_after_phpdocs', + 'object_operator', + 'operators_spaces', + 'phpdoc_indent', + 'phpdoc_no_access', + 'phpdoc_no_package', + 'phpdoc_scalar', + 'phpdoc_separation', + 'phpdoc_short_description', + 'phpdoc_to_comment', + 'phpdoc_trim', + 'phpdoc_type_to_var', + 'phpdoc_var_without_name', + 'print_to_echo', + 'remove_leading_slash_use', + 'remove_lines_between_uses', + 'return', + 'self_accessor', + 'short_bool_cast', + 'single_array_no_trailing_comma', + 'single_blank_line_before_namespace', + 'single_quote', + 'spaces_before_semicolon', + 'spaces_cast', + 'standardize_not_equal', + 'ternary_spaces', + 'trim_array_spaces', + 'unalign_double_arrow', + 'unalign_equals', + 'unary_operators_spaces', + 'unneeded_control_parentheses', + 'unused_use', + 'whitespacy_lines', + + // Contrib + 'header_comment', + 'multiline_spaces_before_semicolon', + 'newline_after_open_tag', + 'ordered_use', + 'php_unit_construct', + 'php_unit_strict', + 'phpdoc_order', + 'short_array_syntax', + 'short_echo_tag', +]; + +Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader($header); + +$finder = Symfony\CS\Finder\DefaultFinder::create() + ->in(__DIR__); + +return Symfony\CS\Config\Config::create() + ->level(Symfony\CS\FixerInterface::PSR2_LEVEL) + ->fixers($fixers) + ->finder($finder); diff --git a/.travis.yml b/.travis.yml index e989d1df..e20e2ff8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ language: php php: - - 5.5.9 - - 5.5 - 5.6 - 7.0 - hhvm @@ -10,12 +8,11 @@ php: sudo: false install: - - travis_retry composer install --no-interaction --prefer-source + - travis_retry composer install --no-interaction script: - - if [ "$TRAVIS_PHP_VERSION" != "5.5.9" ] && [ "$TRAVIS_PHP_VERSION" != "5.5" ] && [ "$TRAVIS_PHP_VERSION" != "5.6" ]; then vendor/bin/phpunit; vendor/bin/phpspec run; fi - - if [ "$TRAVIS_PHP_VERSION" == "5.5.9" ] || [ "$TRAVIS_PHP_VERSION" == "5.5" ] || [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then vendor/bin/phpunit --coverage-clover build/logs/clover.xml; fi + - vendor/bin/phpunit --coverage-clover build/logs/clover.xml after_script: - - if [ "$TRAVIS_PHP_VERSION" == "5.5.9" ] || [ "$TRAVIS_PHP_VERSION" == "5.5" ] || [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi - - if [ "$TRAVIS_PHP_VERSION" == "5.5.9" ] || [ "$TRAVIS_PHP_VERSION" == "5.5" ] || [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml; fi + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index d6725657..6eebfe5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # CHANGELOG +## 6.0.0 (upcoming) + +- Rewrite for league/oauth2-server 5.0 +- Added Laravel 5.3 support +- Added auth guard support +- Removed PHP 5.5 support + ## 5.1.1 (released 2015-12-22) - Fix Laravel 5.2 support bug diff --git a/README.md b/README.md index ea4d2dbc..514bd10e 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ -# OAuth 2.0 Server for Laravel +# Laravel OAuth 2.0 ![oauth2-server-laravel](https://cloud.githubusercontent.com/assets/499192/9065550/751404ba-3ad2-11e5-9f92-3d4d5d4b9c54.png) [OAuth 2.0](http://tools.ietf.org/wg/oauth/draft-ietf-oauth-v2/) authorization server and resource server for the Laravel and Lumen frameworks. Standard compliant thanks to the amazing work by [The League of Extraordinary Packages](http://www.thephpleague.com) OAuth 2.0 authorization server and resource server. -[![Latest Version](http://img.shields.io/github/release/lucadegasperi/oauth2-server-laravel.svg?style=flat-square)](https://github.com/lucadegasperi/oauth2-server-laravel/releases) -[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) -[![Build Status](https://img.shields.io/travis/lucadegasperi/oauth2-server-laravel/master.svg?style=flat-square)](https://travis-ci.org/lucadegasperi/oauth2-server-laravel) -[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/lucadegasperi/oauth2-server-laravel/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/lucadegasperi/oauth2-server-laravel/code-structure) -[![Quality Score](https://img.shields.io/scrutinizer/g/lucadegasperi/oauth2-server-laravel/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/lucadegasperi/oauth2-server-laravel) -[![Total Downloads](https://img.shields.io/packagist/dt/lucadegasperi/oauth2-server-laravel.svg?style=flat-square)](https://packagist.org/packages/lucadegasperi/oauth2-server-laravel) +[![Build Status](https://img.shields.io/travis/lucadegasperi/oauth2-server-laravel/master.svg?style=flat)](https://travis-ci.org/lucadegasperi/oauth2-server-laravel) +[![StyleCI](https://styleci.io/repos/13300645/shield?style=flat)](https://styleci.io/repos/13300645) +[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/lucadegasperi/oauth2-server-laravel/master.svg?style=flat)](https://scrutinizer-ci.com/g/lucadegasperi/oauth2-server-laravel/code-structure) +[![Quality Score](https://img.shields.io/scrutinizer/g/lucadegasperi/oauth2-server-laravel/master.svg?style=flat)](https://scrutinizer-ci.com/g/lucadegasperi/oauth2-server-laravel) +[![Total Downloads](https://img.shields.io/packagist/dt/lucadegasperi/oauth2-server-laravel.svg?style=flat)](https://packagist.org/packages/lucadegasperi/oauth2-server-laravel) +[![Latest Version](http://img.shields.io/github/release/lucadegasperi/oauth2-server-laravel.svg?style=flat)](https://github.com/lucadegasperi/oauth2-server-laravel/releases) +[![License](https://img.shields.io/packagist/l/lucadegasperi/oauth2-server-laravel.svg?style=flat)](https://packagist.org/packages/lucadegasperi/oauth2-server-laravel) > **Note:** This package assumes you have a good-enough knowledge of the principles behind the [OAuth 2.0 authorization specification](http://tools.ietf.org/html/rfc6749). @@ -23,6 +24,7 @@ 5.0.x | 4.0.x |>= 5.4 5.1.x | 5.0.x |>= 5.5.9 5.2.x | 5.1.x |>= 5.5.9 + 5.3.x | 6.0.x |>= 5.6.4 ## Documentation diff --git a/composer.json b/composer.json index d28e3d29..2ad63074 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "lucadegasperi/oauth2-server-laravel", - "description": "An OAuth 2.0 bridge for Laravel and Lumen", - "keywords": ["oauth2", "oauth", "server", "api", "laravel", "lumen"], + "description": "An OAuth 2.0 bridge for Laravel", + "keywords": ["oauth2", "oauth", "server", "api", "laravel", "league", "authentication", "authorization"], "license": "MIT", "authors": [ { @@ -14,21 +14,21 @@ } ], "require": { - "php": ">=5.5.9", - "illuminate/database": "5.1.* || 5.2.*", - "illuminate/console": "5.1.* || 5.2.*", - "illuminate/contracts": "5.1.* || 5.2.*", - "illuminate/http": "5.1.* || 5.2.*", - "illuminate/support": "5.1.* || 5.2.*", - "illuminate/config": "5.1.* || 5.2.*", - "league/oauth2-server": "4.1.*" + "php": "^5.6.4 || ^7.0", + "illuminate/config": "5.2.* || 5.3.*", + "illuminate/console": "5.2.* || 5.3.*", + "illuminate/contracts": "5.2.* || 5.3.*", + "illuminate/database": "5.2.* || 5.3.*", + "illuminate/http": "5.2.* || 5.3.*", + "illuminate/support": "5.2.* || 5.3.*", + "league/oauth2-server": "^5.0", + "symfony/psr-http-message-bridge": "^0.2", + "zendframework/zend-diactoros": "^1.3" }, "require-dev": { - "orchestra/testbench": "3.1.* || 3.2.*", - "phpunit/phpunit": "^4.8 || ^5.0", - "phpspec/phpspec": "^2.2", - "mockery/mockery": "^0.9.4", - "henrikbjorn/phpspec-code-coverage": "^1.0" + "graham-campbell/testbench": "^3.2", + "mockery/mockery": "^0.9.5", + "phpunit/phpunit": "^5.4" }, "autoload": { "psr-4": { @@ -41,15 +41,11 @@ }, "classmap": [ "database" - ], - "files": [ - "tests/AbstractTestCase.php", - "tests/integration/AbstractDBTestCase.php" ] }, "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-master": "6.0-dev" } }, "minimum-stability": "dev", diff --git a/config/oauth2.php b/config/oauth2.php old mode 100755 new mode 100644 index cd11554a..cf0156a9 --- a/config/oauth2.php +++ b/config/oauth2.php @@ -1,7 +1,7 @@ * @@ -26,127 +26,64 @@ | http://git.io/vJLAv | */ - 'grant_types' => [ - + [ + 'class' => \League\OAuth2\Server\Grant\PasswordGrant::class, + 'access_token_ttl' => '', + ], ], /* |-------------------------------------------------------------------------- - | Output Token Type - |-------------------------------------------------------------------------- - | - | This will tell the authorization server the output format for the access - | token and the resource server how to parse the access token used. - | - | Default value is League\OAuth2\Server\TokenType\Bearer - | - */ - - 'token_type' => 'League\OAuth2\Server\TokenType\Bearer', - - /* - |-------------------------------------------------------------------------- - | State Parameter - |-------------------------------------------------------------------------- - | - | Whether or not the state parameter is required in the query string. - | - */ - - 'state_param' => false, - - /* - |-------------------------------------------------------------------------- - | Scope Parameter - |-------------------------------------------------------------------------- - | - | Whether or not the scope parameter is required in the query string. - | - */ - - 'scope_param' => false, - - /* - |-------------------------------------------------------------------------- - | Scope Delimiter + | Private Key Path |-------------------------------------------------------------------------- | - | Which character to use to split the scope parameter in the query string. | */ - - 'scope_delimiter' => ',', + 'private_key_path' => 'file://path_to_private_key/private.key', /* |-------------------------------------------------------------------------- - | Default Scope + | Public Key Path |-------------------------------------------------------------------------- | - | The default scope to use if not present in the query string. | */ - - 'default_scope' => null, + 'public_key_path' => 'file://path_to_private_key/public.key', /* |-------------------------------------------------------------------------- - | Access Token TTL + | Key Passphrase |-------------------------------------------------------------------------- | - | For how long the issued access token is valid (in seconds) this can be - | also set on a per grant-type basis. + | Default value is null, put your passphrase here if the key has one | */ - - 'access_token_ttl' => 3600, + 'key_passphrase' => null, /* |-------------------------------------------------------------------------- - | Limit clients to specific grants + | Output Response Type |-------------------------------------------------------------------------- | - | Whether or not to limit clients to specific grant types. This is useful - | to allow only trusted clients to access your API differently. - | - */ - - 'limit_clients_to_grants' => false, - - /* - |-------------------------------------------------------------------------- - | Limit clients to specific scopes - |-------------------------------------------------------------------------- + | This will tell the authorization server the output format for the access + | token and the resource server how to parse the access token used. | - | Whether or not to limit clients to specific scopes. This is useful to - | only allow specific clients to use some scopes. + | Default value is \League\OAuth2\Server\ResponseTypes\BearerTokenResponse | */ - - 'limit_clients_to_scopes' => false, + 'response_type' => \League\OAuth2\Server\ResponseTypes\BearerTokenResponse::class, /* |-------------------------------------------------------------------------- - | Limit scopes to specific grants + | Authorization Validator |-------------------------------------------------------------------------- | - | Whether or not to limit scopes to specific grants. This is useful to - | allow certain scopes to be used only with certain grant types. - | - */ - - 'limit_scopes_to_grants' => false, - - /* - |-------------------------------------------------------------------------- - | HTTP Header Only - |-------------------------------------------------------------------------- + | This will tell the resource server the validator to use to validate an incoming request | - | This will tell the resource server where to check for the access_token. - | By default it checks both the query string and the http headers. + | Default value is \League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator::class | */ - - 'http_headers_only' => false, + 'authorization_validator' => \League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator::class, ]; diff --git a/database/migrations/2014_04_24_110403_create_oauth_grant_scopes_table.php b/database/migrations/2014_04_24_110403_create_oauth_grant_scopes_table.php deleted file mode 100644 index 97449c9e..00000000 --- a/database/migrations/2014_04_24_110403_create_oauth_grant_scopes_table.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use Illuminate\Database\Migrations\Migration; -use Illuminate\Database\Schema\Blueprint; -use Illuminate\Support\Facades\Schema; - -/** - * This is the create oauth grant scopes table migration class. - * - * @author Luca Degasperi - */ -class CreateOauthGrantScopesTable extends Migration -{ - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - Schema::create('oauth_grant_scopes', function (Blueprint $table) { - $table->increments('id'); - $table->string('grant_id', 40); - $table->string('scope_id', 40); - - $table->timestamps(); - - $table->index('grant_id'); - $table->index('scope_id'); - - $table->foreign('grant_id') - ->references('id')->on('oauth_grants') - ->onDelete('cascade'); - - $table->foreign('scope_id') - ->references('id')->on('oauth_scopes') - ->onDelete('cascade'); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::table('oauth_grant_scopes', function (Blueprint $table) { - $table->dropForeign('oauth_grant_scopes_grant_id_foreign'); - $table->dropForeign('oauth_grant_scopes_scope_id_foreign'); - }); - Schema::drop('oauth_grant_scopes'); - } -} diff --git a/database/migrations/2014_04_24_110557_create_oauth_client_endpoints_table.php b/database/migrations/2014_04_24_110557_create_oauth_client_endpoints_table.php deleted file mode 100644 index 86650385..00000000 --- a/database/migrations/2014_04_24_110557_create_oauth_client_endpoints_table.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use Illuminate\Database\Migrations\Migration; -use Illuminate\Database\Schema\Blueprint; -use Illuminate\Support\Facades\Schema; - -/** - * This is the create oauth client endpoints table migration class. - * - * @author Luca Degasperi - */ -class CreateOauthClientEndpointsTable extends Migration -{ - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - Schema::create('oauth_client_endpoints', function (Blueprint $table) { - $table->increments('id'); - $table->string('client_id', 40); - $table->string('redirect_uri'); - - $table->timestamps(); - - $table->unique(['client_id', 'redirect_uri']); - - $table->foreign('client_id') - ->references('id')->on('oauth_clients') - ->onDelete('cascade') - ->onUpdate('cascade'); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::table('oauth_client_endpoints', function (Blueprint $table) { - $table->dropForeign('oauth_client_endpoints_client_id_foreign'); - }); - - Schema::drop('oauth_client_endpoints'); - } -} diff --git a/database/migrations/2014_04_24_110817_create_oauth_client_grants_table.php b/database/migrations/2014_04_24_110817_create_oauth_client_grants_table.php deleted file mode 100644 index 1afb2c9a..00000000 --- a/database/migrations/2014_04_24_110817_create_oauth_client_grants_table.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use Illuminate\Database\Migrations\Migration; -use Illuminate\Database\Schema\Blueprint; -use Illuminate\Support\Facades\Schema; - -/** - * This is the create oauth client grants table migration class. - * - * @author Luca Degasperi - */ -class CreateOauthClientGrantsTable extends Migration -{ - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - Schema::create('oauth_client_grants', function (Blueprint $table) { - $table->increments('id'); - $table->string('client_id', 40); - $table->string('grant_id', 40); - $table->timestamps(); - - $table->index('client_id'); - $table->index('grant_id'); - - $table->foreign('client_id') - ->references('id')->on('oauth_clients') - ->onDelete('cascade') - ->onUpdate('no action'); - - $table->foreign('grant_id') - ->references('id')->on('oauth_grants') - ->onDelete('cascade') - ->onUpdate('no action'); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::table('oauth_client_grants', function (Blueprint $table) { - $table->dropForeign('oauth_client_grants_client_id_foreign'); - $table->dropForeign('oauth_client_grants_grant_id_foreign'); - }); - Schema::drop('oauth_client_grants'); - } -} diff --git a/database/migrations/2014_04_24_111002_create_oauth_sessions_table.php b/database/migrations/2014_04_24_111002_create_oauth_sessions_table.php deleted file mode 100644 index 375c4a41..00000000 --- a/database/migrations/2014_04_24_111002_create_oauth_sessions_table.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use Illuminate\Database\Migrations\Migration; -use Illuminate\Database\Schema\Blueprint; -use Illuminate\Support\Facades\Schema; - -/** - * This is the create oauth sessions table migration class. - * - * @author Luca Degasperi - */ -class CreateOauthSessionsTable extends Migration -{ - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - Schema::create('oauth_sessions', function (Blueprint $table) { - $table->increments('id'); - $table->string('client_id', 40); - $table->enum('owner_type', ['client', 'user'])->default('user'); - $table->string('owner_id'); - $table->string('client_redirect_uri')->nullable(); - $table->timestamps(); - - $table->index(['client_id', 'owner_type', 'owner_id']); - - $table->foreign('client_id') - ->references('id')->on('oauth_clients') - ->onDelete('cascade') - ->onUpdate('cascade'); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::table('oauth_sessions', function (Blueprint $table) { - $table->dropForeign('oauth_sessions_client_id_foreign'); - }); - Schema::drop('oauth_sessions'); - } -} diff --git a/database/migrations/2014_04_24_111109_create_oauth_session_scopes_table.php b/database/migrations/2014_04_24_111109_create_oauth_session_scopes_table.php deleted file mode 100644 index 79b16146..00000000 --- a/database/migrations/2014_04_24_111109_create_oauth_session_scopes_table.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use Illuminate\Database\Migrations\Migration; -use Illuminate\Database\Schema\Blueprint; -use Illuminate\Support\Facades\Schema; - -/** - * This is the create oauth session scopes table migration class. - * - * @author Luca Degasperi - */ -class CreateOauthSessionScopesTable extends Migration -{ - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - Schema::create('oauth_session_scopes', function (Blueprint $table) { - $table->increments('id'); - $table->integer('session_id')->unsigned(); - $table->string('scope_id', 40); - - $table->timestamps(); - - $table->index('session_id'); - $table->index('scope_id'); - - $table->foreign('session_id') - ->references('id')->on('oauth_sessions') - ->onDelete('cascade'); - - $table->foreign('scope_id') - ->references('id')->on('oauth_scopes') - ->onDelete('cascade'); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::table('oauth_session_scopes', function (Blueprint $table) { - $table->dropForeign('oauth_session_scopes_session_id_foreign'); - $table->dropForeign('oauth_session_scopes_scope_id_foreign'); - }); - Schema::drop('oauth_session_scopes'); - } -} diff --git a/database/migrations/2014_04_24_110151_create_oauth_scopes_table.php b/database/migrations/2016_04_02_000001_create_oauth_scopes_table.php similarity index 84% rename from database/migrations/2014_04_24_110151_create_oauth_scopes_table.php rename to database/migrations/2016_04_02_000001_create_oauth_scopes_table.php index 0b4a1d6d..1169b45c 100644 --- a/database/migrations/2014_04_24_110151_create_oauth_scopes_table.php +++ b/database/migrations/2016_04_02_000001_create_oauth_scopes_table.php @@ -1,7 +1,7 @@ * @@ -28,9 +28,10 @@ class CreateOauthScopesTable extends Migration public function up() { Schema::create('oauth_scopes', function (Blueprint $table) { - $table->string('id', 40)->primary(); + $table->increments('id'); + $table->string('identifier')->unique(); + $table->string('name'); $table->string('description'); - $table->timestamps(); }); } diff --git a/database/migrations/2014_04_24_110459_create_oauth_clients_table.php b/database/migrations/2016_04_02_000002_create_oauth_clients_table.php similarity index 66% rename from database/migrations/2014_04_24_110459_create_oauth_clients_table.php rename to database/migrations/2016_04_02_000002_create_oauth_clients_table.php index 817b3f38..4daa23ef 100644 --- a/database/migrations/2014_04_24_110459_create_oauth_clients_table.php +++ b/database/migrations/2016_04_02_000002_create_oauth_clients_table.php @@ -1,7 +1,7 @@ * @@ -14,7 +14,7 @@ use Illuminate\Support\Facades\Schema; /** - * This is the create oauth client table migration class. + * This is the create oauth clients table migration class. * * @author Luca Degasperi */ @@ -27,13 +27,13 @@ class CreateOauthClientsTable extends Migration */ public function up() { - Schema::create('oauth_clients', function (BluePrint $table) { - $table->string('id', 40)->primary(); - $table->string('secret', 40); + Schema::create('oauth_clients', function (Blueprint $table) { + $table->increments('id'); + $table->string('identifier')->unique(); + $table->string('secret'); $table->string('name'); + $table->string('description'); $table->timestamps(); - - $table->unique(['id', 'secret']); }); } @@ -44,6 +44,6 @@ public function up() */ public function down() { - Schema::drop('oauth_clients'); + Schema::drop('oauth_scopes'); } } diff --git a/database/migrations/2014_04_24_110705_create_oauth_client_scopes_table.php b/database/migrations/2016_04_02_000003_create_oauth_client_scopes_table.php similarity index 64% rename from database/migrations/2014_04_24_110705_create_oauth_client_scopes_table.php rename to database/migrations/2016_04_02_000003_create_oauth_client_scopes_table.php index e7a76431..7640a803 100644 --- a/database/migrations/2014_04_24_110705_create_oauth_client_scopes_table.php +++ b/database/migrations/2016_04_02_000003_create_oauth_client_scopes_table.php @@ -1,7 +1,7 @@ * @@ -29,21 +29,19 @@ public function up() { Schema::create('oauth_client_scopes', function (Blueprint $table) { $table->increments('id'); - $table->string('client_id', 40); - $table->string('scope_id', 40); - - $table->timestamps(); + $table->integer('client_id')->unsigned(); + $table->integer('scope_id')->unsigned(); $table->index('client_id'); $table->index('scope_id'); $table->foreign('client_id') - ->references('id')->on('oauth_clients') - ->onDelete('cascade'); + ->references('id')->on('oauth_clients') + ->onDelete('cascade'); $table->foreign('scope_id') - ->references('id')->on('oauth_scopes') - ->onDelete('cascade'); + ->references('id')->on('oauth_scopes') + ->onDelete('cascade'); }); } @@ -54,10 +52,6 @@ public function up() */ public function down() { - Schema::table('oauth_client_scopes', function (Blueprint $table) { - $table->dropForeign('oauth_client_scopes_client_id_foreign'); - $table->dropForeign('oauth_client_scopes_scope_id_foreign'); - }); Schema::drop('oauth_client_scopes'); } } diff --git a/database/migrations/2014_04_24_111254_create_oauth_auth_codes_table.php b/database/migrations/2016_04_02_000004_create_oauth_auth_codes_table.php similarity index 58% rename from database/migrations/2014_04_24_111254_create_oauth_auth_codes_table.php rename to database/migrations/2016_04_02_000004_create_oauth_auth_codes_table.php index 2904702c..de2fe651 100644 --- a/database/migrations/2014_04_24_111254_create_oauth_auth_codes_table.php +++ b/database/migrations/2016_04_02_000004_create_oauth_auth_codes_table.php @@ -1,7 +1,7 @@ * @@ -28,18 +28,20 @@ class CreateOauthAuthCodesTable extends Migration public function up() { Schema::create('oauth_auth_codes', function (Blueprint $table) { - $table->string('id', 40)->primary(); - $table->integer('session_id')->unsigned(); - $table->string('redirect_uri'); - $table->integer('expire_time'); + $table->increments('id'); + $table->string('code')->unique(); + $table->string('redirect_uri')->nullable(); - $table->timestamps(); + $table->integer('client_id')->unsigned(); + + // use a string for the user identifier + $table->string('user_id'); - $table->index('session_id'); + // TODO: make it the current timestamp + $table->timestamp('expires_at')->useCurrent(); + $table->timestamps(); - $table->foreign('session_id') - ->references('id')->on('oauth_sessions') - ->onDelete('cascade'); + $table->foreign('client_id')->references('id')->on('oauth_clients')->onDelete('cascade'); }); } @@ -50,9 +52,6 @@ public function up() */ public function down() { - Schema::table('oauth_auth_codes', function (Blueprint $table) { - $table->dropForeign('oauth_auth_codes_session_id_foreign'); - }); - Schema::drop('oauth_auth_codes'); + Schema::drop('oauth_client_scopes'); } } diff --git a/database/migrations/2014_04_24_111518_create_oauth_access_tokens_table.php b/database/migrations/2016_04_02_000005_create_oauth_access_tokens_table.php similarity index 60% rename from database/migrations/2014_04_24_111518_create_oauth_access_tokens_table.php rename to database/migrations/2016_04_02_000005_create_oauth_access_tokens_table.php index c5881633..2a48329c 100644 --- a/database/migrations/2014_04_24_111518_create_oauth_access_tokens_table.php +++ b/database/migrations/2016_04_02_000005_create_oauth_access_tokens_table.php @@ -1,7 +1,7 @@ * @@ -28,18 +28,19 @@ class CreateOauthAccessTokensTable extends Migration public function up() { Schema::create('oauth_access_tokens', function (Blueprint $table) { - $table->string('id', 40)->primary(); - $table->integer('session_id')->unsigned(); - $table->integer('expire_time'); + $table->increments('id'); + $table->string('token')->unique(); - $table->timestamps(); + $table->integer('client_id')->unsigned(); + + // use a string for the user identifier + $table->string('user_id')->nullable(); - $table->unique(['id', 'session_id']); - $table->index('session_id'); + // TODO: make it the current timestamp + $table->timestamp('expires_at')->useCurrent(); + $table->timestamps(); - $table->foreign('session_id') - ->references('id')->on('oauth_sessions') - ->onDelete('cascade'); + $table->foreign('client_id')->references('id')->on('oauth_clients')->onDelete('cascade'); }); } @@ -50,9 +51,6 @@ public function up() */ public function down() { - Schema::table('oauth_access_tokens', function (Blueprint $table) { - $table->dropForeign('oauth_access_tokens_session_id_foreign'); - }); Schema::drop('oauth_access_tokens'); } } diff --git a/database/migrations/2014_04_24_111403_create_oauth_auth_code_scopes_table.php b/database/migrations/2016_04_02_000006_create_oauth_auth_code_scopes_table.php similarity index 60% rename from database/migrations/2014_04_24_111403_create_oauth_auth_code_scopes_table.php rename to database/migrations/2016_04_02_000006_create_oauth_auth_code_scopes_table.php index fdee7611..58746dc8 100644 --- a/database/migrations/2014_04_24_111403_create_oauth_auth_code_scopes_table.php +++ b/database/migrations/2016_04_02_000006_create_oauth_auth_code_scopes_table.php @@ -1,7 +1,7 @@ * @@ -14,7 +14,7 @@ use Illuminate\Support\Facades\Schema; /** - * This is the create oauth code scopes table migration class. + * This is the create oauth auth codes scopes table migration class. * * @author Luca Degasperi */ @@ -29,21 +29,19 @@ public function up() { Schema::create('oauth_auth_code_scopes', function (Blueprint $table) { $table->increments('id'); - $table->string('auth_code_id', 40); - $table->string('scope_id', 40); - - $table->timestamps(); + $table->integer('auth_code_id')->unsigned(); + $table->integer('scope_id')->unsigned(); $table->index('auth_code_id'); $table->index('scope_id'); $table->foreign('auth_code_id') - ->references('id')->on('oauth_auth_codes') - ->onDelete('cascade'); + ->references('id')->on('oauth_auth_codes') + ->onDelete('cascade'); $table->foreign('scope_id') - ->references('id')->on('oauth_scopes') - ->onDelete('cascade'); + ->references('id')->on('oauth_scopes') + ->onDelete('cascade'); }); } @@ -54,10 +52,6 @@ public function up() */ public function down() { - Schema::table('oauth_auth_code_scopes', function (Blueprint $table) { - $table->dropForeign('oauth_auth_code_scopes_auth_code_id_foreign'); - $table->dropForeign('oauth_auth_code_scopes_scope_id_foreign'); - }); Schema::drop('oauth_auth_code_scopes'); } } diff --git a/database/migrations/2014_04_24_111657_create_oauth_access_token_scopes_table.php b/database/migrations/2016_04_02_000007_create_oauth_access_token_scopes_table.php similarity index 63% rename from database/migrations/2014_04_24_111657_create_oauth_access_token_scopes_table.php rename to database/migrations/2016_04_02_000007_create_oauth_access_token_scopes_table.php index 4c9a7af4..4e10ae7b 100644 --- a/database/migrations/2014_04_24_111657_create_oauth_access_token_scopes_table.php +++ b/database/migrations/2016_04_02_000007_create_oauth_access_token_scopes_table.php @@ -1,7 +1,7 @@ * @@ -29,21 +29,19 @@ public function up() { Schema::create('oauth_access_token_scopes', function (Blueprint $table) { $table->increments('id'); - $table->string('access_token_id', 40); - $table->string('scope_id', 40); - - $table->timestamps(); + $table->integer('access_token_id')->unsigned(); + $table->integer('scope_id')->unsigned(); $table->index('access_token_id'); $table->index('scope_id'); $table->foreign('access_token_id') - ->references('id')->on('oauth_access_tokens') - ->onDelete('cascade'); + ->references('id')->on('oauth_access_tokens') + ->onDelete('cascade'); $table->foreign('scope_id') - ->references('id')->on('oauth_scopes') - ->onDelete('cascade'); + ->references('id')->on('oauth_scopes') + ->onDelete('cascade'); }); } @@ -54,10 +52,6 @@ public function up() */ public function down() { - Schema::table('oauth_access_token_scopes', function (Blueprint $table) { - $table->dropForeign('oauth_access_token_scopes_scope_id_foreign'); - $table->dropForeign('oauth_access_token_scopes_access_token_id_foreign'); - }); Schema::drop('oauth_access_token_scopes'); } } diff --git a/database/migrations/2014_04_24_111810_create_oauth_refresh_tokens_table.php b/database/migrations/2016_04_02_000008_create_oauth_refresh_tokens_table.php similarity index 63% rename from database/migrations/2014_04_24_111810_create_oauth_refresh_tokens_table.php rename to database/migrations/2016_04_02_000008_create_oauth_refresh_tokens_table.php index b7262b03..1c3fd096 100644 --- a/database/migrations/2014_04_24_111810_create_oauth_refresh_tokens_table.php +++ b/database/migrations/2016_04_02_000008_create_oauth_refresh_tokens_table.php @@ -1,7 +1,7 @@ * @@ -28,15 +28,16 @@ class CreateOauthRefreshTokensTable extends Migration public function up() { Schema::create('oauth_refresh_tokens', function (Blueprint $table) { - $table->string('id', 40)->unique(); - $table->string('access_token_id', 40)->primary(); - $table->integer('expire_time'); + $table->increments('id'); + $table->string('token')->unique(); + $table->integer('access_token_id')->unsigned(); + + // TODO: make it the current timestamp + $table->timestamp('expires_at')->useCurrent(); $table->timestamps(); - $table->foreign('access_token_id') - ->references('id')->on('oauth_access_tokens') - ->onDelete('cascade'); + $table->foreign('access_token_id')->references('id')->on('oauth_access_tokens')->onDelete('cascade'); }); } @@ -47,10 +48,6 @@ public function up() */ public function down() { - Schema::table('oauth_refresh_tokens', function (Blueprint $table) { - $table->dropForeign('oauth_refresh_tokens_access_token_id_foreign'); - }); - Schema::drop('oauth_refresh_tokens'); } } diff --git a/database/migrations/2014_04_24_110304_create_oauth_grants_table.php b/database/migrations/2016_04_02_000010_create_oauth_client_redirect_uris_table.php similarity index 54% rename from database/migrations/2014_04_24_110304_create_oauth_grants_table.php rename to database/migrations/2016_04_02_000010_create_oauth_client_redirect_uris_table.php index a15f8ff7..34293ff9 100644 --- a/database/migrations/2014_04_24_110304_create_oauth_grants_table.php +++ b/database/migrations/2016_04_02_000010_create_oauth_client_redirect_uris_table.php @@ -1,7 +1,7 @@ * @@ -14,11 +14,11 @@ use Illuminate\Support\Facades\Schema; /** - * This is the create oauth grants table migration class. + * This is the create oauth client redirect uris table migration class. * * @author Luca Degasperi */ -class CreateOauthGrantsTable extends Migration +class CreateOauthClientRedirectUrisTable extends Migration { /** * Run the migrations. @@ -27,9 +27,14 @@ class CreateOauthGrantsTable extends Migration */ public function up() { - Schema::create('oauth_grants', function (Blueprint $table) { - $table->string('id', 40)->primary(); + Schema::create('oauth_client_redirect_uris', function (Blueprint $table) { + $table->increments('id'); + $table->string('uri')->unique(); + $table->integer('client_id')->unsigned(); + $table->timestamps(); + + $table->foreign('client_id')->references('id')->on('oauth_clients')->onDelete('cascade'); }); } @@ -40,6 +45,6 @@ public function up() */ public function down() { - Schema::drop('oauth_grants'); + Schema::drop('oauth_client_redirect_uris'); } } diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 4c3af43a..00000000 --- a/docs/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Documentation - -This wiki will guide you through all the things you need to successfully integrate an OAuth 2.0 compliant server into your Laravel applications. Let's begin! - -## Getting Started - -1. [Introduction](getting-started/introduction.md) -2. [Terminology](getting-started/terminology.md) -3. [Laravel 4 Installation](getting-started/laravel-4.md) -4. [Laravel 5 Installation](getting-started/laravel-5.md) -5. [Lumen Installation](getting-started/lumen.md) -6. [Configuration](getting-started/config.md) -7. [Middlewares](getting-started/middlewares.md) -8. [Apache ModRewrite](getting-started/apache.md) - -#### Authorization Server - -1. [Choosing a Grant](authorization-server/choosing-grant.md) -2. Implementing an Authorization Server - 1. [With the Client Credentials Grant](authorization-server/client-credentials.md) - 2. [With the Password Grant](authorization-server/password.md) - 3. [With the Auth Code Grant](authorization-server/auth-code.md) - 4. [With the Refresh Token Grant](authorization-server/refresh-token.md) -3. Extending the server - 1. Using a different storage - 2. [Creating your own grant type](authorization-server/custom.md) - -#### Resource Server - -1. [Securing your API endpoints](resource-server/securing-endpoints.md) - 2. [Defining scopes](resource-server/securing-endpoints.md#defining-scopes) - 3. [Checking the access token](resource-server/securing-endpoints.md#checking-the-access-token) - 4. [Checking the scopes](resource-server/securing-endpoints.md#checking-the-scopes) - -## Articles & Resources - -- [The OAuth 2.0 authorization framework specification](https://tools.ietf.org/html/rfc6749) -- [The PHP League's official documentation](http://oauth2.thephpleague.com) -- [OAuth 2 Simplified by Aaron Parecki](https://aaronparecki.com/articles/2012/07/29/1/oauth2-simplified) - -## Contributing - -We welcome any pull request to improve the documentation. Please see our [contributing guidelines](../CONTRIBUTING.md). - -## License - -The Laravel OAuth 2.0 package is released under [the MIT License](../LICENSE). diff --git a/docs/authorization-server/auth-code.md b/docs/authorization-server/auth-code.md deleted file mode 100644 index c509b983..00000000 --- a/docs/authorization-server/auth-code.md +++ /dev/null @@ -1,85 +0,0 @@ -# Implementing an Authorization Server with the Auth Code Grant - -1. To enable this grant add the following to the `config/oauth2.php` configuration file - - ```php - 'grant_types' => [ - 'authorization_code' => [ - 'class' => '\League\OAuth2\Server\Grant\AuthCodeGrant', - 'access_token_ttl' => 3600, - 'auth_token_ttl' => 3600 - ] - ] - ``` - -2. Set up a route to respond to the incoming auth code requests - - ```php - Route::get('oauth/authorize', ['as' => 'oauth.authorize.get', 'middleware' => ['check-authorization-params', 'auth'], function() { - $authParams = Authorizer::getAuthCodeRequestParams(); - - $formParams = array_except($authParams,'client'); - - $formParams['client_id'] = $authParams['client']->getId(); - - $formParams['scope'] = implode(config('oauth2.scope_delimiter'), array_map(function ($scope) { - return $scope->getId(); - }, $authParams['scopes'])); - - return View::make('oauth.authorization-form', ['params' => $formParams, 'client' => $authParams['client']]); - }]); - ``` - > **Note:** The form you submit should preserve the query string. - - ```php -

{{$client->getName()}}

-
- - - - - - - - -
- ``` - -3. Set up a route to respond to the form being posted. - - ```php - Route::post('oauth/authorize', ['as' => 'oauth.authorize.post', 'middleware' => ['csrf', 'check-authorization-params', 'auth'], function() { - - $params = Authorizer::getAuthCodeRequestParams(); - $params['user_id'] = Auth::user()->id; - $redirectUri = '/'; - - // If the user has allowed the client to access its data, redirect back to the client with an auth code. - if (Request::has('approve')) { - $redirectUri = Authorizer::issueAuthCode('user', $params['user_id'], $params); - } - - // If the user has denied the client to access its data, redirect back to the client with an error message. - if (Request::has('deny')) { - $redirectUri = Authorizer::authCodeRequestDeniedRedirectUri(); - } - - return Redirect::to($redirectUri); - }]); - ``` - -4. Add a route to respond to the access token requests - - ```php - Route::post('oauth/access_token', function() { - return Response::json(Authorizer::issueAccessToken()); - }); - ``` - -5. Next add a sample `client` to the `oauth_clients` table. - -6. And finally add `redirect_uri` to the `oauth_client_endpoints` table for `client`. - ---- - -[← Back to start](../README.md) diff --git a/docs/authorization-server/choosing-grant.md b/docs/authorization-server/choosing-grant.md deleted file mode 100644 index 2f6065a6..00000000 --- a/docs/authorization-server/choosing-grant.md +++ /dev/null @@ -1,124 +0,0 @@ -# Choosing a Grant - -OAuth 2.0 by it’s nature is a very flexible standard and can be adapted to work in many different scenarios. The [core specification](http://tools.ietf.org/html/rfc6749) describes four authorization grants: - -* Authorization code grant -* Implicit grant -* Resource owner credentials grant -* Client credentials grant - -The specification also details another grant called the _refresh token grant_. - -Furthermore there are a number of other grants that have gone through the IETF ratification process (none of which at the time of writing have been formally standardised): - -* Message authentication code (MAC) tokens -* SAML 2.0 Bearer Assertion Profiles -* JSON web token grant - -The end goal of each of these grants (except the refresh token grant) is for the client application to have an access token (which represents a user’s permission for the client to access their data) which it can use to authenticate a request to an API endpoint. - -This page describes each of the above grants and their appropriate use cases. - -As a refresher here is a quick glossary of OAuth terms (taken from the core spec): - -* **Resource owner (a.k.a. the User)** - An entity capable of granting access to a protected resource. When the resource owner is a person, it is referred to as an end-user. -* **Resource server (a.k.a. the API server)** - The server hosting the protected resources, capable of accepting and responding to protected resource requests using access tokens. -* **Client** - An application making protected resource requests on behalf of the resource owner and with its authorization. The term client does not imply any particular implementation characteristics (e.g. whether the application executes on a server, a desktop, or other devices). -* **Authorization server** - The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization. - -## Authorization code grant ([section 4.1](http://tools.ietf.org/html/rfc6749#section-4.1)) - -> To enable this grant: -> ```php -> 'grant_types' => [ -> 'authorization_code' => [ -> 'class' => '\League\OAuth2\Server\Grant\AuthCodeGrant', -> 'access_token_ttl' => 3600, -> 'auth_token_ttl' => 60 -> ] -> ] -> ``` - -The authorization code grant is the grant that most people think of when OAuth is described. - -If you’ve ever signed into a website or application with your Twitter/Facebook/Google/(insert major Internet company here) account then you’ll have experienced using this grant. - -Essentially a user will click on a “sign in with Facebook” (or other IdP) and then be redirected from the application/website (the “client”) to the IdP authorization server. The user will then sign in to the IdP with their credentials, and then - if they haven’t already - authorise the client to allow it to use the user’s data (such as their name, email address, etc). If they authorise the request the user will be redirected back to the client with a token (called the authorization code) in the query string (e.g. `http://client.com/redirect?code=XYZ123`) which the client will capture and exchange for an access token in the background. - -This grant is suitable where the resource owner is a user and they are using a client which allows a user to interact with a website in a browser. An obvious example is the client being another website, but desktop applications such as Spotify or Reeder use embedded browsers. - -Some mobile applications use this flow and again use an embedded browser (or redirect the user to the native browser and then are redirected back to the app using a custom protocol). - -In this grant the access token is kept private from the resource owner. - -If you have a mobile application that is for your own service (such as the official Spotify or Facebook apps on iOS) it isn’t appropriate to use this grant as the app itself should already be trusted by your authorization server and so the _resource owner credentials grant_ would be more appropriate. - -## Implicit grant ([section 4.2](http://tools.ietf.org/html/rfc6749#section-4.2)) - -The implicit grant is similar to the authentication code grant described above. The user will be redirected in a browser to the IdP authorization server, sign in, authorize the request but instead of being returned to the client with an authentication code they are redirected with an access token straight away. - -The purpose of the implicit grant is for use by clients which are not capable of keeping the client’s own credentials secret; for example a JavaScript only application. - -**If you decide to implement this grant then you must be aware that the access token should be treated as “public knowledge” (like a public RSA key)** and therefore it must have a very limited permissions when interacting with the API server. For example an access token that was granted using the authentication code grant could have permission to be used to delete resources owned by the user, however an access token granted through the implicit flow should only be able to “read” resources and never perform any destructive operations (i.e. non-idempotent method). - -## Resource owner credentials grant ([section 4.3](http://tools.ietf.org/html/rfc6749#section-4.3)) - -To enable this grant: -```php -'grant_types' => [ - 'password' => [ - 'class' => '\League\OAuth2\Server\Grant\PasswordGrant', - 'callback' => '\YourAppNamespace\Verifier@verify', - 'access_token_ttl' => 3600 - ] -] -``` - -When this grant is implemented the client itself will ask the user for their username and password (as opposed to being redirected to an IdP authorization server to authenticate) and then send these to the authorization server along with the client’s own credentials. If the authentication is successful then the client will be issued with an access token. - -This grant is suitable for trusted clients such as a service’s own mobile client (for example Spotify’s iOS app). You could also use this in software where it’s not easy to implement the authorization code - for example we bolted this authorization grant into [OwnCloud](http://owncloud.org/) so we could retrieve details about a user that we couldn’t access over LDAP from the university’s Active Directory server. - -## Client credentials grant ([section 4.4](http://tools.ietf.org/html/rfc6749#section-4.4)) - -To enable this grant: -```php -'grant_types' => [ - 'client_credentials' => [ - 'class' => '\League\OAuth2\Server\Grant\ClientCredentialsGrant', - 'access_token_ttl' => 3600 - ] -] -``` - -This grant is similar to the resource owner credentials grant except only the client’s credentials are used to authenticate a request for an access token. Again this grant should only be allowed to be used by trusted clients. - -This grant is suitable for machine-to-machine authentication, for example for use in a cron job which is performing maintenance tasks over an API. Another example would be a client making requests to an API that don’t require user’s permission. - -When someone visits a member of staff’s page on the [University of Lincoln staff directory](http://staff.lincoln.ac.uk/) the website uses it’s own access token (that was generated using this grant) to authenticate a request to the API server to get the data about the member of staff that is used to build the page. When a member of staff signs in to update their profile however their own access token is used to retrieve and update their data. Therefore there is a good separation of concerns and we can easily restrict permissions that each type of access token has. - -## Refresh token grant ([section 1.5](http://tools.ietf.org/html/rfc6749#section-1.5)) - -To enable this grant: -```php -'grant_types' => [ - 'refresh_token' => [ - 'class' => '\League\OAuth2\Server\Grant\RefreshTokenGrant', - 'access_token_ttl' => 3600, - 'refresh_token_ttl' => 36000 - ] -] -``` - -The OAuth 2.0 specification also details a fifth grant which can be used to “refresh” (i.e. renew) an access token which has expired. - -Authorization servers which support this grant will also issue a “refresh token” when it returns an access token to a client. When the access token expires instead of sending the user back through the authorization code grant the client can use to the refresh token to retrieve a new access token with the same permissions as the old one. - -A problem with the grant is that it means the client has to maintain state of each token and then either on a cron job keep access tokens up to date or when it tries to make a request and it fails then go and update the access token and repeat the request. - ---- - -This page was originally posted at [http://alexbilbie.com/2013/02/a-guide-to-oauth-2-grants/](http://alexbilbie.com/2013/02/a-guide-to-oauth-2-grants/). It has been modified to suite this package configuration. - ---- - -[← Back to start](../README.md) diff --git a/docs/authorization-server/client-credentials.md b/docs/authorization-server/client-credentials.md deleted file mode 100644 index 66d630eb..00000000 --- a/docs/authorization-server/client-credentials.md +++ /dev/null @@ -1,26 +0,0 @@ -# Implementing an Authorization Server with the Client Credentials Grant - -1. To enable this grant add the following to the `config/oauth2.php` configuration file: - - ```php - 'grant_types' => [ - 'client_credentials' => [ - 'class' => '\League\OAuth2\Server\Grant\ClientCredentialsGrant', - 'access_token_ttl' => 3600 - ] - ] - ``` - -2. Next add a couple of `clients` to the `oauth_clients` table. - -3. Finally set up a route to respond to the incoming access token requests. - - ```php - Route::post('oauth/access_token', function() { - return Response::json(Authorizer::issueAccessToken()); - }); - ``` - ---- - -[← Back to start](../README.md) diff --git a/docs/authorization-server/custom.md b/docs/authorization-server/custom.md deleted file mode 100644 index a42a7b1c..00000000 --- a/docs/authorization-server/custom.md +++ /dev/null @@ -1,20 +0,0 @@ -# Creating a Custom Grant - -To create your custom grant type follow Alex's guide. - -> http://oauth2.thephpleague.com/authorization-server/custom-grants/ - -Registering your custom Grant with this package is easy: - -Add the following properties to your `config/oauth.php` -```php -'grant_types' => [ - 'custom_grant_identifier' => [ - 'class' => 'Your\Custom\Grant\Namespace\And\Class' - ] -] -``` - ---- - -[← Back to start](../README.md) diff --git a/docs/authorization-server/password.md b/docs/authorization-server/password.md deleted file mode 100644 index bc59f42b..00000000 --- a/docs/authorization-server/password.md +++ /dev/null @@ -1,49 +0,0 @@ -# Implementing an Authorization Server with the Password Grant - -1. To enable this grant add the following to the `config/oauth2.php` configuration file. - ```php - 'grant_types' => [ - 'password' => [ - 'class' => '\League\OAuth2\Server\Grant\PasswordGrant', - 'callback' => '\App\PasswordVerifier@verify', - 'access_token_ttl' => 3600 - ] - ] - ``` - -2. Create a class with a `verify` method where you check if the provided user is a valid one. - - ```php - use Illuminate\Support\Facades\Auth; - - class PasswordGrantVerifier - { - public function verify($username, $password) - { - $credentials = [ - 'email' => $username, - 'password' => $password, - ]; - - if (Auth::once($credentials)) { - return Auth::user()->id; - } - - return false; - } - } - ``` - -3. Next add a sample `client` to the `oauth_clients` table. - -4. Finally set up a route to respond to the incoming access token requests. - - ```php - Route::post('oauth/access_token', function() { - return Response::json(Authorizer::issueAccessToken()); - }); - ``` - ---- - -[← Back to start](../README.md) diff --git a/docs/authorization-server/refresh-token.md b/docs/authorization-server/refresh-token.md deleted file mode 100644 index d411a34f..00000000 --- a/docs/authorization-server/refresh-token.md +++ /dev/null @@ -1,30 +0,0 @@ -# Implementing an Authorization Server with the Refresh Token Grant - -1. To enable this grant add the following to the `config/oauth2.php` configuration file - - ```php - 'grant_types' => [ - 'refresh_token' => [ - 'class' => '\League\OAuth2\Server\Grant\RefreshTokenGrant', - 'access_token_ttl' => 3600, - 'refresh_token_ttl' => 36000 - ] - ] - ``` - - > **Note:** The refresh token grant is to be used together with one other of the following grants: `PasswordGrant`, `AuthCodeGrant`. - - -2. Set up a route to respond to the incoming access token requests. - - ```php - Route::post('oauth/access_token', function() { - return Response::json(Authorizer::issueAccessToken()); - }); - ``` - -3. Whenever you request an Access Token using a grant that supports the use of the Refresh Token grant, you'll get a Refresh Token together with the Access Token. Once the Access Token expires, use the Refresh Token to require a new one. - ---- - -[← Back to start](../README.md) diff --git a/docs/getting-started/apache.md b/docs/getting-started/apache.md deleted file mode 100644 index 019b00f4..00000000 --- a/docs/getting-started/apache.md +++ /dev/null @@ -1,37 +0,0 @@ -# Apache ModRewrite - -If you are using Apache, you might notice that your Authorization headers are not making it through with the request. Many hosts do not allow this header through by default, and Apache is no exception. - -Open up your .htaccess file in /public and add the following lines of code before the Front Controller block: - -```sh -# Authorization Headers -RewriteCond %{HTTP:Authorization} . -RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] -``` - -Your full `.htaccess` file should look like this after the change: - -```sh - - - Options -MultiViews - - - RewriteEngine On - - # Redirect Trailing Slashes... - RewriteRule ^(.*)/$ /$1 [L,R=301] - # Authorization Header - RewriteCond %{HTTP:Authorization} . - RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] - # Handle Front Controller... - RewriteCond %{REQUEST_FILENAME} !-d - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^ index.php [L] - -``` - ---- - -[← Back to start](../README.md) diff --git a/docs/getting-started/config.md b/docs/getting-started/config.md deleted file mode 100644 index eea0db76..00000000 --- a/docs/getting-started/config.md +++ /dev/null @@ -1,86 +0,0 @@ -# Config - -The file in `config/oauth2.php` contains plenty of options you can use to configure your OAuth 2.0 server implementation to suit the needs of your business. Here's the explanation for them. - -### `grant_types` -options: `array` - -An array of grant types supported by the authorization server to obtain an access token. See [Issuing access tokens](https://github.com/lucadegasperi/oauth2-server-laravel/wiki#authorization-server) for how to configure and use the different grant types. - -### `token_type` -options: `string` - -default: `League\OAuth2\Server\TokenType\Bearer` - -This option informs the authorization server how the returned tokens should be formatted. - -### `state_param` -options: `true` or `false` - -default: `false` - -If this option is true, each request to the authorization server should contain a `&state=random_string` param. The state parameter is an additional security measure and the authorization server will reply back to your request with a response containing the same state param you passed. If the state param between the request and response doesn't match, the authorization server might have been compromised. - -### `scope_param` -options: `true` or `false` - -default: `false` - -Whether or not the scope parameter is required in the query string. The scope(s) reflects the type of permission the client wants to access on user's behalf. See [Defining Scopes] for how to use scopes. - - -### `scope_delimiter` -options: `string` - -default `','` - -The separator used to split the different scopes provided in the query string when multiple scopes are provided. See [Defining Scopes] for how to use scopes. - - -### `default_scope` -options: `string` or `null` - -default: `null` - -This option indicates the default scope each access token request has when no scope parameter is provided in the query string. `null` means the requests have no default scope. See [Defining Scopes] for how to use scopes. - - -### `access_token_ttl` -options: `integer` - -default: `3600` - -The number of seconds after an issued access token is not considered valid. Can be overwritten on a grant type basis. - -### `limit_clients_to_grants` -options: `true` or `false` - -default: `false` - -This options sets whether or not a client is limited to specific grant types for obtaining an access token. The `oauth_client_grants` table regulates which clients can use which grant types. - -### `limit_clients_to_scopes` -options: `true` or `false` - -default: `false` - -This options sets which clients can use which scopes. It is useful for allowing different grades of permissions to different clients. The `oauth_client_scopes` table regulates which clients can use which scopes. - -### `limit_scopes_to_grants` -options: `true` or `false` - -default: `false` - -This options allows the use of certain scopes only when required with the appropriate grant type. this is due to the fact that different grant types have different grades of security. For example, a highly permissive scope should be allowed only to clients you trust or can request an access token securely. -The `oauth_grant_scopes` table regulates which grant types can use which scopes. - -### `http_headers_only` -options: `true` or `false` - -default: `false` - -This options tells the resource server where to check for the access token. If set to true only the http headers will be checked. - ---- - -[← Back to start](../README.md) diff --git a/docs/getting-started/introduction.md b/docs/getting-started/introduction.md deleted file mode 100644 index 8787677c..00000000 --- a/docs/getting-started/introduction.md +++ /dev/null @@ -1,21 +0,0 @@ -# Introduction - -This library makes working with OAuth 2.0 inside your Laravel application trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them. - -It supports out of the box the following grants: - -- Authorization code grant -- Client credentials grant -- Resource owner password credentials grant -- Refresh grant -- You can also define your own grants. - -In addition it supports the following token types: - -- Bearer tokens -- MAC tokens (coming soon) -- JSON web tokens (coming soon) - ---- - -[← Back to start](../README.md) diff --git a/docs/getting-started/laravel-4.md b/docs/getting-started/laravel-4.md deleted file mode 100644 index 1c116742..00000000 --- a/docs/getting-started/laravel-4.md +++ /dev/null @@ -1,52 +0,0 @@ -# Laravel 4 - -Composer is the recommended way to install this package. Add the following line to your `composer.json` file: - -```json -"lucadegasperi/oauth2-server-laravel": "^3.0" -``` - -Then run `composer update` to get the package. - -> **Note:** If installation fails set `"minimum-stability": "dev"` in your `composer.json` file. - -Once composer has installed the package add this line of code to the `providers` array located in your `app/config/app.php` file: -```php -'LucaDegasperi\OAuth2Server\Storage\FluentStorageServiceProvider', -'LucaDegasperi\OAuth2Server\OAuth2ServerServiceProvider', -``` - -And this lines to the `aliases` array: -```php -'Authorizer' => 'LucaDegasperi\OAuth2Server\Facades\AuthorizerFacade', -``` - -## Configuration Publishing - -In order to customize the behavior of this package, a configuration file to publish is provided to you. - -```bash -php artisan config:publish lucadegasperi/oauth2-server-laravel -``` - -Afterwards you can edit the file `app/config/packages/lucadegasperi/oauth2-server-laravel/oauth2.php` to suit your needs. A description of the configuration fields is [described here](https://github.com/lucadegasperi/oauth2-server-laravel/wiki/Configuration-Options). - -## Migrations - -This package comes with all the database tables you need to run a full featured OAuth 2.0 server. Run the migrations command to get them into your application installation - -```bash -php artisan oauth2-server:migrations -``` - -## Sample Controller - -To make your life easier, this package comes with a sample controller you can use to get started with your OAuth 2.0 Server. The `controller` command will publish the controller into your `app/controllers` directory. - -```bash -php artisan oauth2-server:controller -``` - ---- - -[← Back to start](../README.md) diff --git a/docs/getting-started/laravel-5.md b/docs/getting-started/laravel-5.md deleted file mode 100644 index b5a87070..00000000 --- a/docs/getting-started/laravel-5.md +++ /dev/null @@ -1,54 +0,0 @@ -# Laravel 5 - -Composer is the recommended way to install this package. Add the following line to your `composer.json` file: - -```json -"lucadegasperi/oauth2-server-laravel": "5.0.*" -``` - -Then run `composer update` to get the package. - -Once composer has installed the package add this line of code to the `providers` array located in your `config/app.php` file: -```php -LucaDegasperi\OAuth2Server\Storage\FluentStorageServiceProvider::class, -LucaDegasperi\OAuth2Server\OAuth2ServerServiceProvider::class, -``` - -Add this line to the `aliases` array: -```php -'Authorizer' => LucaDegasperi\OAuth2Server\Facades\Authorizer::class, -``` - -Add the following line to your `app/Http/Kernel.php` file in the `$middleware` array -```php -\LucaDegasperi\OAuth2Server\Middleware\OAuthExceptionHandlerMiddleware::class, -``` -This will catch any OAuth error and respond appropriately. - -Then add -```php -'oauth' => \LucaDegasperi\OAuth2Server\Middleware\OAuthMiddleware::class, -'oauth-user' => \LucaDegasperi\OAuth2Server\Middleware\OAuthUserOwnerMiddleware::class, -'oauth-client' => \LucaDegasperi\OAuth2Server\Middleware\OAuthClientOwnerMiddleware::class, -'check-authorization-params' => \LucaDegasperi\OAuth2Server\Middleware\CheckAuthCodeRequestMiddleware::class, -``` -to the `$routeMiddleware` array. - -In order to make some the authorization and resource server work correctly with Laravel5, remove the `App\Http\Middleware\VerifyCsrfToken` line from the `$middleware` array and place it in the `$routeMiddleware` array like this: `'csrf' => App\Http\Middleware\VerifyCsrfToken::class,` - -> **Note:** remember to add the csrf middleware manually on any route where it's appropriate. - -### Migrations and Configuration Publishing -Run `php artisan vendor:publish` to publish this package configuration and migrations. Afterwards you can edit the file `config/oauth2.php` to suit your needs. - -> **Note:** migrations are only published, remember to run them when ready. - -Run migration to create required tables - -```bash -php artisan migrate -``` - ---- - -[← Back to start](../README.md) diff --git a/docs/getting-started/lumen.md b/docs/getting-started/lumen.md deleted file mode 100644 index bd23e2bd..00000000 --- a/docs/getting-started/lumen.md +++ /dev/null @@ -1,53 +0,0 @@ -# Lumen - -Composer is the recommended way to install this package. Add the following line to your composer.json file: - -```php -"lucadegasperi/oauth2-server-laravel": "^5.0" -``` -Then run composer update to get the package. - -### Register package - -In your `bootstrap/app.php` register service providers - -```php -$app->register(\LucaDegasperi\OAuth2Server\Storage\FluentStorageServiceProvider::class); -$app->register(\LucaDegasperi\OAuth2Server\OAuth2ServerServiceProvider::class); -``` - -... and middleware - -```php -$app->middleware([ - \LucaDegasperi\OAuth2Server\Middleware\OAuthExceptionHandlerMiddleware::class -]); -``` - -... and route middleware - -```php -$app->routeMiddleware([ - 'check-authorization-params' => \LucaDegasperi\OAuth2Server\Middleware\CheckAuthCodeRequestMiddleware::class, - 'csrf' => \Laravel\Lumen\Http\Middleware\VerifyCsrfToken::class, - 'oauth' => \LucaDegasperi\OAuth2Server\Middleware\OAuthMiddleware::class, - 'oauth-client' => \LucaDegasperi\OAuth2Server\Middleware\OAuthClientOwnerMiddleware::class, - 'oauth-user' => \LucaDegasperi\OAuth2Server\Middleware\OAuthUserOwnerMiddleware::class, -]); -``` - -### Copy config - -Copy `vendor/lucadegasperi/oauth2-server-laravel/config/oauth2.php` to your own config folder (`config/oauth2.php` in your project root). It has to be the correct config folder as it is registered using `$app->configure()`. - -### Migrate - -First copy the migrations from `vendor/lucadegasperi/oauth2-server-laravel/database/migrations` to your applications `database/migrations` directory. - -Uncomment `$app->withEloquent();` and run `php artisan migrate`. - -If you get an error saying the Config class can not be found, add `class_alias('Illuminate\Support\Facades\Config', 'Config');` to your `bootstrap/app.php` file and uncomment `$app->withFacades();` temporarily to import the migrations. - ---- - -[← Back to start](../README.md) diff --git a/docs/getting-started/middlewares.md b/docs/getting-started/middlewares.md deleted file mode 100644 index 2870804d..00000000 --- a/docs/getting-started/middlewares.md +++ /dev/null @@ -1,39 +0,0 @@ -# Middlewares - -This package comes with four different middlewares to make the integration into Laravel much easier. - -### OAuthMiddleware - -This is the core middleware which should be used in almost all cases for authoring requests. - -### OAuthClientOwnerMiddleware - -Use this middleware to check if the current authorization request owner is of the type `client`. This middleware is associated with the `client_credentials` grant. - -### OAuthUserOwnerMiddleware - -Use this middleware to check if the current authorization request owner is of the type `user`. This middleware is associated with the `password` grant. - -> **Note:** this middleware is required in order to fetch the current resource owners ID. - -### CheckAuthCodeRequestMiddleware - -Use this middleware to check access tokens on the client after successfully authenticating the resource owner and obtaining authorization. This middleware is associated with the `authorization_code` grant. - -## Order - -Please note that the middlewares has to be applied in a certain order. The *OAuthMiddleware* has to be added before the `OAuthClientOwnerMiddleware` and the `OAuthUserOwnerMiddleware`. - -```php -public function __construct() -{ - $this->middleware('oauth'); - $this->middleware('oauth-user'); -} -``` - -If the middlewares isn't in the correct order, methods like the `Authorizer::getResourceOwnerId()` wont work. - ---- - -[← Back to start](../README.md) diff --git a/docs/getting-started/terminology.md b/docs/getting-started/terminology.md deleted file mode 100644 index 83d53032..00000000 --- a/docs/getting-started/terminology.md +++ /dev/null @@ -1,17 +0,0 @@ -# Terminology - -Before we dive into some code, here's a refresher on the meaning of the terms used by OAuth 2.0. - -Term | Description ------|----------- -Access token | A token used to access protected resources. -Authorization code | An intermediary token generated when a user authorizes a client to access protected resources on their behalf. The client receives this token and exchanges it for an access token. -Authorization server | A server which issues access tokens after successfully authenticating a client and resource owner, and authorizing the request. -Client | An application which accesses protected resources on behalf of the resource owner (such as a user). The client could be hosted on a server, desktop, mobile or other device. -Grant | A grant is a method of acquiring an access token. -Resource server | A server which sits in front of protected resources (for example "tweets", "users", "photos", or personal data) and is capable of accepting and responding to protected resource requests using access tokens. -Scope | A permission. - ---- - -[← Back to start](../README.md) diff --git a/docs/resource-server/securing-endpoints.md b/docs/resource-server/securing-endpoints.md deleted file mode 100644 index 83afd370..00000000 --- a/docs/resource-server/securing-endpoints.md +++ /dev/null @@ -1,48 +0,0 @@ -# Securing your API endpoints - -This package comes with a series of tools to help you protect your API endpoints using OAuth 2.0. This tools include the access token verification and the permissions verification. First let's talk about defining permissions (scopes). - -### Defining scopes - -In the context of OAuth, scopes are the part of your API, the client (the third-party application) is trying to access. You can think of them as a sort of permission the client asks to have. Scopes are completely arbitrary string you define. When using this package, all your scopes should be saved into the `oauth_scopes` table. -When a client asks for an access token he'll pass the scopes he needs in order to work. The authorization server will then verify the scopes exist and the client has the right to use them. - -> Using scopes is optional, but any non trivial application will benefit from them. - -### Checking the access token - -When requesting the resources of a protected endpoint the client should send an access token (previously issued to it) and the endpoint should check its validity. This is achieved by using the `oauth` middleware on any route of your API you want to protect with OAuth. - -```php -Route::get('protected-resource', ['middleware' => 'oauth', function() { - // return the protected resource -}]); -``` -This middleware will allow the access to the protected resource to any client with a valid access token. It will also send the client an error if he hasn't provided a valid access token. If you want to limit the access to the resource only to clients with certain permissions, here's where scopes come in handy. - -### Checking the scopes - -Every access token is tied to the client, the resource owner and the scopes it can access. To check if the client can access a resource with its permission, use the `oauth` middleware with the optional arguments. This will check the validity of the access token and the permissions. - -```php -Route::get('protected-resource', ['middleware' => 'oauth:scope1+scope2', function() { - // return the protected resource -}]); -``` -When at least one of the scope doesn't match the permissions the client has, the middleware will return an error to the client, informing it that it doesn't have the required permissions to access the endpoint. - -### Checking the access token owner - -When using the client_credentials grant type, the access token owner and the client can be the same entity to distinguish this particular case there's the `oauth-owner` middleware. Parameters for this middleware are either `client` or `user` - -### Finding access token owner - -```php -use LucaDegasperi\OAuth2Server\Facades\Authorizer; - -Authorizer::getResourceOwnerId(); -``` - ---- - -[← Back to start](../README.md) diff --git a/phpspec.yml.dist b/phpspec.yml.dist deleted file mode 100644 index 15c3dd41..00000000 --- a/phpspec.yml.dist +++ /dev/null @@ -1,6 +0,0 @@ -suites: - package_suite: - namespace: LucaDegasperi\OAuth2Server - src_path: src - spec_prefix: unit - spec_path: tests diff --git a/resources/views/authorize.blade.php b/resources/views/authorize.blade.php new file mode 100644 index 00000000..e931d4bb --- /dev/null +++ b/resources/views/authorize.blade.php @@ -0,0 +1,34 @@ +@extends('oauth2server::layout') + +@section('content') +
+
+
+
+
Authorize App
+
+
+ {{ csrf_field() }} + + {{ $authRequest->getClient()->name }} + +
+
+ + + + +
+
+
+ +
+
+
+
+
+@endsection diff --git a/resources/views/layout.blade.php b/resources/views/layout.blade.php new file mode 100644 index 00000000..59bc34d4 --- /dev/null +++ b/resources/views/layout.blade.php @@ -0,0 +1,82 @@ + + + + + + + + Laravel + + + + + + + + {{-- --}} + + + + + + +@yield('content') + + + + +{{-- --}} + + diff --git a/src/Authorizer.php b/src/Authorizer.php deleted file mode 100644 index 4dcc8495..00000000 --- a/src/Authorizer.php +++ /dev/null @@ -1,309 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server; - -use League\OAuth2\Server\AuthorizationServer as Issuer; -use League\OAuth2\Server\Exception\AccessDeniedException; -use League\OAuth2\Server\ResourceServer as Checker; -use League\OAuth2\Server\TokenType\TokenTypeInterface; -use League\OAuth2\Server\Util\RedirectUri; -use LucaDegasperi\OAuth2Server\Exceptions\NoActiveAccessTokenException; -use Symfony\Component\HttpFoundation\Request; - -/** - * This is the authorizer class. - * - * @author Luca Degasperi - */ -class Authorizer -{ - /** - * The authorization server (aka the issuer). - * - * @var \League\OAuth2\Server\AuthorizationServer - */ - protected $issuer; - - /** - * The resource server (aka the checker). - * - * @var \League\OAuth2\Server\ResourceServer - */ - protected $checker; - - /** - * The auth code request parameters. - * - * @var array - */ - protected $authCodeRequestParams; - - /** - * The redirect uri generator. - * - * @var bool|null - */ - protected $redirectUriGenerator = null; - - /** - * Create a new Authorizer instance. - * - * @param \League\OAuth2\Server\AuthorizationServer $issuer - * @param \League\OAuth2\Server\ResourceServer $checker - */ - public function __construct(Issuer $issuer, Checker $checker) - { - $this->issuer = $issuer; - $this->checker = $checker; - $this->authCodeRequestParams = []; - } - - /** - * Get the issuer. - * - * @return \League\OAuth2\Server\AuthorizationServer - */ - public function getIssuer() - { - return $this->issuer; - } - - /** - * Get the checker. - * - * @return \League\OAuth2\Server\ResourceServer - */ - public function getChecker() - { - return $this->checker; - } - - /** - * Get the current access token for the session. - * - * If the session does not have an active access token, an exception will be thrown. - * - * @throws \LucaDegasperi\OAuth2Server\Exceptions\NoActiveAccessTokenException - * - * @return \League\OAuth2\Server\Entity\AccessTokenEntity - */ - public function getAccessToken() - { - $accessToken = $this->getChecker()->getAccessToken(); - - if (is_null($accessToken)) { - throw new NoActiveAccessTokenException('Tried to access session data without an active access token'); - } - - return $accessToken; - } - - /** - * Issue an access token if the request parameters are valid. - * - * @return array a response object for the protocol in use - */ - public function issueAccessToken() - { - return $this->issuer->issueAccessToken(); - } - - /** - * Get the Auth Code request parameters. - * - * @return array - */ - public function getAuthCodeRequestParams() - { - return $this->authCodeRequestParams; - } - - /** - * Get a single parameter from the auth code request parameters. - * - * @param $key - * @param null $default - * - * @return mixed - */ - public function getAuthCodeRequestParam($key, $default = null) - { - if (array_key_exists($key, $this->authCodeRequestParams)) { - return $this->authCodeRequestParams[$key]; - } - - return $default; - } - - /** - * Check the validity of the auth code request. - * - * @return null a response appropriate for the protocol in use - */ - public function checkAuthCodeRequest() - { - $this->authCodeRequestParams = $this->issuer->getGrantType('authorization_code')->checkAuthorizeParams(); - } - - /** - * Issue an auth code. - * - * @param string $ownerType the auth code owner type - * @param string $ownerId the auth code owner id - * @param array $params additional parameters to merge - * - * @return string the auth code redirect url - */ - public function issueAuthCode($ownerType, $ownerId, $params = []) - { - $params = array_merge($this->authCodeRequestParams, $params); - - return $this->issuer->getGrantType('authorization_code')->newAuthorizeRequest($ownerType, $ownerId, $params); - } - - /** - * Generate a redirect uri when the auth code request is denied by the user. - * - * @return string a correctly formed url to redirect back to - */ - public function authCodeRequestDeniedRedirectUri() - { - $error = new AccessDeniedException(); - - return $this->getRedirectUriGenerator()->make($this->getAuthCodeRequestParam('redirect_uri'), [ - 'error' => $error->errorType, - 'error_description' => $error->getMessage(), - ] - ); - } - - /** - * get the RedirectUri generator instance. - * - * @return RedirectUri - */ - public function getRedirectUriGenerator() - { - if (is_null($this->redirectUriGenerator)) { - $this->redirectUriGenerator = new RedirectUri(); - } - - return $this->redirectUriGenerator; - } - - /** - * Set the RedirectUri generator instance. - * - * @param $redirectUri - */ - public function setRedirectUriGenerator($redirectUri) - { - $this->redirectUriGenerator = $redirectUri; - } - - /** - * Validate a request with an access token in it. - * - * @param bool $httpHeadersOnly whether or not to check only the http headers of the request - * @param string|null $accessToken an access token to validate - * - * @return mixed - */ - public function validateAccessToken($httpHeadersOnly = false, $accessToken = null) - { - return $this->checker->isValidRequest($httpHeadersOnly, $accessToken); - } - - /** - * get the scopes associated with the current request. - * - * @return array - */ - public function getScopes() - { - return $this->getAccessToken()->getScopes(); - } - - /** - * Check if the current request has all the scopes passed. - * - * @param string|array $scope the scope(s) to check for existence - * - * @return bool - */ - public function hasScope($scope) - { - if (is_array($scope)) { - foreach ($scope as $s) { - if ($this->hasScope($s) === false) { - return false; - } - } - - return true; - } - - return $this->getAccessToken()->hasScope($scope); - } - - /** - * Get the resource owner ID of the current request. - * - * @return string - */ - public function getResourceOwnerId() - { - return $this->getAccessToken()->getSession()->getOwnerId(); - } - - /** - * Get the resource owner type of the current request (client or user). - * - * @return string - */ - public function getResourceOwnerType() - { - return $this->getAccessToken()->getSession()->getOwnerType(); - } - - /** - * Get the client id of the current request. - * - * @return string - */ - public function getClientId() - { - return $this->checker->getAccessToken()->getSession()->getClient()->getId(); - } - - /** - * Set the request to use on the issuer and checker. - * - * @param \Symfony\Component\HttpFoundation\Request $request - */ - public function setRequest(Request $request) - { - $this->issuer->setRequest($request); - $this->checker->setRequest($request); - } - - /** - * Set the token type to use. - * - * @param \League\OAuth2\Server\TokenType\TokenTypeInterface $tokenType - */ - public function setTokenType(TokenTypeInterface $tokenType) - { - $this->issuer->setTokenType($tokenType); - $this->checker->setTokenType($tokenType); - } -} diff --git a/src/Entities/AccessToken.php b/src/Entities/AccessToken.php new file mode 100644 index 00000000..85f0c55e --- /dev/null +++ b/src/Entities/AccessToken.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Entities; + +use Carbon\Carbon; +use Illuminate\Database\Eloquent\Model; +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Entities\ScopeEntityInterface; +use League\OAuth2\Server\Entities\Traits\AccessTokenTrait; + +/** + * This is the access token model class. + * + * @author Luca Degasperi + */ +class AccessToken extends Model implements AccessTokenEntityInterface +{ + use AccessTokenTrait; + + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'oauth_access_tokens'; + + /** + * The attributes that should be mutated to dates. + * + * @var string[] + */ + protected $dates = ['expires_at']; + + /** + * Get the token's identifier. + * + * @return string + */ + public function getIdentifier() + { + return $this->token; + } + + /** + * Set the token's identifier. + * + * @param $identifier + */ + public function setIdentifier($identifier) + { + $this->token = $identifier; + } + + /** + * Get the token's expiry date time. + * + * @return \DateTime + */ + public function getExpiryDateTime() + { + return $this->expires_at; + } + + /** + * Set the date time when the token expires. + * + * @param \DateTime $dateTime + */ + public function setExpiryDateTime(\DateTime $dateTime) + { + $this->expires_at = Carbon::instance($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->user_id = $identifier; + } + + /** + * Get the token user's identifier. + * + * @return string|int + */ + public function getUserIdentifier() + { + return $this->user_id; + } + + /** + * 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 \League\OAuth2\Server\Entities\ClientEntityInterface $client + */ + public function setClient(ClientEntityInterface $client) + { + $this->client()->associate($client); + } + + /** + * Associate a scope with the token. + * + * @param \League\OAuth2\Server\Entities\ScopeEntityInterface $scope + */ + public function addScope(ScopeEntityInterface $scope) + { + $this->scopes()->attach($scope); + } + + /** + * Return an array of scopes associated with the token. + * + * @return ScopeEntityInterface[] + */ + public function getScopes() + { + return $this->scopes->toArray(); + } + + public function client() + { + return $this->belongsTo(Client::class); + } + + public function scopes() + { + return $this->belongsToMany(Scope::class, 'oauth_access_token_scopes'); + } + + public function refreshToken() + { + return $this->hasOne(RefreshToken::class); + } +} diff --git a/src/Entities/AuthCode.php b/src/Entities/AuthCode.php new file mode 100644 index 00000000..fe5b3d16 --- /dev/null +++ b/src/Entities/AuthCode.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Entities; + +use Carbon\Carbon; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Auth\User; +use League\OAuth2\Server\Entities\AuthCodeEntityInterface; +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Entities\ScopeEntityInterface; + +/** + * This is the auth code model class. + * + * @author Luca Degasperi + */ +class AuthCode extends Model implements AuthCodeEntityInterface +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'oauth_auth_codes'; + + /** + * The attributes that should be mutated to dates. + * + * @var string[] + */ + protected $dates = ['expires_at']; + + /** + * @return string + */ + public function getRedirectUri() + { + return $this->redirect_uri; + } + + /** + * @param string $uri + */ + public function setRedirectUri($uri) + { + $this->redirect_uri = $uri; + } + + /** + * Get the token's identifier. + * + * @return string + */ + public function getIdentifier() + { + return $this->code; + } + + /** + * Set the token's identifier. + * + * @param $identifier + */ + public function setIdentifier($identifier) + { + $this->code = $identifier; + } + + /** + * Get the token's expiry date time. + * + * @return \DateTime + */ + public function getExpiryDateTime() + { + return $this->expires_at; + } + + /** + * Set the date time when the token expires. + * + * @param \DateTime $dateTime + */ + public function setExpiryDateTime(\DateTime $dateTime) + { + $this->expires_at = Carbon::instance($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->user_id = $identifier; + } + + /** + * Get the token user's identifier. + * + * @return string|int + */ + public function getUserIdentifier() + { + return $this->user_id; + } + + /** + * 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 \League\OAuth2\Server\Entities\ClientEntityInterface $client + */ + public function setClient(ClientEntityInterface $client) + { + $this->client()->associate($client); + } + + /** + * Associate a scope with the token. + * + * @param \League\OAuth2\Server\Entities\ScopeEntityInterface $scope + */ + public function addScope(ScopeEntityInterface $scope) + { + $this->scopes()->attach($scope); + } + + /** + * Return an array of scopes associated with the token. + * + * @return ScopeEntityInterface[] + */ + public function getScopes() + { + return $this->scopes->toArray(); + } + + public function client() + { + return $this->belongsTo(Client::class); + } + + public function user() + { + return $this->belongsTo(User::class); + } + + public function scopes() + { + return $this->belongsToMany(Scope::class, 'oauth_auth_code_scopes'); + } +} diff --git a/src/Entities/Client.php b/src/Entities/Client.php new file mode 100644 index 00000000..89a09c6e --- /dev/null +++ b/src/Entities/Client.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Entities; + +use Illuminate\Database\Eloquent\Model; +use League\OAuth2\Server\Entities\ClientEntityInterface; + +/** + * This is the client model class. + * + * @author Luca Degasperi + */ +class Client extends Model implements ClientEntityInterface +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'oauth_clients'; + + /** + * Get the client's identifier. + * + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Set the client's identifier. + * + * @param $identifier + */ + public function setIdentifier($identifier) + { + $this->identifier = $identifier; + } + + /** + * Get the client's name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set the client's name. + * + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Set the client's redirect uri. + * + * @param string $redirectUri + */ + public function setRedirectUri($redirectUri) + { + $this->redirect_uri = $redirectUri; + } + + /** + * Returns the registered redirect URI (as a string). + * + * Alternatively return an indexed array of redirect URIs. + * + * @return string|string[] + */ + public function getRedirectUri() + { + return $this->redirectUris->map(function ($item, $key) { + return $item->uri; + })->toArray(); + } + + public function accessTokens() + { + return $this->hasMany(AccessToken::class); + } + + public function authCodes() + { + return $this->hasMany(AuthCode::class); + } + + public function scopes() + { + return $this->belongsToMany(Scope::class, 'oauth_client_scopes'); + } + + public function redirectUris() + { + return $this->hasMany(RedirectUri::class); + } +} diff --git a/src/Entities/RedirectUri.php b/src/Entities/RedirectUri.php new file mode 100644 index 00000000..716b81ee --- /dev/null +++ b/src/Entities/RedirectUri.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Entities; + +use Illuminate\Database\Eloquent\Model; + +/** + * This is the redirect uri model class. + * + * @author Luca Degasperi + */ +class RedirectUri extends Model +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'oauth_client_redirect_uris'; + + public function client() + { + return $this->belongsTo(Client::class); + } +} diff --git a/src/Entities/RefreshToken.php b/src/Entities/RefreshToken.php new file mode 100644 index 00000000..f34cfcdb --- /dev/null +++ b/src/Entities/RefreshToken.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Entities; + +use Carbon\Carbon; +use Illuminate\Database\Eloquent\Model; +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; + +/** + * This is the refresh token model class. + * + * @author Luca Degasperi + */ +class RefreshToken extends Model implements RefreshTokenEntityInterface +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'oauth_refresh_tokens'; + + /** + * The attributes that should be mutated to dates. + * + * @var string[] + */ + protected $dates = ['expires_at']; + + /** + * Get the token's identifier. + * + * @return string + */ + public function getIdentifier() + { + return $this->token; + } + + /** + * Set the token's identifier. + * + * @param $identifier + */ + public function setIdentifier($identifier) + { + $this->token = $identifier; + } + + /** + * Get the token's expiry date time. + * + * @return \DateTime + */ + public function getExpiryDateTime() + { + return $this->expires_at; + } + + /** + * Set the date time when the token expires. + * + * @param \DateTime $dateTime + */ + public function setExpiryDateTime(\DateTime $dateTime) + { + $this->expires_at = Carbon::instance($dateTime); + } + + /** + * Set the access token that the refresh token was associated with. + * + * @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken + */ + public function setAccessToken(AccessTokenEntityInterface $accessToken) + { + $this->accessToken()->associate($accessToken); + } + + /** + * Get the access token that the refresh token was originally associated with. + * + * @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface + */ + public function getAccessToken() + { + return $this->accessToken; + } + + public function accessToken() + { + return $this->belongsTo(AccessToken::class); + } +} diff --git a/src/Entities/Scope.php b/src/Entities/Scope.php new file mode 100644 index 00000000..b1775458 --- /dev/null +++ b/src/Entities/Scope.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Entities; + +use Illuminate\Database\Eloquent\Model; +use League\OAuth2\Server\Entities\ScopeEntityInterface; + +/** + * This is the scope model class. + * + * @author Luca Degasperi + */ +class Scope extends Model implements ScopeEntityInterface +{ + /** + * The table associated with the model. + * + * @var string + */ + protected $table = 'oauth_scopes'; + + /** + * Get the scope's identifier. + * + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Set the scope's identifier. + * + * @param $identifier + */ + public function setIdentifier($identifier) + { + $this->identifier = $identifier; + } + + public function accessTokens() + { + return $this->belongsToMany(AccessToken::class, 'oauth_access_token_scopes'); + } + + public function authCodes() + { + return $this->belongsToMany(AuthCode::class, 'oauth_auth_code_scopes'); + } + + public function clients() + { + return $this->belongsToMany(Client::class, 'oauth_client_scopes'); + } +} diff --git a/src/Exceptions/NoActiveAccessTokenException.php b/src/Exceptions/NoActiveAccessTokenException.php deleted file mode 100644 index 3546c53c..00000000 --- a/src/Exceptions/NoActiveAccessTokenException.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Exceptions; - -use Exception; - -/** - * This is the no active access token exception class. - * - * @author Troy Pavlek - */ -class NoActiveAccessTokenException extends Exception -{ - // -} diff --git a/src/Facades/Authorizer.php b/src/Facades/Authorizer.php deleted file mode 100644 index 0ca046d1..00000000 --- a/src/Facades/Authorizer.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Facades; - -use Illuminate\Support\Facades\Facade; - -/** - * This is the authorizer facade class. - * - * @author Luca Degasperi - */ -class Authorizer extends Facade -{ - /** - * Get the registered name of the component. - * - * @return string - */ - protected static function getFacadeAccessor() - { - return 'oauth2-server.authorizer'; - } -} diff --git a/src/Guard.php b/src/Guard.php new file mode 100644 index 00000000..76e72601 --- /dev/null +++ b/src/Guard.php @@ -0,0 +1,229 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server; + +use Illuminate\Auth\GuardHelpers; +use Illuminate\Contracts\Auth\Guard as IlluminateGuard; +use Illuminate\Contracts\Auth\UserProvider; +use Illuminate\Http\Request; +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\Repositories\ClientRepositoryInterface; +use League\OAuth2\Server\ResourceServer; +use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory; + +/** + * This is the guard class. + * + * @author Luca Degasperi + */ +class Guard implements IlluminateGuard +{ + use GuardHelpers; + + /** + * @var Request + */ + private $request; + + /** + * @var ClientEntityInterface + */ + private $client = null; + + /** + * @var array + */ + private $scopes = null; + + /** + * @var string + */ + private $accessToken = null; + + /** + * @var OAuthServerException + */ + private $exception = null; + + /** + * @var ResourceServer + */ + private $resourceServer; + + /** + * @var ClientRepositoryInterface + */ + private $clientRepository; + + /** + * Guard constructor. + * + * @param UserProvider $provider + * @param ResourceServer $resourceServer + * @param Request $request + * @param ClientRepositoryInterface $clientRepository + */ + public function __construct( + UserProvider $provider, + ResourceServer $resourceServer, + Request $request, + ClientRepositoryInterface $clientRepository + ) { + $this->provider = $provider; + $this->resourceServer = $resourceServer; + $psr7Factory = new DiactorosFactory(); + $this->request = $psr7Factory->createRequest($request); + $this->clientRepository = $clientRepository; + } + + /** + * Get the currently authenticated user. + * + * @return \Illuminate\Contracts\Auth\Authenticatable|null + */ + public function user() + { + if (!is_null($this->user)) { + return $this->user; + } + + $this->parseRequest(); + + return $this->user; + } + + /** + * Validate a user's credentials. + * + * @param array $credentials + * + * @return bool + */ + public function validate(array $credentials = []) + { + $user = $this->provider->retrieveByCredentials($credentials); + + if ($this->hasValidCredentials($user, $credentials)) { + return true; + } + + return false; + } + + /** + * Determine if the user matches the credentials. + * + * @param mixed $user + * @param array $credentials + * + * @return bool + */ + protected function hasValidCredentials($user, $credentials) + { + return !is_null($user) && $this->provider->validateCredentials($user, $credentials); + } + + public function scopes() + { + if (!is_null($this->scopes)) { + return $this->scopes; + } + + $this->parseRequest(); + + return $this->scopes; + } + + public function accessToken() + { + if (!is_null($this->accessToken)) { + return $this->accessToken; + } + + $this->parseRequest(); + + return $this->accessToken; + } + + /** + * Get the client doing the request. + */ + public function client() + { + if (!is_null($this->client)) { + return $this->client; + } + + $this->parseRequest(); + + return $this->client; + } + + public function setClient(ClientEntityInterface $client) + { + $this->client = $client; + + return $this; + } + + /** + * Set the current request instance. + * + * @param Request $request + * + * @return $this + */ + public function setRequest(Request $request) + { + $psr7Factory = new DiactorosFactory(); + $this->request = $psr7Factory->createRequest($request); + + return $this; + } + + protected function parseRequest() + { + try { + $this->request = $this->resourceServer->validateAuthenticatedRequest($this->request); + + $this->user = $this->provider->retrieveById($this->request->getAttribute('oauth_user_id')); + // TODO: open PR for the null workaround + $this->client = $this->clientRepository->getClientEntity($this->request->getAttribute('oauth_client_id'), null, null, false); + $this->scopes = $this->request->getAttribute('oauth_scopes', []); + $this->accessToken = $this->request->getAttribute('oauth_access_token_id'); + } catch (OAuthServerException $exception) { + $this->user = null; + $this->client = null; + $this->accessToken = null; + $this->exception = $exception; + } + // TODO: catch other exceptions as well. + } + + public function getException() + { + return $this->exception; + } + + public function getResourceServer() + { + return $this->resourceServer; + } + + public function setResourceServer(ResourceServer $server) + { + $this->resourceServer = $server; + + return $this; + } +} diff --git a/src/Lumen/OAuth2ServerServiceProvider.php b/src/Lumen/OAuth2ServerServiceProvider.php deleted file mode 100644 index 897cff99..00000000 --- a/src/Lumen/OAuth2ServerServiceProvider.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Lumen; - -use LucaDegasperi\OAuth2Server\OAuth2ServerServiceProvider as ServiceProvider; - -/** - * This is the Lumen oauth server service provider class. - * - * @deprecated since version 5.1. Use the base OAuth2ServerServiceProvider instead. - * - * @author Luca Degasperi - */ -class OAuth2ServerServiceProvider extends ServiceProvider -{ - // -} diff --git a/src/Middleware/CheckAuthCodeRequestMiddleware.php b/src/Middleware/CheckAuthCodeRequestMiddleware.php deleted file mode 100644 index 7e3318b9..00000000 --- a/src/Middleware/CheckAuthCodeRequestMiddleware.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Middleware; - -use Closure; -use LucaDegasperi\OAuth2Server\Authorizer; - -/** - * This is the check auth code request middleware class. - * - * @author Luca Degasperi - */ -class CheckAuthCodeRequestMiddleware -{ - /** - * The authorizer instance. - * - * @var \LucaDegasperi\OAuth2Server\Authorizer - */ - protected $authorizer; - - /** - * Create a new check auth code request middleware instance. - * - * @param \LucaDegasperi\OAuth2Server\Authorizer $authorizer - */ - public function __construct(Authorizer $authorizer) - { - $this->authorizer = $authorizer; - } - - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * - * @return mixed - */ - public function handle($request, Closure $next) - { - $this->authorizer->setRequest($request); - - $this->authorizer->checkAuthCodeRequest(); - - return $next($request); - } -} diff --git a/src/Middleware/OAuthClientOwnerMiddleware.php b/src/Middleware/OAuthClientOwnerMiddleware.php deleted file mode 100644 index c1cf55e6..00000000 --- a/src/Middleware/OAuthClientOwnerMiddleware.php +++ /dev/null @@ -1,62 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Middleware; - -use Closure; -use League\OAuth2\Server\Exception\AccessDeniedException; -use LucaDegasperi\OAuth2Server\Authorizer; - -/** - * This is the oauth client middleware class. - * - * @author Vincent Klaiber - */ -class OAuthClientOwnerMiddleware -{ - /** - * The Authorizer instance. - * - * @var \LucaDegasperi\OAuth2Server\Authorizer - */ - protected $authorizer; - - /** - * Create a new oauth client middleware instance. - * - * @param \LucaDegasperi\OAuth2Server\Authorizer $authorizer - */ - public function __construct(Authorizer $authorizer) - { - $this->authorizer = $authorizer; - } - - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * - * @throws \League\OAuth2\Server\Exception\AccessDeniedException - * - * @return mixed - */ - public function handle($request, Closure $next) - { - $this->authorizer->setRequest($request); - - if ($this->authorizer->getResourceOwnerType() !== 'client') { - throw new AccessDeniedException(); - } - - return $next($request); - } -} diff --git a/src/Middleware/OAuthExceptionHandlerMiddleware.php b/src/Middleware/OAuthExceptionHandlerMiddleware.php deleted file mode 100644 index 003aaddc..00000000 --- a/src/Middleware/OAuthExceptionHandlerMiddleware.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Middleware; - -use Closure; -use Illuminate\Http\JsonResponse; -use League\OAuth2\Server\Exception\OAuthException; - -/** - * This is the exception handler middleware class. - * - * @author Luca Degasperi - */ -class OAuthExceptionHandlerMiddleware -{ - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * - * @return mixed - */ - public function handle($request, Closure $next) - { - try { - return $next($request); - } catch (OAuthException $e) { - $data = [ - 'error' => $e->errorType, - 'error_description' => $e->getMessage(), - ]; - - return new JsonResponse($data, $e->httpStatusCode, $e->getHttpHeaders()); - } - } -} diff --git a/src/Middleware/OAuthMiddleware.php b/src/Middleware/OAuthMiddleware.php deleted file mode 100644 index 9f921a2f..00000000 --- a/src/Middleware/OAuthMiddleware.php +++ /dev/null @@ -1,91 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Middleware; - -use Closure; -use League\OAuth2\Server\Exception\InvalidScopeException; -use LucaDegasperi\OAuth2Server\Authorizer; - -/** - * This is the oauth middleware class. - * - * @author Luca Degasperi - */ -class OAuthMiddleware -{ - /** - * The Authorizer instance. - * - * @var \LucaDegasperi\OAuth2Server\Authorizer - */ - protected $authorizer; - - /** - * Whether or not to check the http headers only for an access token. - * - * @var bool - */ - protected $httpHeadersOnly = false; - - /** - * Create a new oauth middleware instance. - * - * @param \LucaDegasperi\OAuth2Server\Authorizer $authorizer - * @param bool $httpHeadersOnly - */ - public function __construct(Authorizer $authorizer, $httpHeadersOnly = false) - { - $this->authorizer = $authorizer; - $this->httpHeadersOnly = $httpHeadersOnly; - } - - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @param string|null $scopesString - * - * @throws \League\OAuth2\Server\Exception\InvalidScopeException - * - * @return mixed - */ - public function handle($request, Closure $next, $scopesString = null) - { - $scopes = []; - - if (!is_null($scopesString)) { - $scopes = explode('+', $scopesString); - } - - $this->authorizer->setRequest($request); - - $this->authorizer->validateAccessToken($this->httpHeadersOnly); - $this->validateScopes($scopes); - - return $next($request); - } - - /** - * Validate the scopes. - * - * @param $scopes - * - * @throws \League\OAuth2\Server\Exception\InvalidScopeException - */ - public function validateScopes($scopes) - { - if (!empty($scopes) && !$this->authorizer->hasScope($scopes)) { - throw new InvalidScopeException(implode(',', $scopes)); - } - } -} diff --git a/src/Middleware/OAuthUserOwnerMiddleware.php b/src/Middleware/OAuthUserOwnerMiddleware.php deleted file mode 100644 index fd05929c..00000000 --- a/src/Middleware/OAuthUserOwnerMiddleware.php +++ /dev/null @@ -1,62 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Middleware; - -use Closure; -use League\OAuth2\Server\Exception\AccessDeniedException; -use LucaDegasperi\OAuth2Server\Authorizer; - -/** - * This is the oauth user middleware class. - * - * @author Vincent Klaiber - */ -class OAuthUserOwnerMiddleware -{ - /** - * The Authorizer instance. - * - * @var \LucaDegasperi\OAuth2Server\Authorizer - */ - protected $authorizer; - - /** - * Create a new oauth user middleware instance. - * - * @param \LucaDegasperi\OAuth2Server\Authorizer $authorizer - */ - public function __construct(Authorizer $authorizer) - { - $this->authorizer = $authorizer; - } - - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * - * @throws \League\OAuth2\Server\Exception\AccessDeniedException - * - * @return mixed - */ - public function handle($request, Closure $next) - { - $this->authorizer->setRequest($request); - - if ($this->authorizer->getResourceOwnerType() !== 'user') { - throw new AccessDeniedException(); - } - - return $next($request); - } -} diff --git a/src/OAuth2ServerServiceProvider.php b/src/OAuth2ServerServiceProvider.php index c5fd9dd2..ccf8ac48 100644 --- a/src/OAuth2ServerServiceProvider.php +++ b/src/OAuth2ServerServiceProvider.php @@ -1,7 +1,7 @@ * @@ -11,27 +11,27 @@ namespace LucaDegasperi\OAuth2Server; -use Illuminate\Contracts\Foundation\Application; -use Illuminate\Foundation\Application as LaravelApplication; +use DateInterval; use Illuminate\Support\ServiceProvider; -use Laravel\Lumen\Application as LumenApplication; use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\CryptKey; +use League\OAuth2\Server\Grant\AuthCodeGrant; +use League\OAuth2\Server\Grant\ImplicitGrant; +use League\OAuth2\Server\Grant\PasswordGrant; +use League\OAuth2\Server\Grant\RefreshTokenGrant; +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\ResourceServer; -use League\OAuth2\Server\Storage\AccessTokenInterface; -use League\OAuth2\Server\Storage\AuthCodeInterface; -use League\OAuth2\Server\Storage\ClientInterface; -use League\OAuth2\Server\Storage\RefreshTokenInterface; -use League\OAuth2\Server\Storage\ScopeInterface; -use League\OAuth2\Server\Storage\SessionInterface; -use LucaDegasperi\OAuth2Server\Middleware\CheckAuthCodeRequestMiddleware; -use LucaDegasperi\OAuth2Server\Middleware\OAuthClientOwnerMiddleware; -use LucaDegasperi\OAuth2Server\Middleware\OAuthMiddleware; -use LucaDegasperi\OAuth2Server\Middleware\OAuthUserOwnerMiddleware; /** * This is the oauth2 server service provider class. * * @author Luca Degasperi + * @author Vincent Klaiber */ class OAuth2ServerServiceProvider extends ServiceProvider { @@ -42,26 +42,24 @@ class OAuth2ServerServiceProvider extends ServiceProvider */ public function boot() { - $this->setupConfig($this->app); - $this->setupMigrations($this->app); + $this->setupConfig(); + $this->setupMigrations(); + + $this->bootGuard(); + + $this->loadViewsFrom(__DIR__.'/../resources/views', 'oauth2server'); } /** * Setup the config. * - * @param \Illuminate\Contracts\Foundation\Application $app - * * @return void */ - protected function setupConfig(Application $app) + protected function setupConfig() { $source = realpath(__DIR__.'/../config/oauth2.php'); - if ($app instanceof LaravelApplication && $app->runningInConsole()) { - $this->publishes([$source => config_path('oauth2.php')]); - } elseif ($app instanceof LumenApplication) { - $app->configure('oauth2'); - } + $this->publishes([$source => config_path('oauth2.php')]); $this->mergeConfigFrom($source, 'oauth2'); } @@ -69,17 +67,13 @@ protected function setupConfig(Application $app) /** * Setup the migrations. * - * @param \Illuminate\Contracts\Foundation\Application $app - * * @return void */ - protected function setupMigrations(Application $app) + protected function setupMigrations() { $source = realpath(__DIR__.'/../database/migrations/'); - if ($app instanceof LaravelApplication && $app->runningInConsole()) { - $this->publishes([$source => database_path('migrations')], 'migrations'); - } + $this->publishes([$source => database_path('migrations')], 'migrations'); } /** @@ -89,111 +83,95 @@ protected function setupMigrations(Application $app) */ public function register() { - $this->registerAuthorizer($this->app); - $this->registerMiddlewareBindings($this->app); + $this->registerGrantTypes(); + $this->registerServer(); } - /** - * Register the Authorization server with the IoC container. - * - * @param \Illuminate\Contracts\Foundation\Application $app - * - * @return void - */ - public function registerAuthorizer(Application $app) + protected function registerServer() { - $app->singleton('oauth2-server.authorizer', function ($app) { - $config = $app['config']->get('oauth2'); - $issuer = $app->make(AuthorizationServer::class) - ->setClientStorage($app->make(ClientInterface::class)) - ->setSessionStorage($app->make(SessionInterface::class)) - ->setAuthCodeStorage($app->make(AuthCodeInterface::class)) - ->setAccessTokenStorage($app->make(AccessTokenInterface::class)) - ->setRefreshTokenStorage($app->make(RefreshTokenInterface::class)) - ->setScopeStorage($app->make(ScopeInterface::class)) - ->requireScopeParam($config['scope_param']) - ->setDefaultScope($config['default_scope']) - ->requireStateParam($config['state_param']) - ->setScopeDelimiter($config['scope_delimiter']) - ->setAccessTokenTTL($config['access_token_ttl']); - - // add the supported grant types to the authorization server - foreach ($config['grant_types'] as $grantIdentifier => $grantParams) { - $grant = $app->make($grantParams['class']); - $grant->setAccessTokenTTL($grantParams['access_token_ttl']); - - if (array_key_exists('callback', $grantParams)) { - list($className, $method) = array_pad(explode('@', $grantParams['callback']), 2, 'verify'); - $verifier = $app->make($className); - $grant->setVerifyCredentialsCallback([$verifier, $method]); - } - - if (array_key_exists('auth_token_ttl', $grantParams)) { - $grant->setAuthTokenTTL($grantParams['auth_token_ttl']); - } - - if (array_key_exists('refresh_token_ttl', $grantParams)) { - $grant->setRefreshTokenTTL($grantParams['refresh_token_ttl']); - } - - if (array_key_exists('rotate_refresh_tokens', $grantParams)) { - $grant->setRefreshTokenRotation($grantParams['rotate_refresh_tokens']); - } - - $issuer->addGrantType($grant); + $this->app->singleton(AuthorizationServer::class, function ($app) { + $server = new AuthorizationServer( + $app->make(ClientRepositoryInterface::class), + $app->make(AccessTokenRepositoryInterface::class), + $app->make(ScopeRepositoryInterface::class), + new CryptKey($app['config']->get('oauth2.private_key_path'), $app['config']->get('oauth2.key_passphrase')), + new CryptKey($app['config']->get('oauth2.public_key_path'), $app['config']->get('oauth2.key_passphrase')), + $app->make($app['config']->get('oauth2.response_type')), + $app->make($app['config']->get('oauth2.authorization_validator')) + ); + + foreach ($app['config']->get('oauth2.grant_types') as $grantType) { + $server->enableGrantType( + $app->make($grantType['class'], $grantType), + new DateInterval('PT'.$grantType['access_token_ttl'].'S') + ); } - $checker = $app->make(ResourceServer::class); - - $authorizer = new Authorizer($issuer, $checker); - $authorizer->setRequest($app['request']); - $authorizer->setTokenType($app->make($config['token_type'])); + return $server; + }); - $app->refresh('request', $authorizer, 'setRequest'); + $this->app->singleton(ResourceServer::class, function ($app) { + $server = new ResourceServer( + $app->make(AccessTokenRepositoryInterface::class), + new CryptKey($app['config']->get('oauth2.public_key_path'), $app['config']->get('oauth2.key_passphrase')), + $app->make($app['config']->get('oauth2.authorization_validator')) + ); - return $authorizer; + return $server; }); - - $app->alias('oauth2-server.authorizer', Authorizer::class); } - /** - * Register the Middleware to the IoC container because - * some middleware need additional parameters. - * - * @param \Illuminate\Contracts\Foundation\Application $app - * - * @return void - */ - public function registerMiddlewareBindings(Application $app) + protected function registerGrantTypes() { - $app->singleton(CheckAuthCodeRequestMiddleware::class, function ($app) { - return new CheckAuthCodeRequestMiddleware($app['oauth2-server.authorizer']); - }); + $this->app->bind(AuthCodeGrant::class, function ($app, $parameters = []) { + $grant = new AuthCodeGrant( + $app->make(AuthCodeRepositoryInterface::class), + $app->make(RefreshTokenRepositoryInterface::class), + new DateInterval('PT'.$parameters['auth_code_ttl'].'S') + ); + + if (array_key_exists('code_exchange_proof', $parameters)) { + if ($parameters['code_exchange_proof'] === true) { + $grant->enableCodeExchangeProof(); + } + } - $app->singleton(OAuthMiddleware::class, function ($app) { - $httpHeadersOnly = $app['config']->get('oauth2.http_headers_only'); + return $grant; + }); - return new OAuthMiddleware($app['oauth2-server.authorizer'], $httpHeadersOnly); + $this->app->bind(ImplicitGrant::class, function ($app, $parameters = []) { + return new ImplicitGrant( + $app->make(UserRepositoryInterface::class) + ); }); - $app->singleton(OAuthClientOwnerMiddleware::class, function ($app) { - return new OAuthClientOwnerMiddleware($app['oauth2-server.authorizer']); + $this->app->bind(PasswordGrant::class, function ($app, $parameters = []) { + return new PasswordGrant( + $app->make(UserRepositoryInterface::class), + $app->make(RefreshTokenRepositoryInterface::class) + ); }); - $app->singleton(OAuthUserOwnerMiddleware::class, function ($app) { - return new OAuthUserOwnerMiddleware($app['oauth2-server.authorizer']); + $this->app->bind(RefreshTokenGrant::class, function ($app, $parameters = []) { + return new RefreshTokenGrant( + $app->make(RefreshTokenRepositoryInterface::class) + ); }); } - /** - * Get the services provided by the provider. - * - * @return string[] - * @codeCoverageIgnore - */ - public function provides() + protected function bootGuard() { - return ['oauth2-server.authorizer']; + $this->app['auth']->extend('oauth2', function ($app, $name, array $config) { + $guard = new Guard( + $app['auth']->createUserProvider($config['provider']), + $app->make(ResourceServer::class), + $app['request'], + $app->make(ClientRepositoryInterface::class) + ); + + $app->refresh('request', $guard, 'setRequest'); + + return $guard; + }); } } diff --git a/src/Repositories/AccessTokenRepository.php b/src/Repositories/AccessTokenRepository.php new file mode 100644 index 00000000..bd7ae5ce --- /dev/null +++ b/src/Repositories/AccessTokenRepository.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Repositories; + +use League\OAuth2\Server\Entities\AccessTokenEntityInterface; +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface; +use LucaDegasperi\OAuth2Server\Entities\AccessToken; + +/** + * This is the access token repository class. + * + * @author Luca Degasperi + */ +class AccessTokenRepository implements AccessTokenRepositoryInterface +{ + /** + * Create a new access token. + * + * @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity + * @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes + * @param mixed $userIdentifier + * + * @return AccessTokenEntityInterface + */ + public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null) + { + return new AccessToken(); + } + + /** + * Persists a new access token to permanent storage. + * + * @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessTokenEntity + */ + public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity) + { + $accessTokenEntity->save(); + } + + /** + * Revoke an access token. + * + * @param string $tokenId + */ + public function revokeAccessToken($tokenId) + { + AccessToken::where('token', $tokenId)->delete(); + } + + /** + * 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) + { + return AccessToken::where('token', $tokenId)->count() === 0; + } +} diff --git a/src/Repositories/AuthCodeRepository.php b/src/Repositories/AuthCodeRepository.php new file mode 100644 index 00000000..bca6dba0 --- /dev/null +++ b/src/Repositories/AuthCodeRepository.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Repositories; + +use League\OAuth2\Server\Entities\AuthCodeEntityInterface; +use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface; +use LucaDegasperi\OAuth2Server\Entities\AuthCode; + +/** + * This is the auth code repository class. + * + * @author Luca Degasperi + */ +class AuthCodeRepository implements AuthCodeRepositoryInterface +{ + /** + * Creates a new AuthCode. + * + * @return \League\OAuth2\Server\Entities\AuthCodeEntityInterface + */ + public function getNewAuthCode() + { + return new AuthCode(); + } + + /** + * Persists a new auth code to permanent storage. + * + * @param \League\OAuth2\Server\Entities\AuthCodeEntityInterface $authCodeEntity + */ + public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity) + { + $authCodeEntity->save(); + } + + /** + * Revoke an auth code. + * + * @param string $codeId + */ + public function revokeAuthCode($codeId) + { + AuthCode::where('code', $codeId)->delete(); + } + + /** + * 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) + { + return AuthCode::where('code', $codeId)->count() === 0; + } +} diff --git a/src/Repositories/ClientRepository.php b/src/Repositories/ClientRepository.php new file mode 100644 index 00000000..9210f301 --- /dev/null +++ b/src/Repositories/ClientRepository.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Repositories; + +use League\OAuth2\Server\Repositories\ClientRepositoryInterface; +use LucaDegasperi\OAuth2Server\Entities\Client; + +/** + * This is the client repository class. + * + * @author Luca Degasperi + */ +class ClientRepository implements ClientRepositoryInterface +{ + /** + * 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 unless the client + * is confidential + * + * @return \League\OAuth2\Server\Entities\ClientEntityInterface + */ + public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true) + { + // TODO: check if the client can use the $grantType + $query = Client::where('identifier', $clientIdentifier); + + if ($mustValidateSecret) { + $query->where('secret', $clientSecret); + } + + return $query->first(); + } +} diff --git a/src/Repositories/RefreshTokenRepository.php b/src/Repositories/RefreshTokenRepository.php new file mode 100644 index 00000000..b9500141 --- /dev/null +++ b/src/Repositories/RefreshTokenRepository.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Repositories; + +use League\OAuth2\Server\Entities\RefreshTokenEntityInterface; +use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface; +use LucaDegasperi\OAuth2Server\Entities\RefreshToken; + +/** + * This is the refresh token repository class. + * + * @author Luca Degasperi + */ +class RefreshTokenRepository implements RefreshTokenRepositoryInterface +{ + /** + * Creates a new refresh token. + * + * @return RefreshTokenEntityInterface + */ + public function getNewRefreshToken() + { + return new RefreshToken(); + } + + /** + * Create a new refresh token_name. + * + * @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshTokenEntity + */ + public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity) + { + $refreshTokenEntity->save(); + } + + /** + * Revoke the refresh token. + * + * @param string $tokenId + */ + public function revokeRefreshToken($tokenId) + { + RefreshToken::where('token', $tokenId)->delete(); + } + + /** + * 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) + { + return RefreshToken::where('token', $tokenId)->count() === 0; + } +} diff --git a/src/Repositories/RepositoriesServiceProvider.php b/src/Repositories/RepositoriesServiceProvider.php new file mode 100644 index 00000000..bf16c952 --- /dev/null +++ b/src/Repositories/RepositoriesServiceProvider.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Repositories; + +use Illuminate\Support\ServiceProvider; +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; + +/** + * This is the repository service provider class. + * + * @author Luca Degasperi + */ +class RepositoriesServiceProvider extends ServiceProvider +{ + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + $this->app->bind(AccessTokenRepositoryInterface::class, AccessTokenRepository::class); + $this->app->bind(AuthCodeRepositoryInterface::class, AuthCodeRepository::class); + $this->app->bind(ClientRepositoryInterface::class, ClientRepository::class); + $this->app->bind(RefreshTokenRepositoryInterface::class, RefreshTokenRepository::class); + $this->app->bind(UserRepositoryInterface::class, UserRepository::class); + $this->app->bind(ScopeRepositoryInterface::class, function ($app) { + return new ScopeRepository($app['config']->get('oauth2.default_scopes', [])); + }); + } +} diff --git a/src/Repositories/ScopeRepository.php b/src/Repositories/ScopeRepository.php new file mode 100644 index 00000000..bb0e20b6 --- /dev/null +++ b/src/Repositories/ScopeRepository.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Repositories; + +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Entities\ScopeEntityInterface; +use League\OAuth2\Server\Repositories\ScopeRepositoryInterface; +use LucaDegasperi\OAuth2Server\Entities\Scope; + +/** + * This is the scope repository class. + * + * @author Luca Degasperi + */ +class ScopeRepository implements ScopeRepositoryInterface +{ + /** + * @var string + */ + private $defaultScopes; + + public function __construct($defaultScopes = []) + { + $this->defaultScopes = $defaultScopes; + } + + /** + * Return information about a scope. + * + * @param string $identifier The scope identifier + * + * @return \League\OAuth2\Server\Entities\ScopeEntityInterface + */ + public function getScopeEntityByIdentifier($identifier) + { + return Scope::where('identifier', $identifier)->first(); + } + + /** + * Given a client, grant type and optional user identifier validate the set of scopes requested are valid and optionally + * append additional scopes or remove requested scopes. + * + * @param ScopeEntityInterface[] $scopes + * @param string $grantType + * @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity + * @param null|string $userIdentifier + * + * @return \League\OAuth2\Server\Entities\ScopeEntityInterface[] + */ + public function finalizeScopes(array $scopes, $grantType, ClientEntityInterface $clientEntity, $userIdentifier = null) + { + if (!$clientEntity->has('scopes')) { + return $scopes; + } + + $clientScopes = $clientEntity->scopes; + + // TODO: this can be simplified imho. + $scopes = array_filter($scopes, function ($scope) use ($clientScopes) { + $identifier = $scope->getItentifier(); + + return $clientScopes->contains(function ($key, $value) use ($identifier) { + $value->getIdentifer() == $identifier; + }); + }); + + // TODO: add possibility to append scopes from clients or grants + + return $scopes; + } +} diff --git a/src/Repositories/UserRepository.php b/src/Repositories/UserRepository.php new file mode 100644 index 00000000..c4de9963 --- /dev/null +++ b/src/Repositories/UserRepository.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Repositories; + +use Illuminate\Auth\AuthManager; +use League\OAuth2\Server\Entities\ClientEntityInterface; +use League\OAuth2\Server\Repositories\UserRepositoryInterface; + +/** + * This is the user repository class. + * + * @author Luca Degasperi + */ +class UserRepository implements UserRepositoryInterface +{ + /** + * @var AuthManager + */ + private $authManager; + + public function __construct(AuthManager $authManager) + { + $this->authManager = $authManager; + } + + /** + * Get a user entity. + * + * @param string $username + * @param string $password + * @param string $grantType The grant type used + * @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity + * + * @return \League\OAuth2\Server\Entities\UserEntityInterface + */ + public function getUserEntityByUserCredentials( + $username, + $password, + $grantType, + ClientEntityInterface $clientEntity + ) { + + // TODO: allow developer to choose which credentials key to use + $credentials = [ + 'email' => $username, + 'password' => $password, + ]; + + $user = $this->authManager->getProvider()->retrieveByCredentials($credentials); + + if (is_null($user)) { + return; + } + + // TODO: validate grant type and client for user + + return $this->authManager->getProvider()->validateCredentials($user, $credentials) ? $user : null; + } +} diff --git a/src/Storage/AbstractFluentAdapter.php b/src/Storage/AbstractFluentAdapter.php deleted file mode 100644 index 13d52585..00000000 --- a/src/Storage/AbstractFluentAdapter.php +++ /dev/null @@ -1,90 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Storage; - -use Illuminate\Database\ConnectionResolverInterface as Resolver; -use League\OAuth2\Server\Storage\AbstractStorage; - -/** - * This is the abstract fluent adapter class. - * - * @author Luca Degasperi - */ -abstract class AbstractFluentAdapter extends AbstractStorage -{ - /** - * The connection resolver instance. - * - * @var \Illuminate\Database\ConnectionResolverInterface - */ - protected $resolver; - - /** - * The connection name. - * - * @var string - */ - protected $connectionName; - - /** - * Create a new abstract fluent adapter instance. - * - * @param \Illuminate\Database\ConnectionResolverInterface $resolver - */ - public function __construct(Resolver $resolver) - { - $this->resolver = $resolver; - $this->connectionName = null; - } - - /** - * Set the resolver. - * - * @param \Illuminate\Database\ConnectionResolverInterface $resolver - */ - public function setResolver(Resolver $resolver) - { - $this->resolver = $resolver; - } - - /** - * Get the resolver. - * - * @return \Illuminate\Database\ConnectionResolverInterface - */ - public function getResolver() - { - return $this->resolver; - } - - /** - * Set the connection name. - * - * @param string $connectionName - * - * @return void - */ - public function setConnectionName($connectionName) - { - $this->connectionName = $connectionName; - } - - /** - * Get the connection. - * - * @return \Illuminate\Database\ConnectionInterface - */ - protected function getConnection() - { - return $this->resolver->connection($this->connectionName); - } -} diff --git a/src/Storage/FluentAccessToken.php b/src/Storage/FluentAccessToken.php deleted file mode 100644 index bcc4b74b..00000000 --- a/src/Storage/FluentAccessToken.php +++ /dev/null @@ -1,149 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Storage; - -use Carbon\Carbon; -use League\OAuth2\Server\Entity\AccessTokenEntity; -use League\OAuth2\Server\Entity\ScopeEntity; -use League\OAuth2\Server\Storage\AccessTokenInterface; - -/** - * This is the fluent access token class. - * - * @author Luca Degasperi - */ -class FluentAccessToken extends AbstractFluentAdapter implements AccessTokenInterface -{ - /** - * Get an instance of Entities\AccessToken. - * - * @param string $token The access token - * - * @return null|AbstractTokenEntity - */ - public function get($token) - { - $result = $this->getConnection()->table('oauth_access_tokens') - ->where('oauth_access_tokens.id', $token) - ->first(); - - if (is_null($result)) { - return; - } - - return (new AccessTokenEntity($this->getServer())) - ->setId($result->id) - ->setExpireTime((int) $result->expire_time); - } - - /* - public function getByRefreshToken(RefreshTokenEntity $refreshToken) - { - $result = $this->getConnection()->table('oauth_access_tokens') - ->select('oauth_access_tokens.*') - ->join('oauth_refresh_tokens', 'oauth_access_tokens.id', '=', 'oauth_refresh_tokens.access_token_id') - ->where('oauth_refresh_tokens.id', $refreshToken->getId()) - ->first(); - - if (is_null($result)) { - return null; - } - - return (new AccessTokenEntity($this->getServer())) - ->setId($result->id) - ->setExpireTime((int)$result->expire_time); - } - */ - - /** - * Get the scopes for an access token. - * - * @param \League\OAuth2\Server\Entity\AccessTokenEntity $token The access token - * - * @return array Array of \League\OAuth2\Server\Entity\ScopeEntity - */ - public function getScopes(AccessTokenEntity $token) - { - $result = $this->getConnection()->table('oauth_access_token_scopes') - ->select('oauth_scopes.*') - ->join('oauth_scopes', 'oauth_access_token_scopes.scope_id', '=', 'oauth_scopes.id') - ->where('oauth_access_token_scopes.access_token_id', $token->getId()) - ->get(); - - $scopes = []; - - foreach ($result as $scope) { - $scopes[] = (new ScopeEntity($this->getServer()))->hydrate([ - 'id' => $scope->id, - 'description' => $scope->description, - ]); - } - - return $scopes; - } - - /** - * Creates a new access token. - * - * @param string $token The access token - * @param int $expireTime The expire time expressed as a unix timestamp - * @param string|int $sessionId The session ID - * - * @return \League\OAuth2\Server\Entity\AccessTokenEntity - */ - public function create($token, $expireTime, $sessionId) - { - $this->getConnection()->table('oauth_access_tokens')->insert([ - 'id' => $token, - 'expire_time' => $expireTime, - 'session_id' => $sessionId, - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now(), - ]); - - return (new AccessTokenEntity($this->getServer())) - ->setId($token) - ->setExpireTime((int) $expireTime); - } - - /** - * Associate a scope with an access token. - * - * @param \League\OAuth2\Server\Entity\AccessTokenEntity $token The access token - * @param \League\OAuth2\Server\Entity\ScopeEntity $scope The scope - * - * @return void - */ - public function associateScope(AccessTokenEntity $token, ScopeEntity $scope) - { - $this->getConnection()->table('oauth_access_token_scopes')->insert([ - 'access_token_id' => $token->getId(), - 'scope_id' => $scope->getId(), - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now(), - ]); - } - - /** - * Delete an access token. - * - * @param \League\OAuth2\Server\Entity\AccessTokenEntity $token The access token to delete - * - * @return void - */ - public function delete(AccessTokenEntity $token) - { - $this->getConnection()->table('oauth_access_tokens') - ->where('oauth_access_tokens.id', $token->getId()) - ->delete(); - } -} diff --git a/src/Storage/FluentAuthCode.php b/src/Storage/FluentAuthCode.php deleted file mode 100644 index 9f5c94d6..00000000 --- a/src/Storage/FluentAuthCode.php +++ /dev/null @@ -1,130 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Storage; - -use Carbon\Carbon; -use League\OAuth2\Server\Entity\AuthCodeEntity; -use League\OAuth2\Server\Entity\ScopeEntity; -use League\OAuth2\Server\Storage\AuthCodeInterface; - -/** - * This is the fluent auth code class. - * - * @author Luca Degasperi - */ -class FluentAuthCode extends AbstractFluentAdapter implements AuthCodeInterface -{ - /** - * Get the auth code. - * - * @param string $code - * - * @return \League\OAuth2\Server\Entity\AuthCodeEntity - */ - public function get($code) - { - $result = $this->getConnection()->table('oauth_auth_codes') - ->where('oauth_auth_codes.id', $code) - ->where('oauth_auth_codes.expire_time', '>=', time()) - ->first(); - - if (is_null($result)) { - return; - } - - return (new AuthCodeEntity($this->getServer())) - ->setId($result->id) - ->setRedirectUri($result->redirect_uri) - ->setExpireTime((int) $result->expire_time); - } - - /** - * Get the scopes for an access token. - * - * @param \League\OAuth2\Server\Entity\AuthCodeEntity $token The auth code - * - * @return array Array of \League\OAuth2\Server\Entity\ScopeEntity - */ - public function getScopes(AuthCodeEntity $token) - { - $result = $this->getConnection()->table('oauth_auth_code_scopes') - ->select('oauth_scopes.*') - ->join('oauth_scopes', 'oauth_auth_code_scopes.scope_id', '=', 'oauth_scopes.id') - ->where('oauth_auth_code_scopes.auth_code_id', $token->getId()) - ->get(); - - $scopes = []; - - foreach ($result as $scope) { - $scopes[] = (new ScopeEntity($this->getServer()))->hydrate([ - 'id' => $scope->id, - 'description' => $scope->description, - ]); - } - - return $scopes; - } - - /** - * Associate a scope with an access token. - * - * @param \League\OAuth2\Server\Entity\AuthCodeEntity $token The auth code - * @param \League\OAuth2\Server\Entity\ScopeEntity $scope The scope - * - * @return void - */ - public function associateScope(AuthCodeEntity $token, ScopeEntity $scope) - { - $this->getConnection()->table('oauth_auth_code_scopes')->insert([ - 'auth_code_id' => $token->getId(), - 'scope_id' => $scope->getId(), - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now(), - ]); - } - - /** - * Delete an access token. - * - * @param \League\OAuth2\Server\Entity\AuthCodeEntity $token The access token to delete - * - * @return void - */ - public function delete(AuthCodeEntity $token) - { - $this->getConnection()->table('oauth_auth_codes') - ->where('oauth_auth_codes.id', $token->getId()) - ->delete(); - } - - /** - * Create an auth code. - * - * @param string $token The token ID - * @param int $expireTime Token expire time - * @param int $sessionId Session identifier - * @param string $redirectUri Client redirect uri - * - * @return void - */ - public function create($token, $expireTime, $sessionId, $redirectUri) - { - $this->getConnection()->table('oauth_auth_codes')->insert([ - 'id' => $token, - 'session_id' => $sessionId, - 'redirect_uri' => $redirectUri, - 'expire_time' => $expireTime, - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now(), - ]); - } -} diff --git a/src/Storage/FluentClient.php b/src/Storage/FluentClient.php deleted file mode 100644 index 32df5127..00000000 --- a/src/Storage/FluentClient.php +++ /dev/null @@ -1,190 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Storage; - -use Carbon\Carbon; -use Illuminate\Database\ConnectionResolverInterface as Resolver; -use League\OAuth2\Server\Entity\ClientEntity; -use League\OAuth2\Server\Entity\SessionEntity; -use League\OAuth2\Server\Storage\ClientInterface; - -/** - * This is the fluent client class. - * - * @author Luca Degasperi - */ -class FluentClient extends AbstractFluentAdapter implements ClientInterface -{ - /** - * Limit clients to grants. - * - * @var bool - */ - protected $limitClientsToGrants = false; - - /** - * Create a new fluent client instance. - * - * @param \Illuminate\Database\ConnectionResolverInterface $resolver - * @param bool $limitClientsToGrants - */ - public function __construct(Resolver $resolver, $limitClientsToGrants = false) - { - parent::__construct($resolver); - $this->limitClientsToGrants = $limitClientsToGrants; - } - - /** - * Check if clients are limited to grants. - * - * @return bool - */ - public function areClientsLimitedToGrants() - { - return $this->limitClientsToGrants; - } - - /** - * Whether or not to limit clients to grants. - * - * @param bool $limit - */ - public function limitClientsToGrants($limit = false) - { - $this->limitClientsToGrants = $limit; - } - - /** - * Get the client. - * - * @param string $clientId - * @param string $clientSecret - * @param string $redirectUri - * @param string $grantType - * - * @return null|\League\OAuth2\Server\Entity\ClientEntity - */ - public function get($clientId, $clientSecret = null, $redirectUri = null, $grantType = null) - { - $query = null; - - if (!is_null($redirectUri) && is_null($clientSecret)) { - $query = $this->getConnection()->table('oauth_clients') - ->select( - 'oauth_clients.id as id', - 'oauth_clients.secret as secret', - 'oauth_client_endpoints.redirect_uri as redirect_uri', - 'oauth_clients.name as name') - ->join('oauth_client_endpoints', 'oauth_clients.id', '=', 'oauth_client_endpoints.client_id') - ->where('oauth_clients.id', $clientId) - ->where('oauth_client_endpoints.redirect_uri', $redirectUri); - } elseif (!is_null($clientSecret) && is_null($redirectUri)) { - $query = $this->getConnection()->table('oauth_clients') - ->select( - 'oauth_clients.id as id', - 'oauth_clients.secret as secret', - 'oauth_clients.name as name') - ->where('oauth_clients.id', $clientId) - ->where('oauth_clients.secret', $clientSecret); - } elseif (!is_null($clientSecret) && !is_null($redirectUri)) { - $query = $this->getConnection()->table('oauth_clients') - ->select( - 'oauth_clients.id as id', - 'oauth_clients.secret as secret', - 'oauth_client_endpoints.redirect_uri as redirect_uri', - 'oauth_clients.name as name') - ->join('oauth_client_endpoints', 'oauth_clients.id', '=', 'oauth_client_endpoints.client_id') - ->where('oauth_clients.id', $clientId) - ->where('oauth_clients.secret', $clientSecret) - ->where('oauth_client_endpoints.redirect_uri', $redirectUri); - } - - if ($this->limitClientsToGrants === true && !is_null($grantType)) { - $query = $query->join('oauth_client_grants', 'oauth_clients.id', '=', 'oauth_client_grants.client_id') - ->join('oauth_grants', 'oauth_grants.id', '=', 'oauth_client_grants.grant_id') - ->where('oauth_grants.id', $grantType); - } - - $result = $query->first(); - - if (is_null($result)) { - return; - } - - return $this->hydrateEntity($result); - } - - /** - * Get the client associated with a session. - * - * @param \League\OAuth2\Server\Entity\SessionEntity $session The session - * - * @return null|\League\OAuth2\Server\Entity\ClientEntity - */ - public function getBySession(SessionEntity $session) - { - $result = $this->getConnection()->table('oauth_clients') - ->select( - 'oauth_clients.id as id', - 'oauth_clients.secret as secret', - 'oauth_clients.name as name') - ->join('oauth_sessions', 'oauth_sessions.client_id', '=', 'oauth_clients.id') - ->where('oauth_sessions.id', '=', $session->getId()) - ->first(); - - if (is_null($result)) { - return; - } - - return $this->hydrateEntity($result); - } - - /** - * Create a new client. - * - * @param string $name The client's unique name - * @param string $id The client's unique id - * @param string $secret The clients' unique secret - * - * @return string - */ - public function create($name, $id, $secret) - { - return $this->getConnection()->table('oauth_clients')->insertGetId([ - 'id' => $id, - 'name' => $name, - 'secret' => $secret, - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now(), - ]); - } - - /** - * Hydrate the entity. - * - * @param $result - * - * @return \League\OAuth2\Server\Entity\ClientEntity - */ - protected function hydrateEntity($result) - { - $client = new ClientEntity($this->getServer()); - $client->hydrate([ - 'id' => $result->id, - 'name' => $result->name, - 'secret' => $result->secret, - 'redirectUri' => (isset($result->redirect_uri) ? $result->redirect_uri : null), - ]); - - return $client; - } -} diff --git a/src/Storage/FluentRefreshToken.php b/src/Storage/FluentRefreshToken.php deleted file mode 100644 index 2b0b53a3..00000000 --- a/src/Storage/FluentRefreshToken.php +++ /dev/null @@ -1,87 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Storage; - -use Carbon\Carbon; -use League\OAuth2\Server\Entity\RefreshTokenEntity; -use League\OAuth2\Server\Storage\RefreshTokenInterface; - -/** - * This is the fluent refresh token class. - * - * @author Luca Degasperi - */ -class FluentRefreshToken extends AbstractFluentAdapter implements RefreshTokenInterface -{ - /** - * Return a new instance of \League\OAuth2\Server\Entity\RefreshTokenEntity. - * - * @param string $token - * - * @return \League\OAuth2\Server\Entity\RefreshTokenEntity - */ - public function get($token) - { - $result = $this->getConnection()->table('oauth_refresh_tokens') - ->where('oauth_refresh_tokens.id', $token) - ->where('oauth_refresh_tokens.expire_time', '>=', time()) - ->first(); - - if (is_null($result)) { - return; - } - - return (new RefreshTokenEntity($this->getServer())) - ->setId($result->id) - ->setAccessTokenId($result->access_token_id) - ->setExpireTime((int) $result->expire_time); - } - - /** - * Create a new refresh token_name. - * - * @param string $token - * @param int $expireTime - * @param string $accessToken - * - * @return \League\OAuth2\Server\Entity\RefreshTokenEntity - */ - public function create($token, $expireTime, $accessToken) - { - $this->getConnection()->table('oauth_refresh_tokens')->insert([ - 'id' => $token, - 'expire_time' => $expireTime, - 'access_token_id' => $accessToken, - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now(), - ]); - - return (new RefreshTokenEntity($this->getServer())) - ->setId($token) - ->setAccessTokenId($accessToken) - ->setExpireTime((int) $expireTime); - } - - /** - * Delete the refresh token. - * - * @param \League\OAuth2\Server\Entity\RefreshTokenEntity $token - * - * @return void - */ - public function delete(RefreshTokenEntity $token) - { - $this->getConnection()->table('oauth_refresh_tokens') - ->where('id', $token->getId()) - ->delete(); - } -} diff --git a/src/Storage/FluentScope.php b/src/Storage/FluentScope.php deleted file mode 100644 index 0956fcf4..00000000 --- a/src/Storage/FluentScope.php +++ /dev/null @@ -1,139 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Storage; - -use Illuminate\Database\ConnectionResolverInterface as Resolver; -use League\OAuth2\Server\Entity\ScopeEntity; -use League\OAuth2\Server\Storage\ScopeInterface; - -/** - * This is the fluent scope class. - * - * @author Luca Degasperi - */ -class FluentScope extends AbstractFluentAdapter implements ScopeInterface -{ - /* - * Limit clients to scopes. - * - * @var bool - */ - protected $limitClientsToScopes = false; - - /* - * Limit scopes to grants. - * - * @var bool - */ - protected $limitScopesToGrants = false; - - /** - * Create a new fluent scope instance. - * - * @param \Illuminate\Database\ConnectionResolverInterface $resolver - * @param bool|false $limitClientsToScopes - * @param bool|false $limitScopesToGrants - */ - public function __construct(Resolver $resolver, $limitClientsToScopes = false, $limitScopesToGrants = false) - { - parent::__construct($resolver); - $this->limitClientsToScopes = $limitClientsToScopes; - $this->limitScopesToGrants = $limitScopesToGrants; - } - - /** - * Set limit clients to scopes. - * - * @param bool|false $limit - */ - public function limitClientsToScopes($limit = false) - { - $this->limitClientsToScopes = $limit; - } - - /** - * Set limit scopes to grants. - * - * @param bool|false $limit - */ - public function limitScopesToGrants($limit = false) - { - $this->limitScopesToGrants = $limit; - } - - /** - * Check if clients are limited to scopes. - * - * @return bool|false - */ - public function areClientsLimitedToScopes() - { - return $this->limitClientsToScopes; - } - - /** - * Check if scopes are limited to grants. - * - * @return bool|false - */ - public function areScopesLimitedToGrants() - { - return $this->limitScopesToGrants; - } - - /** - * Return information about a scope. - * - * Example SQL query: - * - * - * SELECT * FROM oauth_scopes WHERE scope = :scope - * - * - * @param string $scope The scope - * @param string $grantType The grant type used in the request (default = "null") - * @param string $clientId The client id used for the request (default = "null") - * - * @return \League\OAuth2\Server\Entity\ScopeEntity|null If the scope doesn't exist return false - */ - public function get($scope, $grantType = null, $clientId = null) - { - $query = $this->getConnection()->table('oauth_scopes') - ->select('oauth_scopes.id as id', 'oauth_scopes.description as description') - ->where('oauth_scopes.id', $scope); - - if ($this->limitClientsToScopes === true && !is_null($clientId)) { - $query = $query->join('oauth_client_scopes', 'oauth_scopes.id', '=', 'oauth_client_scopes.scope_id') - ->where('oauth_client_scopes.client_id', $clientId); - } - - if ($this->limitScopesToGrants === true && !is_null($grantType)) { - $query = $query->join('oauth_grant_scopes', 'oauth_scopes.id', '=', 'oauth_grant_scopes.scope_id') - ->join('oauth_grants', 'oauth_grants.id', '=', 'oauth_grant_scopes.grant_id') - ->where('oauth_grants.id', $grantType); - } - - $result = $query->first(); - - if (is_null($result)) { - return; - } - - $scope = new ScopeEntity($this->getServer()); - $scope->hydrate([ - 'id' => $result->id, - 'description' => $result->description, - ]); - - return $scope; - } -} diff --git a/src/Storage/FluentSession.php b/src/Storage/FluentSession.php deleted file mode 100644 index cecc6a0e..00000000 --- a/src/Storage/FluentSession.php +++ /dev/null @@ -1,165 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Storage; - -use Carbon\Carbon; -use League\OAuth2\Server\Entity\AccessTokenEntity; -use League\OAuth2\Server\Entity\AuthCodeEntity; -use League\OAuth2\Server\Entity\ScopeEntity; -use League\OAuth2\Server\Entity\SessionEntity; -use League\OAuth2\Server\Storage\SessionInterface; - -/** - * This is the fluent session class. - * - * @author Luca Degasperi - */ -class FluentSession extends AbstractFluentAdapter implements SessionInterface -{ - /** - * Get a session from it's identifier. - * - * @param string $sessionId - * - * @return \League\OAuth2\Server\Entity\SessionEntity - */ - public function get($sessionId) - { - $result = $this->getConnection()->table('oauth_sessions') - ->where('oauth_sessions.id', $sessionId) - ->first(); - - if (is_null($result)) { - return; - } - - return (new SessionEntity($this->getServer())) - ->setId($result->id) - ->setOwner($result->owner_type, $result->owner_id); - } - - /** - * Get a session from an access token. - * - * @param \League\OAuth2\Server\Entity\AccessTokenEntity $accessToken The access token - * - * @return \League\OAuth2\Server\Entity\SessionEntity - */ - public function getByAccessToken(AccessTokenEntity $accessToken) - { - $result = $this->getConnection()->table('oauth_sessions') - ->select('oauth_sessions.*') - ->join('oauth_access_tokens', 'oauth_sessions.id', '=', 'oauth_access_tokens.session_id') - ->where('oauth_access_tokens.id', $accessToken->getId()) - ->first(); - - if (is_null($result)) { - return; - } - - return (new SessionEntity($this->getServer())) - ->setId($result->id) - ->setOwner($result->owner_type, $result->owner_id); - } - - /** - * Get a session's scopes. - * - * @param \League\OAuth2\Server\Entity\SessionEntity - * - * @return array Array of \League\OAuth2\Server\Entity\ScopeEntity - */ - public function getScopes(SessionEntity $session) - { - // TODO: Check this before pushing - $result = $this->getConnection()->table('oauth_session_scopes') - ->select('oauth_scopes.*') - ->join('oauth_scopes', 'oauth_session_scopes.scope_id', '=', 'oauth_scopes.id') - ->where('oauth_session_scopes.session_id', $session->getId()) - ->get(); - - $scopes = []; - - foreach ($result as $scope) { - $scopes[] = (new ScopeEntity($this->getServer()))->hydrate([ - 'id' => $scope->id, - 'description' => $scope->description, - ]); - } - - return $scopes; - } - - /** - * Create a new session. - * - * @param string $ownerType Session owner's type (user, client) - * @param string $ownerId Session owner's ID - * @param string $clientId Client ID - * @param string $clientRedirectUri Client redirect URI (default = null) - * - * @return int The session's ID - */ - public function create($ownerType, $ownerId, $clientId, $clientRedirectUri = null) - { - return $this->getConnection()->table('oauth_sessions')->insertGetId([ - 'client_id' => $clientId, - 'owner_type' => $ownerType, - 'owner_id' => $ownerId, - 'client_redirect_uri' => $clientRedirectUri, - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now(), - ]); - } - - /** - * Associate a scope with a session. - * - * @param \League\OAuth2\Server\Entity\SessionEntity $session - * @param \League\OAuth2\Server\Entity\ScopeEntity $scope The scopes ID might be an integer or string - * - * @return void - */ - public function associateScope(SessionEntity $session, ScopeEntity $scope) - { - $this->getConnection()->table('oauth_session_scopes')->insert([ - 'session_id' => $session->getId(), - 'scope_id' => $scope->getId(), - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now(), - ]); - } - - /** - * Get a session from an auth code. - * - * @param \League\OAuth2\Server\Entity\AuthCodeEntity $authCode The auth code - * - * @return \League\OAuth2\Server\Entity\SessionEntity - */ - public function getByAuthCode(AuthCodeEntity $authCode) - { - $result = $this->getConnection()->table('oauth_sessions') - ->select('oauth_sessions.*') - ->join('oauth_auth_codes', 'oauth_sessions.id', '=', 'oauth_auth_codes.session_id') - ->where('oauth_auth_codes.id', $authCode->getId()) - ->first(); - - if (is_null($result)) { - return; - } - - return (new SessionEntity($this->getServer())) - ->setId($result->id) - ->setOwner($result->owner_type, $result->owner_id); - } -} diff --git a/src/Storage/FluentStorageServiceProvider.php b/src/Storage/FluentStorageServiceProvider.php deleted file mode 100644 index 18b61421..00000000 --- a/src/Storage/FluentStorageServiceProvider.php +++ /dev/null @@ -1,132 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Storage; - -use Illuminate\Contracts\Foundation\Application; -use Illuminate\Support\ServiceProvider; -use League\OAuth2\Server\Storage\AccessTokenInterface; -use League\OAuth2\Server\Storage\AuthCodeInterface; -use League\OAuth2\Server\Storage\ClientInterface; -use League\OAuth2\Server\Storage\RefreshTokenInterface; -use League\OAuth2\Server\Storage\ScopeInterface; -use League\OAuth2\Server\Storage\SessionInterface; - -/** - * This is the fluent storage service provider class. - * - * @author Luca Degasperi - */ -class FluentStorageServiceProvider extends ServiceProvider -{ - /** - * Bootstrap the application events. - * - * @return void - */ - public function boot() - { - // - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - $this->registerStorageBindings($this->app); - $this->registerInterfaceBindings($this->app); - } - - /** - * Bind the storage implementations to the IoC container. - * - * @param \Illuminate\Contracts\Foundation\Application $app - * - * @return void - */ - public function registerStorageBindings(Application $app) - { - $provider = $this; - - $app->singleton(FluentAccessToken::class, function () use ($provider) { - $storage = new FluentAccessToken($provider->app['db']); - $storage->setConnectionName($provider->getConnectionName()); - - return $storage; - }); - - $app->singleton(FluentAuthCode::class, function () use ($provider) { - $storage = new FluentAuthCode($provider->app['db']); - $storage->setConnectionName($provider->getConnectionName()); - - return $storage; - }); - - $app->singleton(FluentClient::class, function ($app) use ($provider) { - $limitClientsToGrants = $app['config']->get('oauth2.limit_clients_to_grants'); - $storage = new FluentClient($provider->app['db'], $limitClientsToGrants); - $storage->setConnectionName($provider->getConnectionName()); - - return $storage; - }); - - $app->singleton(FluentRefreshToken::class, function () use ($provider) { - $storage = new FluentRefreshToken($provider->app['db']); - $storage->setConnectionName($provider->getConnectionName()); - - return $storage; - }); - - $app->singleton(FluentScope::class, function ($app) use ($provider) { - $limitClientsToScopes = $app['config']->get('oauth2.limit_clients_to_scopes'); - $limitScopesToGrants = $app['config']->get('oauth2.limit_scopes_to_grants'); - $storage = new FluentScope($provider->app['db'], $limitClientsToScopes, $limitScopesToGrants); - $storage->setConnectionName($provider->getConnectionName()); - - return $storage; - }); - - $app->singleton(FluentSession::class, function () use ($provider) { - $storage = new FluentSession($provider->app['db']); - $storage->setConnectionName($provider->getConnectionName()); - - return $storage; - }); - } - - /** - * Bind the interfaces to their implementations. - * - * @param \Illuminate\Contracts\Foundation\Application $app - * - * @return void - */ - public function registerInterfaceBindings(Application $app) - { - $app->bind(ClientInterface::class, FluentClient::class); - $app->bind(ScopeInterface::class, FluentScope::class); - $app->bind(SessionInterface::class, FluentSession::class); - $app->bind(AuthCodeInterface::class, FluentAuthCode::class); - $app->bind(AccessTokenInterface::class, FluentAccessToken::class); - $app->bind(RefreshTokenInterface::class, FluentRefreshToken::class); - } - - /** - * @return string - */ - public function getConnectionName() - { - return ($this->app['config']->get('oauth2.database') !== 'default') ? $this->app['config']->get('oauth2.database') : null; - } -} diff --git a/src/Traits/OAuthControllerTrait.php b/src/Traits/OAuthControllerTrait.php new file mode 100644 index 00000000..38390d2f --- /dev/null +++ b/src/Traits/OAuthControllerTrait.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Traits; + +use Illuminate\Support\Facades\Auth; +use League\OAuth2\Server\AuthorizationServer; +use League\OAuth2\Server\Exception\OAuthServerException; +use League\OAuth2\Server\RequestTypes\AuthorizationRequest; +use Psr\Http\Message\ServerRequestInterface; +use Zend\Diactoros\Response; + +/** + * This is the oauth controller trait. + * + * @author Luca Degasperi + */ +trait OAuthControllerTrait +{ + public function postAccessToken(ServerRequestInterface $request, AuthorizationServer $server) + { + $response = new Response(); + + try { + return $server->respondToAccessTokenRequest($request, $response); + } catch (OAuthServerException $e) { + return $e->generateHttpResponse($response); + } catch (\Exception $exception) { + return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))->generateHttpResponse($response); + } + } + + public function doAuthorize(ServerRequestInterface $request, AuthorizationServer $server) + { + $response = new Response(); + 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 + if (strtolower($request->getMethod()) === 'post') { + $authRequest->setUser(Auth::user()); + + // (true = approved, false = denied) + $authRequest->setAuthorizationApproved($this->getAuthorizationApprovedAttribute($request)); + + // Return the HTTP redirect response + return $server->completeAuthorizationRequest($authRequest, $response); + } else { + return $this->getAuthorizationView($authRequest, $request->getUri()->getQuery()); + } + } catch (OAuthServerException $exception) { + return $exception->generateHttpResponse($response); + } catch (\Exception $exception) { + return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))->generateHttpResponse($response); + } + } + + public function getAuthorizationView(AuthorizationRequest $authRequest, $queryString) + { + $view = property_exists($this, 'authorizationView') ? $this->authorizationView : 'oauth2server::authorize'; + + return view($view) + ->with('authRequest', $authRequest) + ->with('queryString', $queryString); + } + + public function getAuthorizationApprovedAttribute(ServerRequestInterface $request) + { + $attribute = property_exists($this, 'authorizationApprovedAttribute') ? $this->authorizationApprovedAttribute : 'authorize'; + + return (bool) $this->getRequestParameter($attribute, $request, false); + } + + protected function getRequestParameter($parameter, ServerRequestInterface $request, $default = null) + { + $requestParameters = (array) $request->getParsedBody(); + + return isset($requestParameters[$parameter]) ? $requestParameters[$parameter] : $default; + } +} diff --git a/tests/AbstractTestCase.php b/tests/AbstractTestCase.php index c0bc342a..24f7452c 100644 --- a/tests/AbstractTestCase.php +++ b/tests/AbstractTestCase.php @@ -1,7 +1,7 @@ * @@ -9,38 +9,27 @@ * file that was distributed with this source code. */ -use Orchestra\Testbench\TestCase as OrchestraTestCase; +namespace LucaDegasperi\OAuth2Server\Tests; -abstract class AbstractTestCase extends OrchestraTestCase -{ - public function setUp() - { - parent::setUp(); - } +use GrahamCampbell\TestBench\AbstractPackageTestCase; +use LucaDegasperi\OAuth2Server\OAuth2ServerServiceProvider; +/** + * This is the abstract test class. + * + * @author Vincent Klaiber + */ +abstract class AbstractTestCase extends AbstractPackageTestCase +{ /** - * Get base path. + * Get the service provider class. + * + * @param \Illuminate\Contracts\Foundation\Application $app * * @return string */ - protected function getBasePath() - { - // reset base path to point to our package's src directory - return __DIR__.'/../vendor/orchestra/testbench/fixture'; - } - - protected function getPackageProviders($app) - { - return [ - 'LucaDegasperi\OAuth2Server\Storage\FluentStorageServiceProvider', - 'LucaDegasperi\OAuth2Server\OAuth2ServerServiceProvider', - ]; - } - - protected function getPackageAliases($app) + protected function getServiceProviderClass($app) { - return [ - 'Authorizer' => 'LucaDegasperi\OAuth2Server\Facades\Authorizer', - ]; + return OAuth2ServerServiceProvider::class; } } diff --git a/tests/Database/Seeders/AccessTokensTableSeeder.php b/tests/Database/Seeders/AccessTokensTableSeeder.php deleted file mode 100644 index 10f27cea..00000000 --- a/tests/Database/Seeders/AccessTokensTableSeeder.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Tests\Database\Seeders; - -use Carbon\Carbon; -use Illuminate\Database\Seeder; -use Illuminate\Support\Facades\DB; - -class AccessTokensTableSeeder extends Seeder -{ - public function run() - { - DB::table('oauth_access_tokens')->delete(); - - $datetime = Carbon::now(); - - $tokens = [ - [ - 'id' => 'totallyanaccesstoken1', - 'session_id' => 1, - 'expire_time' => time() + 60, - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - [ - 'id' => 'totallyanaccesstoken2', - 'session_id' => 2, - 'expire_time' => time() + 120, - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - ]; - - DB::table('oauth_access_tokens')->insert($tokens); - } -} diff --git a/tests/Database/Seeders/AuthCodesTableSeeder.php b/tests/Database/Seeders/AuthCodesTableSeeder.php deleted file mode 100644 index 4ff71776..00000000 --- a/tests/Database/Seeders/AuthCodesTableSeeder.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Tests\Database\Seeders; - -use Carbon\Carbon; -use Illuminate\Database\Seeder; -use Illuminate\Support\Facades\DB; - -class AuthCodesTableSeeder extends Seeder -{ - public function run() - { - DB::table('oauth_auth_codes')->delete(); - - $datetime = Carbon::now(); - - $codes = [ - [ - 'id' => 'totallyanauthcode1', - 'session_id' => 1, - 'redirect_uri' => 'https://example1.com/', - 'expire_time' => time() + 60, - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - [ - 'id' => 'totallyanauthcode2', - 'session_id' => 2, - 'redirect_uri' => 'https://example2.com/', - 'expire_time' => time() + 120, - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - ]; - - DB::table('oauth_auth_codes')->insert($codes); - } -} diff --git a/tests/Database/Seeders/ClientsTableSeeder.php b/tests/Database/Seeders/ClientsTableSeeder.php deleted file mode 100644 index 98b3c9b4..00000000 --- a/tests/Database/Seeders/ClientsTableSeeder.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Tests\Database\Seeders; - -use Carbon\Carbon; -use Illuminate\Database\Seeder; -use Illuminate\Support\Facades\DB; - -class ClientsTableSeeder extends Seeder -{ - public function run() - { - DB::table('oauth_clients')->delete(); - - $datetime = Carbon::now(); - - $clients = [ - [ - 'id' => 'client1id', - 'secret' => 'client1secret', - 'name' => 'client1', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - [ - 'id' => 'client2id', - 'secret' => 'client2secret', - 'name' => 'client2', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - ]; - - DB::table('oauth_clients')->insert($clients); - - DB::table('oauth_client_endpoints')->delete(); - - $clientEndpoints = [ - [ - 'client_id' => 'client1id', - 'redirect_uri' => 'http://example1.com/callback', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - [ - 'client_id' => 'client2id', - 'redirect_uri' => 'http://example2.com/callback', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - ]; - - DB::table('oauth_client_endpoints')->insert($clientEndpoints); - } -} diff --git a/tests/Database/Seeders/GrantsTableSeeder.php b/tests/Database/Seeders/GrantsTableSeeder.php deleted file mode 100644 index e938893c..00000000 --- a/tests/Database/Seeders/GrantsTableSeeder.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Tests\Database\Seeders; - -use Carbon\Carbon; -use Illuminate\Database\Seeder; -use Illuminate\Support\Facades\DB; - -class GrantsTableSeeder extends Seeder -{ - public function run() - { - DB::table('oauth_grants')->delete(); - - $datetime = Carbon::now(); - - $grants = [ - [ - 'id' => 'grant1', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - [ - 'id' => 'grant2', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - ]; - - DB::table('oauth_grants')->insert($grants); - - DB::table('oauth_client_grants')->delete(); - - $clientGrants = [ - [ - 'client_id' => 'client1id', - 'grant_id' => 'grant1', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - [ - 'client_id' => 'client2id', - 'grant_id' => 'grant2', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - ]; - - DB::table('oauth_client_grants')->insert($clientGrants); - } -} diff --git a/tests/Database/Seeders/OAuth2DatabaseSeeder.php b/tests/Database/Seeders/OAuth2DatabaseSeeder.php deleted file mode 100644 index 3c256c94..00000000 --- a/tests/Database/Seeders/OAuth2DatabaseSeeder.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Tests\Database\Seeders; - -use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Seeder; -use Illuminate\Support\Facades\App; - -class OAuth2DatabaseSeeder extends Seeder -{ - /** - * Run the database seeds. - * - * @return void - */ - public function run() - { - if (App::environment() === 'production') { - exit('I just stopped you getting fired. Love Luca'); - } - - Model::unguard(); - - $this->call(ClientsTableSeeder::class); - $this->call(GrantsTableSeeder::class); - $this->call(ScopesTableSeeder::class); - $this->call(SessionsTableSeeder::class); - $this->call(AuthCodesTableSeeder::class); - $this->call(AccessTokensTableSeeder::class); - $this->call(RefreshTokensTableSeeder::class); - - Model::reguard(); - } -} diff --git a/tests/Database/Seeders/RefreshTokensTableSeeder.php b/tests/Database/Seeders/RefreshTokensTableSeeder.php deleted file mode 100644 index 351232ff..00000000 --- a/tests/Database/Seeders/RefreshTokensTableSeeder.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Tests\Database\Seeders; - -use Carbon\Carbon; -use Illuminate\Database\Seeder; -use Illuminate\Support\Facades\DB; - -class RefreshTokensTableSeeder extends Seeder -{ - public function run() - { - DB::table('oauth_refresh_tokens')->delete(); - - $datetime = Carbon::now(); - - $tokens = [ - [ - 'id' => 'totallyarefreshtoken1', - 'access_token_id' => 'totallyanaccesstoken1', - 'expire_time' => time() + 60, - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - ]; - - DB::table('oauth_refresh_tokens')->insert($tokens); - } -} diff --git a/tests/Database/Seeders/ScopesTableSeeder.php b/tests/Database/Seeders/ScopesTableSeeder.php deleted file mode 100644 index b8b6bc48..00000000 --- a/tests/Database/Seeders/ScopesTableSeeder.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Tests\Database\Seeders; - -use Carbon\Carbon; -use Illuminate\Database\Seeder; -use Illuminate\Support\Facades\DB; - -class ScopesTableSeeder extends Seeder -{ - public function run() - { - DB::table('oauth_scopes')->delete(); - - $datetime = Carbon::now(); - - $scopes = [ - [ - 'id' => 'scope1', - 'description' => 'Scope 1 Description', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - [ - 'id' => 'scope2', - 'description' => 'Scope 2 Description', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - ]; - - DB::table('oauth_scopes')->insert($scopes); - - DB::table('oauth_client_scopes')->delete(); - - $clientScopes = [ - [ - 'client_id' => 'client1id', - 'scope_id' => 'scope1', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - [ - 'client_id' => 'client2id', - 'scope_id' => 'scope2', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - ]; - - DB::table('oauth_client_scopes')->insert($clientScopes); - - DB::table('oauth_grant_scopes')->delete(); - - $grantScopes = [ - [ - 'grant_id' => 'grant1', - 'scope_id' => 'scope1', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - [ - 'grant_id' => 'grant2', - 'scope_id' => 'scope2', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - ]; - - DB::table('oauth_grant_scopes')->insert($grantScopes); - } -} diff --git a/tests/Database/Seeders/SessionsTableSeeder.php b/tests/Database/Seeders/SessionsTableSeeder.php deleted file mode 100644 index 7e36cc0f..00000000 --- a/tests/Database/Seeders/SessionsTableSeeder.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace LucaDegasperi\OAuth2Server\Tests\Database\Seeders; - -use Carbon\Carbon; -use Illuminate\Database\Seeder; -use Illuminate\Support\Facades\DB; - -class SessionsTableSeeder extends Seeder -{ - public function run() - { - DB::table('oauth_sessions')->delete(); - - $datetime = Carbon::now(); - - $sessions = [ - [ - 'client_id' => 'client1id', - 'owner_id' => '1', - 'owner_type' => 'user', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - [ - 'client_id' => 'client2id', - 'owner_id' => '2', - 'owner_type' => 'user', - 'created_at' => $datetime, - 'updated_at' => $datetime, - ], - ]; - - DB::table('oauth_sessions')->insert($sessions); - } -} diff --git a/tests/ServiceProviderTest.php b/tests/ServiceProviderTest.php new file mode 100644 index 00000000..8ae89b0e --- /dev/null +++ b/tests/ServiceProviderTest.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace LucaDegasperi\OAuth2Server\Tests; + +use GrahamCampbell\TestBenchCore\ServiceProviderTrait; + +/** + * This is the service provider test class. + * + * @author Vincent Klaiber + */ +class ServiceProviderTest extends AbstractTestCase +{ + use ServiceProviderTrait; +} diff --git a/tests/functional/bootstrap/FeatureContext.php b/tests/functional/bootstrap/FeatureContext.php deleted file mode 100644 index 9b010a11..00000000 --- a/tests/functional/bootstrap/FeatureContext.php +++ /dev/null @@ -1,161 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use Behat\Behat\Exception\PendingException; -use League\OAuth2\Server\Grant\ClientCredentialsGrant; -use LucaDegasperi\OAuth2Server\Tests\Database\Seeders\OAuth2DatabaseSeeder; -use Orchestra\Testbench\BehatFeatureContext; -use PHPUnit_Framework_Assert as PHPUnit; - -/** - * Features context. - */ -class FeatureContext extends BehatFeatureContext -{ - /** @BeforeScenario */ - public function up() - { - $this->migrateAndSeed(); - } - - /** @AfterScenario */ - public function down() - { - $this->resetMigrations(); - } - - /** - * @Given /^An authorization server exists that supports the "([^"]*)" grant type$/ - */ - public function anAuthorizationServerExistsThatSupportsTheGrantType($arg1) - { - $clientCredentialsGrant = new ClientCredentialsGrant(); - $this->app['oauth2-server.authorizer']->getIssuer()->addGrantType($clientCredentialsGrant); - - $this->app['router']->enableFilters(); - $this->app['router']->post('oauth/access_token', 'OAuthController@postAccessToken'); - } - - /** - * @Given /^I have invalid client credentials$/ - */ - public function iHaveInvalidClientCredentials() - { - //throw new PendingException(); - } - - /** - * @When /^I post to the "([^"]*)" page "([^"]*)" "([^"]*)" "([^"]*)"$/ - */ - public function iPostToThePage($pageName, $grantType, $clientId, $clientSecret) - { - $params = [ - 'grant_type' => $grantType, - 'client_id' => $clientId, - 'client_secret' => $clientSecret, - ]; - $this->app['env'] = 'functional'; - $this->call('POST', $pageName, $params); - } - - /** - * @Then /^I should get an "([^"]*)" error$/ - */ - public function iShouldGetAnError($arg1) - { - $this->assertResponseStatus(401); - $content = json_decode($this->client->getResponse()->getContent()); - PHPUnit::assertEquals('invalid_client', $content->error); - } - - /** - * @Given /^I have valid client credentials$/ - */ - public function iHaveValidClientCredentials() - { - //throw new PendingException(); - } - - /** - * @Then /^I should get an access token\.$/ - */ - public function iShouldGetAnAccessToken() - { - $this->assertResponseStatus(200); - $content = json_decode($this->client->getResponse()->getContent(), true); - PHPUnit::assertArrayHasKey('access_token', $content); - PHPUnit::assertArrayHasKey('expires_in', $content); - PHPUnit::assertArrayHasKey('token_type', $content); - PHPUnit::assertEquals('Bearer', $content['token_type']); - } - - protected $artisan; - - /** - * Get package aliases. - * - * @return array - */ - protected function getPackageAliases() - { - return [ - 'Authorizer' => 'LucaDegasperi\OAuth2Server\Facades\AuthorizerFacade', - ]; - } - - /** - * Get package providers. - * - * @return array - */ - protected function getPackageProviders() - { - return [ - 'LucaDegasperi\OAuth2Server\Storage\FluentStorageServiceProvider', - 'LucaDegasperi\OAuth2Server\OAuth2ServerServiceProvider', - ]; - } - - /** - * Define environment setup. - * - * @param \Illuminate\Foundation\Application $app - * - * @return void - */ - protected function getEnvironmentSetUp($app) - { - $app['path.base'] = __DIR__.'/../../../src'; - $app['config']->set('database.default', 'testbench'); - $app['config']->set('database.connections.testbench', [ - 'driver' => 'sqlite', - 'database' => ':memory:', - 'prefix' => '', - ]); - $this->artisan = $app->make('artisan'); - } - - public function migrateAndSeed() - { - $this->artisan->call('migrate', [ - '--database' => 'testbench', - '--path' => '../src/migrations', - ]); - $this->artisan->call('db:seed', [ - '--class' => OAuth2DatabaseSeeder::class, - ]); - } - - public function resetMigrations() - { - $this->artisan->call('migrate:reset'); - } -} diff --git a/tests/functional/bootstrap/OAuthController.php b/tests/functional/bootstrap/OAuthController.php deleted file mode 100644 index f0ca2851..00000000 --- a/tests/functional/bootstrap/OAuthController.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use Illuminate\Routing\Controller; -use LucaDegasperi\OAuth2Server\Authorizer; - -class OAuthController extends Controller -{ - protected $authorizer; - - public function __construct(Authorizer $authorizer) - { - $this->authorizer = $authorizer; - - $this->beforeFilter('auth', ['only' => ['getAuthorize', 'postAuthorize']]); - $this->beforeFilter('csrf', ['only' => 'postAuthorize']); - $this->beforeFilter('check-authorization-params', ['only' => ['getAuthorize', 'postAuthorize']]); - } - - public function postAccessToken() - { - return Response::json($this->authorizer->issueAccessToken()); - } - - public function getAuthorize() - { - return View::make('authorization-form', $this->authorizer->getAuthCodeRequestParams()); - } - - public function postAuthorize() - { - // get the user id - $params['user_id'] = Auth::user()->id; - - $redirectUri = ''; - - if (Input::get('approve') !== null) { - $redirectUri = $this->authorizer->issueAuthCode('user', $params['user_id'], $params); - } - - if (Input::get('deny') !== null) { - $redirectUri = $this->authorizer->authCodeRequestDeniedRedirectUri(); - } - - return Redirect::to($redirectUri); - } -} diff --git a/tests/functional/clientcredentials.feature b/tests/functional/clientcredentials.feature deleted file mode 100644 index feeebc1f..00000000 --- a/tests/functional/clientcredentials.feature +++ /dev/null @@ -1,25 +0,0 @@ -Feature: Client Credentials Authorization - In order to gain access to an api - As a client - I want to be able to exchange my credentials for an access token - - Background: - Given An authorization server exists that supports the "client_credentials" grant type - - Scenario Outline: With invalid credentials I won't get an access token - Given I have invalid client credentials - When I post to the "oauth/access_token" page - Then I should get an "invalid_client" error - - Examples: - | grant_type | client_id | client_secret | - | "client_credentials" | "invalid" | "invalid" | - - Scenario Outline: With valid client credentials I should get an access token - Given I have valid client credentials - When I post to the "oauth/access_token" page - Then I should get an access token. - - Examples: - | grant_type | client_id | client_secret | - | "client_credentials" | "client1id" | "client1secret" | \ No newline at end of file diff --git a/tests/integration/AbstractDBTestCase.php b/tests/integration/AbstractDBTestCase.php deleted file mode 100644 index 4414a865..00000000 --- a/tests/integration/AbstractDBTestCase.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use LucaDegasperi\OAuth2Server\Tests\Database\Seeders\OAuth2DatabaseSeeder; - -abstract class AbstractDBTestCase extends AbstractTestCase -{ - protected $artisan; - - public function setUp() - { - parent::setUp(); - - $this->artisan = $this->app->make('Illuminate\Contracts\Console\Kernel'); - $this->artisan->call('migrate', [ - '--database' => 'testbench', - '--path' => '../../../../database/migrations', - ]); - $this->artisan->call('db:seed', [ - '--class' => OAuth2DatabaseSeeder::class, - ]); - } - - protected function getEnvironmentSetUp($app) - { - parent::getEnvironmentSetUp($app); - - $app['config']->set('database.default', 'testbench'); - $app['config']->set('database.connections.testbench', [ - 'driver' => 'sqlite', - 'database' => ':memory:', - 'prefix' => '', - ]); - } - - public function tearDown() - { - //$this->artisan->call('migrate:reset'); - } -} diff --git a/tests/integration/FluentAccessTokenTest.php b/tests/integration/FluentAccessTokenTest.php deleted file mode 100644 index 18e136c7..00000000 --- a/tests/integration/FluentAccessTokenTest.php +++ /dev/null @@ -1,129 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use LucaDegasperi\OAuth2Server\Storage\FluentAccessToken; -use Mockery as m; - -class FluentAccessTokenTest extends AbstractDBTestCase -{ - public function getAccessTokenRepository() - { - $server = m::mock('League\OAuth2\Server\AbstractServer'); - $repo = new FluentAccessToken($this->app['db']); - $repo->setServer($server); - - return $repo; - } - - public function test_it_fetches_an_access_token_object_with_a_valid_token() - { - $repo = $this->getAccessTokenRepository(); - - $result = $repo->get('totallyanaccesstoken1'); - - $this->assertInstanceOf('League\OAuth2\Server\Entity\AccessTokenEntity', $result); - $this->assertEquals('totallyanaccesstoken1', $result->getId()); - $this->assertInternalType('int', $result->getExpireTime()); - } - - public function test_it_returns_null_with_an_invalid_token() - { - $repo = $this->getAccessTokenRepository(); - - $result = $repo->get('invalid_auth_code'); - - $this->assertNull($result); - } - - /*public function test_it_fetches_an_access_token_object_with_a_valid_refresh_token() - { - $token = m::mock('League\OAuth2\Server\Entity\RefreshTokenEntity'); - $token->shouldReceive('getId')->once()->andReturn('totallyarefreshtoken1'); - - $repo = $this->getAccessTokenRepository(); - - $result = $repo->getByRefreshToken($token); - - $this->assertInstanceOf('League\OAuth2\Server\Entity\AccessTokenEntity', $result); - $this->assertEquals('totallyanaccesstoken1', $result->getId()); - $this->assertInternalType('int', $result->getExpireTime()); - } - - public function test_it_returns_null_with_an_invalid_refresh_token() - { - $token = m::mock('League\OAuth2\Server\Entity\RefreshTokenEntity'); - $token->shouldReceive('getId')->once()->andReturn('notarefreshtoken'); - - $repo = $this->getAccessTokenRepository(); - - $result = $repo->getByRefreshToken($token); - - $this->assertNull($result); - }*/ - - public function test_it_deletes_an_access_token() - { - $token = m::mock('League\OAuth2\Server\Entity\AccessTokenEntity'); - $token->shouldReceive('getId')->once()->andReturn('totallyanaccesstoken1'); - - $repo = $this->getAccessTokenRepository(); - - $repo->delete($token); - $result = $repo->get('totallyanaccesstoken1'); - - $this->assertNull($result); - } - - public function test_it_associates_scopes() - { - $token = m::mock('League\OAuth2\Server\Entity\AccessTokenEntity'); - $token->shouldReceive('getId')->times(4)->andReturn('totallyanaccesstoken1'); - - $scope1 = m::mock('League\OAuth2\Server\Entity\ScopeEntity'); - $scope1->shouldReceive('getId')->once()->andReturn('scope1'); - - $scope2 = m::mock('League\OAuth2\Server\Entity\ScopeEntity'); - $scope2->shouldReceive('getId')->once()->andReturn('scope2'); - - $repo = $this->getAccessTokenRepository(); - - $result1 = $repo->getScopes($token); - - $repo->associateScope($token, $scope1); - $repo->associateScope($token, $scope1); - - $result2 = $repo->getScopes($token); - - $this->assertInternalType('array', $result1); - $this->assertEquals(0, count($result1)); - - $this->assertInternalType('array', $result2); - $this->assertEquals(2, count($result2)); - - $first = $result2[0]; - - $this->assertInstanceOf('League\OAuth2\Server\Entity\ScopeEntity', $first); - $this->assertEquals('scope1', $first->getId()); - } - - public function test_it_creates_an_access_token() - { - $repo = $this->getAccessTokenRepository(); - - $time = time() + 120; - $result = $repo->create('accesstoken', $time, 1); - - $this->assertInstanceOf('League\OAuth2\Server\Entity\AccessTokenEntity', $result); - $this->assertEquals('accesstoken', $result->getId()); - $this->assertInternalType('int', $result->getExpireTime()); - $this->assertEquals($time, $result->getExpireTime()); - } -} diff --git a/tests/integration/FluentAuthCodeTest.php b/tests/integration/FluentAuthCodeTest.php deleted file mode 100644 index e443a840..00000000 --- a/tests/integration/FluentAuthCodeTest.php +++ /dev/null @@ -1,105 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use LucaDegasperi\OAuth2Server\Storage\FluentAuthCode; -use Mockery as m; - -class FluentAuthCodeTest extends AbstractDBTestCase -{ - public function getAuthCodeRepository() - { - $server = m::mock('League\OAuth2\Server\AbstractServer'); - $repo = new FluentAuthCode($this->app['db']); - $repo->setServer($server); - - return $repo; - } - - public function test_it_fetches_an_auth_code_with_a_valid_code() - { - $repo = $this->getAuthCodeRepository(); - - $result = $repo->get('totallyanauthcode1'); - - $this->assertInstanceOf('League\OAuth2\Server\Entity\AuthCodeEntity', $result); - $this->assertEquals('totallyanauthcode1', $result->getId()); - $this->assertInternalType('int', $result->getExpireTime()); - } - - public function test_it_returns_null_with_an_invalid_code() - { - $repo = $this->getAuthCodeRepository(); - - $result = $repo->get('invalid_auth_code'); - - $this->assertNull($result); - } - - public function test_it_deletes_an_auth_code() - { - $code = m::mock('League\OAuth2\Server\Entity\AuthCodeEntity'); - $code->shouldReceive('getId')->once()->andReturn('totallyanauthcode1'); - - $repo = $this->getAuthCodeRepository(); - - $repo->delete($code); - $result = $repo->get('totallyanauthcode1'); - - $this->assertNull($result); - } - - public function test_it_associates_scopes() - { - $code = m::mock('League\OAuth2\Server\Entity\AuthCodeEntity'); - $code->shouldReceive('getId')->times(4)->andReturn('totallyanauthcode1'); - - $scope1 = m::mock('League\OAuth2\Server\Entity\ScopeEntity'); - $scope1->shouldReceive('getId')->once()->andReturn('scope1'); - - $scope2 = m::mock('League\OAuth2\Server\Entity\ScopeEntity'); - $scope2->shouldReceive('getId')->once()->andReturn('scope2'); - - $repo = $this->getAuthCodeRepository(); - - $result1 = $repo->getScopes($code); - - $repo->associateScope($code, $scope1); - $repo->associateScope($code, $scope1); - - $result2 = $repo->getScopes($code); - - $this->assertInternalType('array', $result1); - $this->assertEquals(0, count($result1)); - - $this->assertInternalType('array', $result2); - $this->assertEquals(2, count($result2)); - - $first = $result2[0]; - - $this->assertInstanceOf('League\OAuth2\Server\Entity\ScopeEntity', $first); - $this->assertEquals('scope1', $first->getId()); - } - - public function test_it_creates_an_auth_code() - { - $repo = $this->getAuthCodeRepository(); - - $time = time() + 120; - $repo->create('newauthcode', $time, 1, 'http://example1.com'); - $result = $repo->get('newauthcode'); - - $this->assertInstanceOf('League\OAuth2\Server\Entity\AuthCodeEntity', $result); - $this->assertEquals('newauthcode', $result->getId()); - $this->assertInternalType('int', $result->getExpireTime()); - $this->assertEquals($time, $result->getExpireTime()); - $this->assertEquals('http://example1.com', $result->getRedirectUri()); - } -} diff --git a/tests/integration/FluentClientTest.php b/tests/integration/FluentClientTest.php deleted file mode 100644 index 6a97324d..00000000 --- a/tests/integration/FluentClientTest.php +++ /dev/null @@ -1,124 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use LucaDegasperi\OAuth2Server\Storage\FluentClient; -use Mockery as m; - -class FluentClientTest extends AbstractDBTestCase -{ - public function getClientRepository() - { - $server = m::mock('League\OAuth2\Server\AbstractServer'); - $repo = new FluentClient($this->app['db']); - $repo->setServer($server); - - return $repo; - } - - public function test_get_client_with_secret_only() - { - // arrange - $repo = $this->getClientRepository(); - - // act - $client = $repo->get('client1id', 'client1secret'); - - // assert - $this->assertIsClient($client, false); - } - - public function test_get_client_with_redirect_uri_only() - { - $repo = $this->getClientRepository(); - $client = $repo->get('client1id', null, 'http://example1.com/callback'); - - $this->assertIsClient($client); - } - - public function test_get_client_with_secret_and_redirect_uri() - { - $repo = $this->getClientRepository(); - - $client = $repo->get('client1id', 'client1secret', 'http://example1.com/callback'); - - $this->assertIsClient($client); - } - - public function test_null_is_returned_with_unexisting_client() - { - $repo = $this->getClientRepository(); - - $result1 = $repo->get('client3id', 'client3secret'); - $result2 = $repo->get('client3id', null, 'http://example3.com/callback'); - $result3 = $repo->get('client3id', 'client3secret', 'http://example3.com/callback'); - - $this->assertNull($result1); - $this->assertNull($result2); - $this->assertNull($result3); - } - - public function test_false_is_returned_with_invalid_grant() - { - $repo = $this->getClientRepository(); - $repo->limitClientsToGrants(true); - - $result = $repo->get('client1id', 'client1secret', 'http://example1.com/callback', 'grant2'); - - $this->assertTrue($repo->areClientsLimitedToGrants()); - $this->assertNull($result); - } - - public function test_client_is_returned_with_valid_grant() - { - $repo = $this->getClientRepository(); - $repo->limitClientsToGrants(true); - - $client = $repo->get('client1id', 'client1secret', 'http://example1.com/callback', 'grant1'); - - $this->assertTrue($repo->areClientsLimitedToGrants()); - $this->assertIsClient($client); - } - - public function test_it_returns_a_client_associated_with_a_valid_session() - { - $repo = $this->getClientRepository(); - - $session = m::mock('League\OAuth2\Server\Entity\SessionEntity'); - $session->shouldReceive('getId')->once()->andReturn(1); - - $result = $repo->getBySession($session); - $this->assertIsClient($result, false); - } - - public function test_it_returns_null_with_an_invalid_session() - { - $repo = $this->getClientRepository(); - - $session = m::mock('League\OAuth2\Server\Entity\SessionEntity'); - $session->shouldReceive('getId')->once()->andReturn(20); - - $result = $repo->getBySession($session); - $this->assertNull($result); - } - - public function assertIsClient($client, $redirectUri = true) - { - $this->assertInstanceOf('League\OAuth2\Server\Entity\ClientEntity', $client); - $this->assertEquals('client1id', $client->getId()); - $this->assertEquals('client1secret', $client->getSecret()); - $this->assertEquals('client1', $client->getName()); - if ($redirectUri) { - $this->assertEquals('http://example1.com/callback', $client->getRedirectUri()); - } else { - $this->assertNull($client->getRedirectUri()); - } - } -} diff --git a/tests/integration/FluentRefreshTokenTest.php b/tests/integration/FluentRefreshTokenTest.php deleted file mode 100644 index 245fbe71..00000000 --- a/tests/integration/FluentRefreshTokenTest.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use LucaDegasperi\OAuth2Server\Storage\FluentRefreshToken; -use Mockery as m; - -class FluentRefreshTokenTest extends AbstractDBTestCase -{ - public function getRefreshTokenRepository() - { - $server = m::mock('League\OAuth2\Server\AbstractServer'); - $repo = new FluentRefreshToken($this->app['db']); - $repo->setServer($server); - - return $repo; - } - - public function test_it_fetches_a_refresh_token_with_a_valid_token() - { - $repo = $this->getRefreshTokenRepository(); - - $result = $repo->get('totallyarefreshtoken1'); - - $this->assertInstanceOf('League\OAuth2\Server\Entity\RefreshTokenEntity', $result); - $this->assertEquals('totallyarefreshtoken1', $result->getId()); - $this->assertInternalType('int', $result->getExpireTime()); - } - - public function test_it_returns_null_with_an_invalid_token() - { - $repo = $this->getRefreshTokenRepository(); - - $result = $repo->get('invalid_refresh_token'); - - $this->assertNull($result); - } - - public function test_it_deletes_a_refresh_token() - { - $token = m::mock('League\OAuth2\Server\Entity\RefreshTokenEntity'); - $token->shouldReceive('getId')->once()->andReturn('totallyarefreshtoken1'); - - $repo = $this->getRefreshTokenRepository(); - - $repo->delete($token); - $result = $repo->get('totallyarefreshtoken1'); - - $this->assertNull($result); - } - - public function test_it_creates_a_refresh_token() - { - $repo = $this->getRefreshTokenRepository(); - - $time = time() + 120; - $result = $repo->create('newrefreshtoken', $time, 'totallyanaccesstoken2'); - - $this->assertInstanceOf('League\OAuth2\Server\Entity\RefreshTokenEntity', $result); - $this->assertEquals('newrefreshtoken', $result->getId()); - $this->assertInternalType('int', $result->getExpireTime()); - $this->assertEquals($time, $result->getExpireTime()); - } -} diff --git a/tests/integration/FluentScopeTest.php b/tests/integration/FluentScopeTest.php deleted file mode 100644 index b4654bdb..00000000 --- a/tests/integration/FluentScopeTest.php +++ /dev/null @@ -1,108 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use LucaDegasperi\OAuth2Server\Storage\FluentScope; -use Mockery as m; - -class FluentScopeTest extends AbstractDBTestCase -{ - public function getScopeRepository() - { - $server = m::mock('League\OAuth2\Server\AbstractServer'); - $repo = new FluentScope($this->app['db']); - $repo->setServer($server); - - return $repo; - } - - public function test_get_unexisting_scope() - { - $repo = $this->getScopeRepository(); - $repo->limitClientsToScopes(true); - $repo->limitScopesToGrants(true); - - $result = $repo->get('scope3', 'grant3', 'client3id'); - - $this->assertTrue($repo->areClientsLimitedToScopes()); - $this->assertTrue($repo->areScopesLimitedToGrants()); - $this->assertNull($result); - } - - public function test_get_scope_with_client_only() - { - $repo = $this->getScopeRepository(); - $repo->limitClientsToScopes(true); - - $result = $repo->get('scope1', null, 'client1id'); - - $this->assertIsScope($result); - } - - public function test_get_scope_with_invalid_client_only() - { - $repo = $this->getScopeRepository(); - $repo->limitClientsToScopes(true); - - $result = $repo->get('scope1', null, 'invalidclientid'); - - $this->assertTrue($repo->areClientsLimitedToScopes()); - $this->assertNull($result); - } - - public function test_get_scope_with_grant_only() - { - $repo = $this->getScopeRepository(); - $repo->limitScopesToGrants(true); - - $result = $repo->get('scope1', 'grant1'); - - $this->assertIsScope($result); - } - - public function test_get_scope_with_invalid_grant_only() - { - $repo = $this->getScopeRepository(); - $repo->limitScopesToGrants(true); - - $result = $repo->get('scope1', 'invalidgrant'); - - $this->assertTrue($repo->areScopesLimitedToGrants()); - $this->assertNull($result); - } - - public function test_get_scope_with_client_and_grant() - { - $repo = $this->getScopeRepository(); - $repo->limitClientsToScopes(true); - $repo->limitScopesToGrants(true); - - $result = $repo->get('scope1', 'grant1', 'client1id'); - - $this->assertTrue($repo->areClientsLimitedToScopes()); - $this->assertTrue($repo->areScopesLimitedToGrants()); - $this->assertIsScope($result); - } - - public function test_get_scope() - { - $repo = $this->getScopeRepository(); - $result = $repo->get('scope1'); - - $this->assertIsScope($result); - } - - public function assertIsScope($result) - { - $this->assertInstanceOf('League\OAuth2\Server\Entity\ScopeEntity', $result); - $this->assertEquals('scope1', $result->getId()); - $this->assertEquals('Scope 1 Description', $result->getDescription()); - } -} diff --git a/tests/integration/FluentSessionTest.php b/tests/integration/FluentSessionTest.php deleted file mode 100644 index 83c55a8a..00000000 --- a/tests/integration/FluentSessionTest.php +++ /dev/null @@ -1,138 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use LucaDegasperi\OAuth2Server\Storage\FluentSession; -use Mockery as m; - -class FluentSessionTest extends AbstractDBTestCase -{ - public function getSessionRepository() - { - $emitter = m::mock('League\Event\Emitter'); - $emitter->shouldReceive('emit')->once(); - $server = m::mock('League\OAuth2\Server\AbstractServer'); - $server->shouldReceive('getEventEmitter')->once()->andReturn($emitter); - $repo = new FluentSession($this->app['db']); - $repo->setServer($server); - - return $repo; - } - - public function test_session_is_created() - { - $repo = $this->getSessionRepository(); - - $id = $repo->create('user', '1', 'client1'); - $session = $repo->get($id); - - $this->assertInstanceOf('League\OAuth2\Server\Entity\SessionEntity', $session); - $this->assertEquals('user', $session->getOwnerType()); - $this->assertEquals('1', $session->getOwnerId()); - } - - public function test_null_is_returned_when_invalid_session_is_requested() - { - $repo = $this->getSessionRepository(); - $session = $repo->get(20); - $this->assertNull($session); - } - - public function test_scope_is_associated() - { - $session = m::mock('League\OAuth2\Server\Entity\SessionEntity'); - $session->shouldReceive('getId')->twice()->andReturn(1); - - $scope1 = m::mock('League\OAuth2\Server\Entity\ScopeEntity'); - $scope1->shouldReceive('getId')->once()->andReturn('scope1'); - - $scope2 = m::mock('League\OAuth2\Server\Entity\ScopeEntity'); - $scope2->shouldReceive('getId')->once()->andReturn('scope2'); - - $repo = $this->getSessionRepository(); - - $repo->associateScope($session, $scope1); - $repo->associateScope($session, $scope2); - - $result = $repo->getScopes($session); - - $this->assertInternalType('array', $result); - $this->assertEquals(2, count($result)); - $first = $result[0]; - $this->assertInstanceOf('League\OAuth2\Server\Entity\ScopeEntity', $first); - } - - public function test_null_is_returned_when_session_is_requested_by_invalid_auth_code() - { - $authCode = m::mock('League\OAuth2\Server\Entity\AuthCodeEntity'); - $authCode->shouldReceive('getId')->once()->andReturn('unexistingcode'); - - $repo = $this->getSessionRepository(); - - $result = $repo->getByAuthCode($authCode); - - $this->assertNull($result); - } - - public function test_a_session_is_returned_when_session_is_requested_by_valid_auth_code() - { - $authCode = m::mock('League\OAuth2\Server\Entity\AuthCodeEntity'); - $authCode->shouldReceive('getId')->once()->andReturn('totallyanauthcode1'); - - $repo = $this->getSessionRepository(); - - $session = $repo->getByAuthCode($authCode); - - $this->assertInstanceOf('League\OAuth2\Server\Entity\SessionEntity', $session); - $this->assertEquals('user', $session->getOwnerType()); - $this->assertEquals('1', $session->getOwnerId()); - } - - public function test_null_is_returned_when_session_is_requested_by_invalid_access_token() - { - $accessToken = m::mock('League\OAuth2\Server\Entity\AccessTokenEntity'); - $accessToken->shouldReceive('getId')->once()->andReturn('unexistingaccesstoken'); - - $repo = $this->getSessionRepository(); - - $result = $repo->getByAccessToken($accessToken); - - $this->assertNull($result); - } - - public function test_a_session_is_returned_when_session_is_requested_by_valid_access_token() - { - $accessToken = m::mock('League\OAuth2\Server\Entity\AccessTokenEntity'); - $accessToken->shouldReceive('getId')->once()->andReturn('totallyanaccesstoken1'); - - $repo = $this->getSessionRepository(); - - $session = $repo->getByAccessToken($accessToken); - - $this->assertInstanceOf('League\OAuth2\Server\Entity\SessionEntity', $session); - $this->assertEquals('user', $session->getOwnerType()); - $this->assertEquals('1', $session->getOwnerId()); - } - - /*public function test_session_is_deleted() - { - $repo = new FluentSession(); - - $repo->delete('client1id', 'user', '1'); - - $session = DB::table('oauth_sessions') - ->where('client_id', '=', 'client1id') - ->where('owner_type', '=', 'user') - ->where('owner_id', '=', '1') - ->first(); - - $this->assertNull($session, 'no session found'); - }*/ -} diff --git a/tests/unit/LucaDegasperi/OAuth2Server/AuthorizerSpec.php b/tests/unit/LucaDegasperi/OAuth2Server/AuthorizerSpec.php deleted file mode 100644 index 57b3c68c..00000000 --- a/tests/unit/LucaDegasperi/OAuth2Server/AuthorizerSpec.php +++ /dev/null @@ -1,189 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace unit\LucaDegasperi\OAuth2Server; - -use League\OAuth2\Server\AuthorizationServer; -use League\OAuth2\Server\Entity\AccessTokenEntity; -use League\OAuth2\Server\Entity\ClientEntity; -use League\OAuth2\Server\Entity\SessionEntity; -use League\OAuth2\Server\Grant\AuthCodeGrant; -use League\OAuth2\Server\ResourceServer; -use League\OAuth2\Server\Util\RedirectUri; -use LucaDegasperi\OAuth2Server\Exceptions\NoActiveAccessTokenException; -use PhpSpec\ObjectBehavior; -use Symfony\Component\HttpFoundation\Request; - -class AuthorizerSpec extends ObjectBehavior -{ - public function let(AuthorizationServer $issuer, ResourceServer $checker) - { - $this->beConstructedWith($issuer, $checker); - } - - public function it_is_initializable() - { - $this->shouldHaveType('LucaDegasperi\OAuth2Server\Authorizer'); - } - - public function it_issues_an_access_token(AuthorizationServer $issuer) - { - $issuer->issueAccessToken()->willReturn('foo')->shouldBeCalled(); - - $this->issueAccessToken()->shouldReturn('foo'); - } - - public function it_checks_the_auth_code_request_parameters(AuthorizationServer $issuer, AuthCodeGrant $authCodeGrant) - { - $authCodeGrant->checkAuthorizeParams()->willReturn(['foo' => 'bar'])->shouldBeCalled(); - $issuer->getGrantType('authorization_code')->willReturn($authCodeGrant)->shouldBeCalled(); - - $this->checkAuthCodeRequest()->shouldReturn(null); - $this->getAuthCodeRequestParams()->shouldBe(['foo' => 'bar']); - $this->getAuthCodeRequestParam('foo')->shouldBe('bar'); - } - - public function it_issues_an_auth_code(AuthorizationServer $issuer, AuthCodeGrant $authCodeGrant) - { - $authCodeGrant->newAuthorizeRequest('user', '1', ['foo' => 'bar'])->willReturn('baz')->shouldBeCalled(); - $issuer->getGrantType('authorization_code')->willReturn($authCodeGrant)->shouldBeCalled(); - - $this->issueAuthCode('user', '1', ['foo' => 'bar'])->shouldReturn('baz'); - } - - public function it_returns_the_current_scopes(ResourceServer $checker, AccessTokenEntity $accessTokenEntity) - { - $accessTokenEntity->getScopes()->willReturn(['foo', 'bar']); - $checker->getAccessToken()->willReturn($accessTokenEntity)->shouldBeCalled(); - $this->getScopes()->shouldReturn(['foo', 'bar']); - } - - public function it_throws_exception_if_current_scopes_accessed_without_active_access_token(ResourceServer $checker) - { - $checker->getAccessToken()->willReturn(null); - $this->shouldThrow(NoActiveAccessTokenException::class)->during('getScopes'); - } - - public function it_checks_if_a_scope_is_included_into_the_current_ones(ResourceServer $checker, AccessTokenEntity $accessTokenEntity) - { - $accessTokenEntity->hasScope('foo')->willReturn(true)->shouldBeCalled(); - $checker->getAccessToken()->willReturn($accessTokenEntity)->shouldBeCalled(); - $this->hasScope('foo')->shouldReturn(true); - - $accessTokenEntity->hasScope('foo')->willReturn(false)->shouldBeCalled(); - $checker->getAccessToken()->willReturn($accessTokenEntity)->shouldBeCalled(); - $this->hasScope('foo')->shouldReturn(false); - } - - public function it_checks_if_multiple_invalid_scopes_are_included_into_the_current_ones(ResourceServer $checker, AccessTokenEntity $accessTokenEntity) - { - $accessTokenEntity->hasScope('foo')->willReturn(false)->shouldBecalled(); - $accessTokenEntity->hasScope('bar')->willReturn(false)->shouldNotBeCalled(); - $checker->getAccessToken()->willReturn($accessTokenEntity)->shouldBeCalled(); - $this->hasScope(['foo', 'bar'])->shouldReturn(false); - } - - public function it_checks_if_multiple_mixed_scopes_are_included_into_the_current_ones(ResourceServer $checker, AccessTokenEntity $accessTokenEntity) - { - $accessTokenEntity->hasScope('foo')->willReturn(true)->shouldBecalled(); - $accessTokenEntity->hasScope('bar')->willReturn(false)->shouldBeCalled(); - $checker->getAccessToken()->willReturn($accessTokenEntity)->shouldBeCalledTimes(2); - $this->hasScope(['foo', 'bar'])->shouldReturn(false); - } - - public function it_checks_if_multiple_valid_scopes_are_included_into_the_current_ones(ResourceServer $checker, AccessTokenEntity $accessTokenEntity) - { - $accessTokenEntity->hasScope('foo')->willReturn(true)->shouldBecalled(); - $accessTokenEntity->hasScope('bar')->willReturn(true)->shouldBeCalled(); - $checker->getAccessToken()->willReturn($accessTokenEntity)->shouldBeCalledTimes(2); - $this->hasScope(['foo', 'bar'])->shouldReturn(true); - } - - public function it_throws_if_scopes_are_checked_without_active_access_token(ResourceServer $checker) - { - $checker->getAccessToken()->willReturn(null); - $this->shouldThrow(NoActiveAccessTokenException::class)->during('hasScope', ['foo']); - } - - public function it_returns_the_resource_owner_id(ResourceServer $checker, AccessTokenEntity $accessTokenEntity, SessionEntity $sessionEntity) - { - $sessionEntity->getOwnerId()->willReturn('1')->shouldBeCalled(); - $accessTokenEntity->getSession()->willReturn($sessionEntity)->shouldBeCalled(); - $checker->getAccessToken()->willReturn($accessTokenEntity)->shouldBeCalled(); - $this->getResourceOwnerId()->shouldReturn('1'); - } - - public function it_throws_exception_if_resource_owner_id_accessed_without_active_session(ResourceServer $checker) - { - $checker->getAccessToken()->willReturn(null); - $this->shouldThrow(NoActiveAccessTokenException::class)->during('getResourceOwnerId'); - } - - public function it_returns_the_resource_owner_type(ResourceServer $checker, AccessTokenEntity $accessTokenEntity, SessionEntity $sessionEntity) - { - $sessionEntity->getOwnerType()->willReturn('user')->shouldBeCalled(); - $accessTokenEntity->getSession()->willReturn($sessionEntity)->shouldBeCalled(); - $checker->getAccessToken()->willReturn($accessTokenEntity)->shouldBeCalled(); - $this->getResourceOwnerType()->shouldReturn('user'); - } - - public function test_it_throws_exception_if_resource_owner_type_accessed_without_active_session(ResourceServer $checker) - { - $checker->getAccessToken()->willReturn(null); - $this->shouldThrow(NoActiveAccessTokenException::class)->during('getResourceOwnerType'); - } - - public function it_returns_the_client_id(ResourceServer $checker, AccessTokenEntity $accessTokenEntity, SessionEntity $sessionEntity, ClientEntity $clientEntity) - { - $clientEntity->getId()->willReturn('1')->shouldBeCalled(); - $sessionEntity->getClient()->willReturn($clientEntity)->shouldBeCalled(); - $accessTokenEntity->getSession()->willReturn($sessionEntity)->shouldBeCalled(); - $checker->getAccessToken()->willReturn($accessTokenEntity)->shouldBeCalled(); - $this->getClientId()->shouldReturn('1'); - } - - public function it_returns_the_issuer(AuthorizationServer $issuer) - { - $this->getIssuer()->shouldReturn($issuer); - } - - public function it_returns_the_checker(ResourceServer $checker) - { - $this->getChecker()->shouldReturn($checker); - } - - public function it_sets_the_request_to_the_issuer_and_checker(AuthorizationServer $issuer, ResourceServer $checker, Request $request) - { - $issuer->setRequest($request)->shouldBeCalled(); - $checker->setRequest($request)->shouldBeCalled(); - - $this->setRequest($request); - } - - public function it_validates_an_access_token(ResourceServer $checker) - { - $checker->isValidRequest(false, null)->shouldBeCalled(); - - $this->validateAccessToken(false, null); - } - - public function it_generates_a_redirect_uri_when_the_user_denies_the_auth_code() - { - $this->authCodeRequestDeniedRedirectUri()->shouldReturn('?error=access_denied&error_description=The+resource+owner+or+authorization+server+denied+the+request.'); - } - - public function it_sets_a_redirect_uri_generator(RedirectUri $redirectUri) - { - $this->setRedirectUriGenerator($redirectUri); - - $this->getRedirectUriGenerator()->shouldReturn($redirectUri); - } -} diff --git a/tests/unit/LucaDegasperi/OAuth2Server/Middleware/CheckAuthCodeRequestMiddlewareSpec.php b/tests/unit/LucaDegasperi/OAuth2Server/Middleware/CheckAuthCodeRequestMiddlewareSpec.php deleted file mode 100644 index ab6a22d7..00000000 --- a/tests/unit/LucaDegasperi/OAuth2Server/Middleware/CheckAuthCodeRequestMiddlewareSpec.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace unit\LucaDegasperi\OAuth2Server\Middleware; - -use Illuminate\Http\Request; -use League\OAuth2\Server\Exception\InvalidRequestException; -use LucaDegasperi\OAuth2Server\Authorizer; -use PhpSpec\ObjectBehavior; - -class CheckAuthCodeRequestMiddlewareSpec extends ObjectBehavior -{ - private $next = null; - - public function __construct() - { - $this->next = (function () { - throw new MiddlewareException('Called execution of $next'); - }); - } - - public function let(Authorizer $authorizer) - { - $this->beConstructedWith($authorizer); - } - - public function it_is_initializable() - { - $this->shouldHaveType('LucaDegasperi\OAuth2Server\Middleware\CheckAuthCodeRequestMiddleware'); - } - - public function it_calls_the_next_middleware_on_success(Request $request, Authorizer $authorizer) - { - $authorizer->checkAuthCodeRequest()->shouldBeCalled(); - $authorizer->setRequest($request)->shouldBeCalled(); - - $this->shouldThrow(new MiddlewareException('Called execution of $next')) - ->during('handle', [$request, $this->next]); - } - - public function it_exits_on_error(Request $request, Authorizer $authorizer) - { - $authorizer->checkAuthCodeRequest()->willThrow(new InvalidRequestException('client_id'))->shouldBeCalled(); - $authorizer->setRequest($request)->shouldBeCalled(); - - $this->shouldNotThrow(new MiddlewareException('Called execution of $next')) - ->during('handle', [$request, $this->next]); - } -} - -class MiddlewareException extends \Exception -{ -} diff --git a/tests/unit/LucaDegasperi/OAuth2Server/Middleware/OAuthClientOwnerMiddlewareSpec.php b/tests/unit/LucaDegasperi/OAuth2Server/Middleware/OAuthClientOwnerMiddlewareSpec.php deleted file mode 100644 index f990dc1c..00000000 --- a/tests/unit/LucaDegasperi/OAuth2Server/Middleware/OAuthClientOwnerMiddlewareSpec.php +++ /dev/null @@ -1,65 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace unit\LucaDegasperi\OAuth2Server\Middleware; - -use Illuminate\Http\Request; -use League\OAuth2\Server\Exception\AccessDeniedException; -use LucaDegasperi\OAuth2Server\Authorizer; -use PhpSpec\ObjectBehavior; - -/** - * This is the oauth client middleware spec class. - * - * @author Vincent Klaiber - */ -class OAuthClientOwnerMiddlewareSpec extends ObjectBehavior -{ - private $next = null; - - public function __construct() - { - $this->next = (function () { - throw new MiddlewareException('Called execution of $next'); - }); - } - - public function let(Authorizer $authorizer) - { - $this->beConstructedWith($authorizer); - } - - public function it_is_initializable() - { - $this->shouldHaveType('LucaDegasperi\OAuth2Server\Middleware\OAuthClientOwnerMiddleware'); - } - - public function it_passes_if_resource_owners_are_allowed(Request $request, Authorizer $authorizer) - { - $authorizer->getResourceOwnerType()->willReturn('client')->shouldBeCalled(); - $authorizer->setRequest($request)->shouldBeCalled(); - - $this->shouldThrow(new MiddlewareException('Called execution of $next')) - ->during('handle', [$request, $this->next]); - } - - public function it_blocks_if_resource_owners_are_not_allowed(Request $request, Authorizer $authorizer) - { - $authorizer->getResourceOwnerType()->willReturn('user')->shouldBeCalled(); - $authorizer->setRequest($request)->shouldBeCalled(); - - $this->shouldThrow(new AccessDeniedException()) - ->during('handle', [$request, $this->next]); - - $this->shouldNotThrow(new MiddlewareException('Called execution of $next')) - ->during('handle', [$request, $this->next]); - } -} diff --git a/tests/unit/LucaDegasperi/OAuth2Server/Middleware/OAuthMiddlewareSpec.php b/tests/unit/LucaDegasperi/OAuth2Server/Middleware/OAuthMiddlewareSpec.php deleted file mode 100644 index c36b97be..00000000 --- a/tests/unit/LucaDegasperi/OAuth2Server/Middleware/OAuthMiddlewareSpec.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace unit\LucaDegasperi\OAuth2Server\Middleware; - -use Illuminate\Http\Request; -use League\OAuth2\Server\Exception\AccessDeniedException; -use League\OAuth2\Server\Exception\InvalidScopeException; -use LucaDegasperi\OAuth2Server\Authorizer; -use PhpSpec\ObjectBehavior; - -class OAuthMiddlewareSpec extends ObjectBehavior -{ - private $next = null; - - public function __construct() - { - $this->next = (function () { - throw new MiddlewareException('Called execution of $next'); - }); - } - - public function let(Authorizer $authorizer) - { - $this->beConstructedWith($authorizer, false); - } - - public function it_is_initializable() - { - $this->shouldHaveType('LucaDegasperi\OAuth2Server\Middleware\OAuthMiddleware'); - } - - public function it_blocks_invalid_access_tokens(Request $request, Authorizer $authorizer) - { - $authorizer->validateAccessToken(false)->willThrow(new AccessDeniedException())->shouldBeCalled(); - $authorizer->setRequest($request)->shouldBeCalled(); - - $this->shouldNotThrow(new MiddlewareException('Called execution of $next')) - ->during('handle', [$request, $this->next]); - } - - public function it_passes_with_valid_access_token(Request $request, Authorizer $authorizer) - { - $authorizer->validateAccessToken(false)->shouldBeCalled(); - $authorizer->setRequest($request)->shouldBeCalled(); - - $this->shouldThrow(new MiddlewareException('Called execution of $next')) - ->during('handle', [$request, $this->next]); - } - - public function it_block_invalid_scopes(Request $request, Authorizer $authorizer) - { - $authorizer->validateAccessToken(false)->shouldBeCalled(); - $authorizer->setRequest($request)->shouldBeCalled(); - $authorizer->hasScope(['baz'])->willReturn(false)->shouldBeCalled(); - - $this->shouldThrow(new InvalidScopeException('baz')) - ->during('handle', [$request, $this->next, 'baz']); - - $this->shouldNotThrow(new MiddlewareException('Called execution of $next')) - ->during('handle', [$request, $this->next, 'baz']); - } - - public function it_passes_with_valid_scopes(Request $request, Authorizer $authorizer) - { - $authorizer->validateAccessToken(false)->shouldBeCalled(); - $authorizer->setRequest($request)->shouldBeCalled(); - $authorizer->hasScope(['baz'])->willReturn(true)->shouldBeCalled(); - - $this->shouldNotThrow(new InvalidScopeException('baz')) - ->during('handle', [$request, $this->next, 'baz']); - - $this->shouldThrow(new MiddlewareException('Called execution of $next')) - ->during('handle', [$request, $this->next, 'baz']); - } -} diff --git a/tests/unit/LucaDegasperi/OAuth2Server/Middleware/OAuthUserOwnerMiddlewareSpec.php b/tests/unit/LucaDegasperi/OAuth2Server/Middleware/OAuthUserOwnerMiddlewareSpec.php deleted file mode 100644 index cfc026e4..00000000 --- a/tests/unit/LucaDegasperi/OAuth2Server/Middleware/OAuthUserOwnerMiddlewareSpec.php +++ /dev/null @@ -1,65 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace unit\LucaDegasperi\OAuth2Server\Middleware; - -use Illuminate\Http\Request; -use League\OAuth2\Server\Exception\AccessDeniedException; -use LucaDegasperi\OAuth2Server\Authorizer; -use PhpSpec\ObjectBehavior; - -/** - * This is the oauth user middleware spec class. - * - * @author Vincent Klaiber - */ -class OAuthUserOwnerMiddlewareSpec extends ObjectBehavior -{ - private $next = null; - - public function __construct() - { - $this->next = (function () { - throw new MiddlewareException('Called execution of $next'); - }); - } - - public function let(Authorizer $authorizer) - { - $this->beConstructedWith($authorizer); - } - - public function it_is_initializable() - { - $this->shouldHaveType('LucaDegasperi\OAuth2Server\Middleware\OAuthUserOwnerMiddleware'); - } - - public function it_passes_if_resource_owners_are_allowed(Request $request, Authorizer $authorizer) - { - $authorizer->getResourceOwnerType()->willReturn('user')->shouldBeCalled(); - $authorizer->setRequest($request)->shouldBeCalled(); - - $this->shouldThrow(new MiddlewareException('Called execution of $next')) - ->during('handle', [$request, $this->next]); - } - - public function it_blocks_if_resource_owners_are_not_allowed(Request $request, Authorizer $authorizer) - { - $authorizer->getResourceOwnerType()->willReturn('client')->shouldBeCalled(); - $authorizer->setRequest($request)->shouldBeCalled(); - - $this->shouldThrow(new AccessDeniedException()) - ->during('handle', [$request, $this->next]); - - $this->shouldNotThrow(new MiddlewareException('Called execution of $next')) - ->during('handle', [$request, $this->next]); - } -}