From 10c77671f94afbd7e4d9a9b5c6703efc6935c00c Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Mon, 1 Oct 2018 18:10:56 +0200 Subject: [PATCH 1/3] Added oauth2 data to user --- CHANGELOG.md | 32 +- composer.json | 2 +- composer.lock | 370 +++++++++--------- docs/book/v1/authenticated-user.md | 56 +++ docs/book/{ => v1}/authorization-server.md | 46 +-- docs/book/{ => v1}/grant/auth_code.md | 0 docs/book/{ => v1}/grant/auth_code.png | Bin .../book/{ => v1}/grant/client_credentials.md | 0 docs/book/{ => v1}/grant/implicit.md | 0 docs/book/{ => v1}/grant/password.md | 0 docs/book/{ => v1}/grant/refresh_token.md | 0 docs/book/{ => v1}/intro.md | 28 +- docs/book/{ => v1}/usage.md | 0 mkdocs.yml | 17 +- src/OAuth2Adapter.php | 33 +- src/OAuth2AdapterFactory.php | 4 +- test/OAuth2AdapterFactoryTest.php | 21 + test/OAuth2AdapterTest.php | 60 ++- 18 files changed, 405 insertions(+), 264 deletions(-) create mode 100644 docs/book/v1/authenticated-user.md rename docs/book/{ => v1}/authorization-server.md (84%) rename docs/book/{ => v1}/grant/auth_code.md (100%) rename docs/book/{ => v1}/grant/auth_code.png (100%) rename docs/book/{ => v1}/grant/client_credentials.md (100%) rename docs/book/{ => v1}/grant/implicit.md (100%) rename docs/book/{ => v1}/grant/password.md (100%) rename docs/book/{ => v1}/grant/refresh_token.md (100%) rename docs/book/{ => v1}/intro.md (91%) rename docs/book/{ => v1}/usage.md (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82c399d..31cbf53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,17 +2,20 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. -## 0.4.3 - 2018-05-09 +## 1.0.0 - TBD ### Added +- [#41](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/41) Allows + existing PDO service to be used. This will allow us to reuse existing pdo + services instead of opening up a second connection for oauth. - [#42](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/42) Adds `TokenEndpointHandler`, `AuthorizationMiddleware` and `AuthorizationHandler` in the `Zend\Expressive\Authentication\OAuth2` namespace to [implement an authorization server](docs/book/authorization-server.md). ### Changed -- [#42](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/42) Splits +- [#42](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/42) Splits `Zend\Expressive\Authentication\OAuth2\OAuth2Middleware` into individual implementations that allow [OAuth RFC-6749](https://tools.ietf.org/html/rfc6749) compliant authorization server implementations. @@ -27,6 +30,31 @@ All notable changes to this project will be documented in this file, in reverse ### Fixed +- [#44](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/44/) Fixes + revocation of access token for PDO repository +- [#45](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/45) Fixes + issue with empty scope being passed throwing exception. + +## 0.4.3 - 2018-05-09 + +### Added + +- Nothing. + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + - Removes auto-requiring of the encryption key via the configuration unless the default file actually exists and is readable. As the configuration is processed in every request, this is necessary to prevent issues when the file does not exist (e.g., if the user has specified an alternate location). diff --git a/composer.json b/composer.json index cca3743..8bd66a7 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "psr/container": "^1.0", "psr/http-message": "^1.0.1", "psr/http-server-middleware": "^1.0", - "zendframework/zend-expressive-authentication": "^0.4.0" + "zendframework/zend-expressive-authentication": "^1.0" }, "require-dev": { "phpunit/phpunit": "^7.0.1", diff --git a/composer.lock b/composer.lock index a4d4585..e04b4b8 100644 --- a/composer.lock +++ b/composer.lock @@ -1,32 +1,32 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "714de628f77f24ce564890f1614c06a3", + "content-hash": "d8f6be31fe18e6a9ab7a576fb39fd9f2", "packages": [ { "name": "defuse/php-encryption", - "version": "v2.1.0", + "version": "v2.2.1", "source": { "type": "git", "url": "https://github.com/defuse/php-encryption.git", - "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689" + "reference": "0f407c43b953d571421e0020ba92082ed5fb7620" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/defuse/php-encryption/zipball/5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689", - "reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689", + "url": "https://api.github.com/repos/defuse/php-encryption/zipball/0f407c43b953d571421e0020ba92082ed5fb7620", + "reference": "0f407c43b953d571421e0020ba92082ed5fb7620", "shasum": "" }, "require": { "ext-openssl": "*", - "paragonie/random_compat": "~2.0", + "paragonie/random_compat": ">= 2", "php": ">=5.4.0" }, "require-dev": { - "nikic/php-parser": "^2.0|^3.0", + "nikic/php-parser": "^2.0|^3.0|^4.0", "phpunit/phpunit": "^4|^5" }, "bin": [ @@ -67,20 +67,20 @@ "security", "symmetric key cryptography" ], - "time": "2017-05-18T21:28:48+00:00" + "time": "2018-07-24T23:27:56+00:00" }, { "name": "lcobucci/jwt", - "version": "3.2.2", + "version": "3.2.4", "source": { "type": "git", "url": "https://github.com/lcobucci/jwt.git", - "reference": "0b5930be73582369e10c4d4bb7a12bac927a203c" + "reference": "c9704b751315d21735dc98d78d4f37bd73596da7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/0b5930be73582369e10c4d4bb7a12bac927a203c", - "reference": "0b5930be73582369e10c4d4bb7a12bac927a203c", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/c9704b751315d21735dc98d78d4f37bd73596da7", + "reference": "c9704b751315d21735dc98d78d4f37bd73596da7", "shasum": "" }, "require": { @@ -125,7 +125,7 @@ "JWS", "jwt" ], - "time": "2017-09-01T08:23:26+00:00" + "time": "2018-08-03T11:23:50+00:00" }, { "name": "league/event", @@ -247,16 +247,16 @@ }, { "name": "paragonie/random_compat", - "version": "v2.0.11", + "version": "v2.0.17", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8" + "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8", - "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/29af24f25bab834fcbb38ad2a69fa93b867e070d", + "reference": "29af24f25bab834fcbb38ad2a69fa93b867e070d", "shasum": "" }, "require": { @@ -288,10 +288,11 @@ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ "csprng", + "polyfill", "pseudorandom", "random" ], - "time": "2017-09-27T21:40:39+00:00" + "time": "2018-07-04T16:31:37+00:00" }, { "name": "psr/container", @@ -500,16 +501,16 @@ }, { "name": "zendframework/zend-expressive-authentication", - "version": "0.4.0", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-expressive-authentication.git", - "reference": "2fea5529d78cddc26921e46e87ea886ccb8cc7d1" + "reference": "52594067ffc149cd7defb87388e2c33707b5203f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-expressive-authentication/zipball/2fea5529d78cddc26921e46e87ea886ccb8cc7d1", - "reference": "2fea5529d78cddc26921e46e87ea886ccb8cc7d1", + "url": "https://api.github.com/repos/zendframework/zend-expressive-authentication/zipball/52594067ffc149cd7defb87388e2c33707b5203f", + "reference": "52594067ffc149cd7defb87388e2c33707b5203f", "shasum": "" }, "require": { @@ -561,7 +562,7 @@ "zend-expressive", "zf" ], - "time": "2018-03-15T17:19:40+00:00" + "time": "2018-08-27T15:09:06+00:00" } ], "packages-dev": [ @@ -621,25 +622,28 @@ }, { "name": "myclabs/deep-copy", - "version": "1.7.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", + "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" + "phpunit/phpunit": "^7.1" }, "type": "library", "autoload": { @@ -662,26 +666,26 @@ "object", "object graph" ], - "time": "2017-10-19T19:58:43+00:00" + "time": "2018-06-11T23:09:50+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.1", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^1.0.1", + "phar-io/version": "^2.0", "php": "^5.6 || ^7.0" }, "type": "library", @@ -717,20 +721,20 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-03-05T18:14:27+00:00" + "time": "2018-07-08T19:23:20+00:00" }, { "name": "phar-io/version", - "version": "1.0.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", "shasum": "" }, "require": { @@ -764,7 +768,7 @@ } ], "description": "Library for handling version information and constraints", - "time": "2017-03-05T17:38:23+00:00" + "time": "2018-07-08T19:19:57+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -920,33 +924,33 @@ }, { "name": "phpspec/prophecy", - "version": "1.7.5", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.8.x-dev" } }, "autoload": { @@ -979,31 +983,31 @@ "spy", "stub" ], - "time": "2018-02-19T10:16:54+00:00" + "time": "2018-08-05T17:53:17+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "6.0.1", + "version": "6.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f8ca4b604baf23dab89d87773c28cc07405189ba" + "reference": "865662550c384bc1db7e51d29aeda1c2c161d69a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f8ca4b604baf23dab89d87773c28cc07405189ba", - "reference": "f8ca4b604baf23dab89d87773c28cc07405189ba", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/865662550c384bc1db7e51d29aeda1c2c161d69a", + "reference": "865662550c384bc1db7e51d29aeda1c2c161d69a", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", "php": "^7.1", - "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-file-iterator": "^2.0", "phpunit/php-text-template": "^1.2.1", "phpunit/php-token-stream": "^3.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", + "sebastian/environment": "^3.1", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, @@ -1042,29 +1046,32 @@ "testing", "xunit" ], - "time": "2018-02-02T07:01:41+00:00" + "time": "2018-06-01T07:51:50+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.5", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + "reference": "050bedf145a257b1ff02746c31894800e5122946" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", + "reference": "050bedf145a257b1ff02746c31894800e5122946", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1079,7 +1086,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -1089,7 +1096,7 @@ "filesystem", "iterator" ], - "time": "2017-11-27T13:52:08+00:00" + "time": "2018-09-13T20:33:42+00:00" }, { "name": "phpunit/php-text-template", @@ -1232,35 +1239,35 @@ }, { "name": "phpunit/phpunit", - "version": "7.0.2", + "version": "7.3.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e2f8aa21bc54b6ba218bdd4f9e0dac1e9bc3b4e9" + "reference": "7b331efabbb628c518c408fdfcaf571156775de2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e2f8aa21bc54b6ba218bdd4f9e0dac1e9bc3b4e9", - "reference": "e2f8aa21bc54b6ba218bdd4f9e0dac1e9bc3b4e9", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7b331efabbb628c518c408fdfcaf571156775de2", + "reference": "7b331efabbb628c518c408fdfcaf571156775de2", "shasum": "" }, "require": { + "doctrine/instantiator": "^1.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", "php": "^7.1", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^6.0", - "phpunit/php-file-iterator": "^1.4.3", + "phpunit/php-code-coverage": "^6.0.7", + "phpunit/php-file-iterator": "^2.0.1", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^2.0", - "phpunit/phpunit-mock-objects": "^6.0", - "sebastian/comparator": "^2.1", + "sebastian/comparator": "^3.0", "sebastian/diff": "^3.0", "sebastian/environment": "^3.1", "sebastian/exporter": "^3.1", @@ -1269,10 +1276,14 @@ "sebastian/resource-operations": "^1.0", "sebastian/version": "^2.0.1" }, + "conflict": { + "phpunit/phpunit-mock-objects": "*" + }, "require-dev": { "ext-pdo": "*" }, "suggest": { + "ext-soap": "*", "ext-xdebug": "*", "phpunit/php-invoker": "^2.0" }, @@ -1282,7 +1293,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.0-dev" + "dev-master": "7.3-dev" } }, "autoload": { @@ -1308,95 +1319,31 @@ "testing", "xunit" ], - "time": "2018-02-26T07:03:12+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "6.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "e3249dedc2d99259ccae6affbc2684eac37c2e53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/e3249dedc2d99259ccae6affbc2684eac37c2e53", - "reference": "e3249dedc2d99259ccae6affbc2684eac37c2e53", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.1", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2018-02-15T05:27:38+00:00" + "time": "2018-09-08T15:14:29+00:00" }, { "name": "roave/security-advisories", "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "664836e89c7ecad3dbaabc1572ea752c0d532d80" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/664836e89c7ecad3dbaabc1572ea752c0d532d80", - "reference": "664836e89c7ecad3dbaabc1572ea752c0d532d80", - "shasum": "" - }, "conflict": { "3f/pygmentize": "<1.2", - "adodb/adodb-php": "<5.20.6", + "adodb/adodb-php": "<5.20.12", "amphp/artax": "<1.0.6|>=2,<2.0.6", + "amphp/http": "<1.0.1", "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", "aws/aws-sdk-php": ">=3,<3.2.1", + "brightlocal/phpwhois": "<=4.2.5", "bugsnag/bugsnag-laravel": ">=2,<2.0.2", - "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4", + "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4|>=3.4,<3.4.14|>=3.5,<3.5.17|>=3.6,<3.6.4", "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", "cartalyst/sentry": "<=2.1.6", "codeigniter/framework": "<=3.0.6", "composer/composer": "<=1.0.0-alpha11", "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/core": ">=2,<3.5.32", - "contao/core-bundle": ">=4,<4.4.8", + "contao/core": ">=2,<3.5.35", + "contao/core-bundle": ">=4,<4.4.18|>=4.5,<4.5.8", "contao/listing-bundle": ">=4,<4.4.8", "contao/newsletter-bundle": ">=4,<4.1", + "david-garcia/phpwhois": "<=4.3.1", "doctrine/annotations": ">=1,<1.2.7", "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", @@ -1407,89 +1354,116 @@ "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=8,<8.4.5", - "drupal/drupal": ">=8,<8.4.5", + "drupal/core": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", + "drupal/drupal": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", "erusev/parsedown": "<1.7", "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.3|>=5.4,<5.4.11.3|>=2017.8,<2017.8.1.1|>=2017.12,<2017.12.2.1", + "ezyang/htmlpurifier": "<4.1.1", "firebase/php-jwt": "<2", "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", + "fuel/core": "<1.8.1", "gree/jose": "<=2.2", "gregwar/rst": "<1.0.3", "guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1", - "illuminate/auth": ">=4,<4.0.99|>=4.1,<4.1.26", + "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", + "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30", "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", + "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "ivankristianto/phpwhois": "<=4.3", + "james-heinrich/getid3": "<1.9.9", "joomla/session": "<1.3.1", - "laravel/framework": ">=4,<4.0.99|>=4.1,<4.1.29", + "jsmitty12/phpwhois": "<5.1", + "kazist/phpwhois": "<=4.2.6", + "kreait/firebase-php": ">=3.2,<3.8.1", + "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", - "magento/magento1ce": ">=1.5.0.1,<1.9.3.2", + "magento/magento1ce": "<1.9.3.9", "magento/magento1ee": ">=1.9,<1.14.3.2", - "magento/magento2ce": ">=2,<2.2", + "magento/product-community-edition": ">=2,<2.2.5", "monolog/monolog": ">=1.8,<1.12", "namshi/jose": "<2.2", "onelogin/php-saml": "<2.10.4", + "openid/php-openid": "<2.3", "oro/crm": ">=1.7,<1.7.4", "oro/platform": ">=1.7,<1.7.4", "padraic/humbug_get_contents": "<1.1.2", "pagarme/pagarme-php": ">=0,<3", "paragonie/random_compat": "<2", + "paypal/merchant-sdk-php": "<3.12", "phpmailer/phpmailer": ">=5,<5.2.24", "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", + "phpwhois/phpwhois": "<=4.2.5", "phpxmlrpc/extras": "<0.6.1", "propel/propel": ">=2.0.0-alpha1,<=2.0.0-alpha7", "propel/propel1": ">=1,<=1.7.1", "pusher/pusher-php-server": "<2.2.1", "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", + "sensiolabs/connect": "<4.2.3", + "serluck/phpwhois": "<=4.2.6", "shopware/shopware": "<5.3.7", "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", "silverstripe/framework": ">=3,<3.3", "silverstripe/userforms": "<3", + "simple-updates/phpwhois": "<=1", "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", "simplesamlphp/simplesamlphp": "<1.15.2", "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", + "slim/slim": "<2.6", + "smarty/smarty": "<3.1.33", "socalnick/scn-social-auth": "<1.15.2", - "squizlabs/php_codesniffer": ">=1,<2.8.1", + "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", "stormpath/sdk": ">=0,<9.9.99", "swiftmailer/swiftmailer": ">=4,<5.4.5", + "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", + "sylius/sylius": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2", "symfony/dependency-injection": ">=2,<2.0.17", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2", - "symfony/http-foundation": ">=2,<2.3.27|>=2.4,<2.5.11|>=2.6,<2.6.6", + "symfony/http-foundation": ">=2,<2.7.49|>=2.8,<2.8.44|>=3,<3.3.18|>=3.4,<3.4.14|>=4,<4.0.14|>=4.1,<4.1.3", "symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", "symfony/routing": ">=2,<2.0.19", - "symfony/security": ">=2,<2.0.25|>=2.1,<2.1.13|>=2.2,<2.2.9|>=2.3,<2.3.37|>=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8.23,<2.8.25|>=3.2.10,<3.2.12|>=3.3.3,<3.3.5", - "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.6|>=2.8.23,<2.8.25|>=3,<3.0.6|>=3.2.10,<3.2.12|>=3.3.3,<3.3.5", - "symfony/security-csrf": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", + "symfony/security": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.37|>=3,<3.3.17|>=3.4,<3.4.7|>=4,<4.0.7", + "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/serializer": ">=2,<2.0.11", - "symfony/symfony": ">=2,<2.3.41|>=2.4,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", + "symfony/symfony": ">=2,<2.7.49|>=2.8,<2.8.44|>=3,<3.3.18|>=3.4,<3.4.14|>=4,<4.0.14|>=4.1,<4.1.3", "symfony/translation": ">=2,<2.0.17", "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", "thelia/backoffice-default-template": ">=2.1,<2.1.2", - "thelia/thelia": ">=2.1,<2.1.2|>=2.1.0-beta1,<2.1.3", + "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2", + "theonedemon/phpwhois": "<=4.2.5", "titon/framework": ">=0,<9.9.99", + "truckersmp/phpwhois": "<=4.3.1", "twig/twig": "<1.20", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.22|>=8,<8.7.5", + "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.30|>=8,<8.7.17|>=9,<9.3.2", + "typo3/cms-core": ">=8,<8.7.17|>=9,<9.3.2", "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", "willdurand/js-translation-bundle": "<2.1.1", "yiisoft/yii": ">=1.1.14,<1.1.15", - "yiisoft/yii2": "<2.0.14", + "yiisoft/yii2": "<2.0.15", "yiisoft/yii2-bootstrap": "<2.0.4", - "yiisoft/yii2-dev": "<2.0.14", + "yiisoft/yii2-dev": "<2.0.15", + "yiisoft/yii2-elasticsearch": "<2.0.5", "yiisoft/yii2-gii": "<2.0.4", "yiisoft/yii2-jui": "<2.0.4", + "yiisoft/yii2-redis": "<2.0.8", "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", - "zendframework/zend-diactoros": ">=1,<1.0.4", + "zendframework/zend-diactoros": ">=1,<1.8.4", + "zendframework/zend-feed": ">=1,<2.10.3", "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-http": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.3,<2.3.8|>=2.4,<2.4.1", + "zendframework/zend-http": ">=1,<2.8.1", "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", @@ -1498,7 +1472,7 @@ "zendframework/zend-validator": ">=2.3,<2.3.6", "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", - "zendframework/zendframework": ">=2,<2.4.11|>=2.5,<2.5.1", + "zendframework/zendframework": "<2.5.1", "zendframework/zendframework1": "<1.12.20", "zendframework/zendopenid": ">=2,<2.0.2", "zendframework/zendxml": ">=1,<1.0.1", @@ -1520,7 +1494,7 @@ } ], "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2018-03-07T15:45:44+00:00" + "time": "2018-09-17T20:20:31+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1569,30 +1543,30 @@ }, { "name": "sebastian/comparator", - "version": "2.1.3", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", + "php": "^7.1", + "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1629,20 +1603,20 @@ "compare", "equality" ], - "time": "2018-02-01T13:46:46+00:00" + "time": "2018-07-12T15:12:46+00:00" }, { "name": "sebastian/diff", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "e09160918c66281713f1c324c1f4c4c3037ba1e8" + "reference": "366541b989927187c4ca70490a35615d3fef2dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/e09160918c66281713f1c324c1f4c4c3037ba1e8", - "reference": "e09160918c66281713f1c324c1f4c4c3037ba1e8", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/366541b989927187c4ca70490a35615d3fef2dce", + "reference": "366541b989927187c4ca70490a35615d3fef2dce", "shasum": "" }, "require": { @@ -1685,7 +1659,7 @@ "unidiff", "unified diff" ], - "time": "2018-02-01T13:45:15+00:00" + "time": "2018-06-10T07:54:39+00:00" }, { "name": "sebastian/environment", @@ -2284,16 +2258,16 @@ }, { "name": "zendframework/zend-diactoros", - "version": "1.7.1", + "version": "1.8.6", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "bf26aff803a11c5cc8eb7c4878a702c403ec67f1" + "reference": "20da13beba0dde8fb648be3cc19765732790f46e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/bf26aff803a11c5cc8eb7c4878a702c403ec67f1", - "reference": "bf26aff803a11c5cc8eb7c4878a702c403ec67f1", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/20da13beba0dde8fb648be3cc19765732790f46e", + "reference": "20da13beba0dde8fb648be3cc19765732790f46e", "shasum": "" }, "require": { @@ -2306,17 +2280,29 @@ "require-dev": { "ext-dom": "*", "ext-libxml": "*", - "phpunit/phpunit": "^5.7.16 || ^6.0.8", + "php-http/psr7-integration-tests": "dev-master", + "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", "zendframework/zend-coding-standard": "~1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev", - "dev-develop": "1.8.x-dev" + "dev-master": "1.8.x-dev", + "dev-develop": "1.9.x-dev", + "dev-release-2.0": "2.0.x-dev" } }, "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/marshal_headers_from_sapi.php", + "src/functions/marshal_method_from_sapi.php", + "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/marshal_uri_from_sapi.php", + "src/functions/normalize_server.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/parse_cookie_header.php" + ], "psr-4": { "Zend\\Diactoros\\": "src/" } @@ -2332,7 +2318,7 @@ "psr", "psr-7" ], - "time": "2018-02-26T15:44:50+00:00" + "time": "2018-09-05T19:29:37+00:00" } ], "aliases": [], diff --git a/docs/book/v1/authenticated-user.md b/docs/book/v1/authenticated-user.md new file mode 100644 index 0000000..b5b0906 --- /dev/null +++ b/docs/book/v1/authenticated-user.md @@ -0,0 +1,56 @@ +# Authenticated user + +Once the user is authenticated, `zend-expressive-authentication-oauth2` stores +the user's credential in a PSR-7 attribute under the name `Zend\Expressive\Authentication\UserInterface`. + +This attribute contains an object that implements the [UserInterface](https://github.com/zendframework/zend-expressive-authentication/blob/master/src/UserInterface.php). + +This object contains all the information generated by [thephpleage/oauth2-server](https://github.com/thephpleague/oauth2-server), +including the following data: + +```php +[ + 'oauth_user_id' => /* user's identifier (string) */, + 'oauth_client_id' => /* the client id (string) */, + 'oauth_access_token_id' => /* the access token id (string) */, + 'oauth_scopes' => /* the scopes (mixed, usually an array) */ +] +``` + +You can retrieve all these values using `getDetails()` or `getDetail($name)` +functions of `UserInterface`. Here is reported an examples: + +```php +$user->getDetails(); // returns all the values, as array + +$userId = $user->getDetail('oauth_user_id', false); +if (false !== $userId) { + printf("The user ID is %s", $userId); +} +``` + +If you want to retrieve the identity of the user (or the client) you can also +use the `getIdentity()` function. This function returns the user's ID if it is +not null, otherwise it returns the client's ID. + +The `getRoles()` function of the `UserInterface` object returns always an empty +array. + +## Customize the user's object + +You can customize the user's object as you need but you must implement the +`UserInterface`. To customize the user object you need to change the service +with name `Zend\Expressive\Authentication\UserInterface` pointing to your +implementation. For instance, you can point to a `CustomUserFactory` class +that returns a `CustomUser` object (that implements `UserInterface`) as follows: + +```php +// config/autoload/custom-user.local.php +return [ + 'dependencies' => [ + 'factories' => [ + UserInterface::class => CustomUserFactory::class, + ] + ] +]; +``` diff --git a/docs/book/authorization-server.md b/docs/book/v1/authorization-server.md similarity index 84% rename from docs/book/authorization-server.md rename to docs/book/v1/authorization-server.md index 3eb298c..243be71 100644 --- a/docs/book/authorization-server.md +++ b/docs/book/v1/authorization-server.md @@ -1,6 +1,6 @@ # Implement an authorization server -This library provides the basics to implement an authorization server +This library provides the basics to implement an authorization server for your application. Since there are authorization flows that require user interaction, @@ -8,8 +8,8 @@ your application is expected to provide the middleware to handle this. ## Add the token endpoint -This is the most simple part, since this library provides -`Zend\Expressive\Authentication\OAuth2\TokenHandler` to deal with it. +This is the most simple part, since this library provides +`Zend\Expressive\Authentication\OAuth2\TokenEndpointHandler` to deal with it. This endpoint must accept POST requests. @@ -18,7 +18,7 @@ For example: ```php use Zend\Expressive\Authentication\OAuth2; -$app->route('/oauth2/token', OAuth2\TokenHandler::class, ['POST']); +$app->post('/oauth2/token', OAuth2\TokenEndpointHandler::class); ``` ## Add the authorization endpoint @@ -29,11 +29,11 @@ to obtain an access token or authorization code. This endpoint must accept GET requests and should: - Validate the request (especially for a valid client id and redirect url) - - Make sure the User is authenticated (for example by showing a login + - Make sure the User is authenticated (for example by showing a login prompt if needed) - Optionally request the users consent to grant access to the client - Redirect to a specified url of the client with success or error information - + The first and the last part is provided by this library. For example, to add the authorization endpoint you can declare a middleware pipe @@ -43,18 +43,18 @@ to compose these parts: use Zend\Expressive\Authentication\OAuth2; $app->route('/oauth2/authorize', [ - OAuth2\AuthorizatonMiddleware, - + OAuth2\AuthorizationMiddleware::class, + // The followig middleware is provided by your application (see below) - Application\OAuthAuthorizationMiddleware::class, - - OAuth2\AuthorizationHandler + Application\OAuthAuthorizationMiddleware::class, + + OAuth2\AuthorizationHandler::class ], ['GET', 'POST']); ``` In your `Application\OAuthAuthorizationMiddleware`, you'll have access to the `League\OAuth2\Server\RequestTypes\AuthorizationRequest` via the -psr-7 request. Your middleware should populate the user entity with `setUser()` and the +PSR-7 request. Your middleware should populate the user entity with `setUser()` and the user's consent decision with `setAuthorizationApproved()` to this authorization request instance. @@ -68,46 +68,46 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use League\OAuth2\Server\RequestTypes\AuthorizationRequest; +use Zend\Expressive\Authentication\UserInterface; class OAuthAuthorizationMiddleware implements MiddlewareInterface { - public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { // Assume a middleware handled the authentication check and // populates the user object which also implements the // OAuth2 UserEntityInterface - $user = $request->getAttribute('authenticated_user'); - + $user = $request->getAttribute(UserInterface::class); + // Assume some middleware handles and populates a session // container $session = $request->getAttribute('session'); - + // This is populated by the previous middleware /** @var AuthorizationRequest $authRequest */ $authRequest = $request->getAttribute(AuthorizationRequest::class); - + // the user is authenticated if ($user) { $authRequest->setUser($user); - + // Assume all clients are trusted, but you could // handle consent here or within the next middleware // as needed $authRequest->setAuthorizationApproved(true); - + return $handler->handle($request); } - + // The user is not authenticated, show login form ... - + // Store the auth request state // NOTE: Do not attempt to serialize or store the authorization // request object. Store the query parameters instead and redirect // with these to this endpoint again to replay the request. $session['oauth2_request_params'] = $request->getQueryParams(); - + return new RedirectResponse('/oauth2/login'); } } ``` - diff --git a/docs/book/grant/auth_code.md b/docs/book/v1/grant/auth_code.md similarity index 100% rename from docs/book/grant/auth_code.md rename to docs/book/v1/grant/auth_code.md diff --git a/docs/book/grant/auth_code.png b/docs/book/v1/grant/auth_code.png similarity index 100% rename from docs/book/grant/auth_code.png rename to docs/book/v1/grant/auth_code.png diff --git a/docs/book/grant/client_credentials.md b/docs/book/v1/grant/client_credentials.md similarity index 100% rename from docs/book/grant/client_credentials.md rename to docs/book/v1/grant/client_credentials.md diff --git a/docs/book/grant/implicit.md b/docs/book/v1/grant/implicit.md similarity index 100% rename from docs/book/grant/implicit.md rename to docs/book/v1/grant/implicit.md diff --git a/docs/book/grant/password.md b/docs/book/v1/grant/password.md similarity index 100% rename from docs/book/grant/password.md rename to docs/book/v1/grant/password.md diff --git a/docs/book/grant/refresh_token.md b/docs/book/v1/grant/refresh_token.md similarity index 100% rename from docs/book/grant/refresh_token.md rename to docs/book/v1/grant/refresh_token.md diff --git a/docs/book/intro.md b/docs/book/v1/intro.md similarity index 91% rename from docs/book/intro.md rename to docs/book/v1/intro.md index 2bc387b..c7aaa34 100644 --- a/docs/book/intro.md +++ b/docs/book/v1/intro.md @@ -63,7 +63,7 @@ return [ 'username' => '', 'password' => '' ], - + // Set value to null to disable a grant 'grants' => [ \League\OAuth2\Server\Grant\ClientCredentialsGrant::class @@ -111,9 +111,9 @@ return [ ``` The `grants` array is for enabling/disabling grants. By default all the supported -grants are configured to be available. If you would like to disable any of the +grants are configured to be available. If you would like to disable any of the supplied grants, simply change the value for the grant to NULL. Additionally, -you can extend this array to add your own custom grants. +you can extend this array to add your own custom grants. You need to provide an OAuth2 database yourself, or generate a [SQLite](https://www.sqlite.org) @@ -142,22 +142,10 @@ For security reason, the client `secret` and the user `password` are stored using the `bcrypt` algorithm provided by [password_hash](http://php.net/manual/en/function.password-hash.php) function of PHP. -## Configure the OAuth2 route - -As last step, in order to use the OAuth2 server you need to configure a route -to point to `Zend\Expressive\Authentication\OAuth2\OAuth2Middleware` with -GET and POST HTTP verbs. - -For instance, you can add the following route to your Expressive application: +## Configure OAuth2 routes -```php -$app->route( - '/oauth', - Zend\Expressive\Authentication\OAuth2\OAuth2Middleware::class, - ['GET', 'POST'], - 'oauth' -); -``` +As last step, in order to use the OAuth2 server you need to configure the routes +for the **token endpoint** and **authorization**. -With this configuration, you can interact with the OAuth2 server using `/oauth` -URL. +You can read how add the **token endpoint** and the **authorization** routes in +the [Implement an authorization server](authorization-server) section. diff --git a/docs/book/usage.md b/docs/book/v1/usage.md similarity index 100% rename from docs/book/usage.md rename to docs/book/v1/usage.md diff --git a/mkdocs.yml b/mkdocs.yml index bb56eee..1989b01 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,15 +2,16 @@ docs_dir: docs/book site_dir: docs/html pages: - index.md - - Introduction: intro.md - - Usage: usage.md - - "Authorization server": authorization-server.md + - Introduction: v1/intro.md + - Usage: v1/usage.md + - "Authorization server": v1/authorization-server.md + - "Authenticated user": v1/authenticated-user.md - Grant: - - "Client credentials": grant/client_credentials.md - - "Password": grant/password.md - - "Authorization code": grant/auth_code.md - - "Implicit": grant/implicit.md - - "Refresh token": grant/refresh_token.md + - "Client credentials": v1/grant/client_credentials.md + - "Password": v1/grant/password.md + - "Authorization code": v1/grant/auth_code.md + - "Implicit": v1/grant/implicit.md + - "Refresh token": v1/grant/refresh_token.md site_name: OAuth2 middleware site_description: 'OAuth2 (server) for Expressive and PSR-7 applications.' repo_url: 'https://github.com/zendframework/zend-expressive-authentication-oauth2' diff --git a/src/OAuth2Adapter.php b/src/OAuth2Adapter.php index f1278f1..379025b 100644 --- a/src/OAuth2Adapter.php +++ b/src/OAuth2Adapter.php @@ -16,12 +16,9 @@ use Psr\Http\Message\ServerRequestInterface; use Zend\Expressive\Authentication\AuthenticationInterface; use Zend\Expressive\Authentication\UserInterface; -use Zend\Expressive\Authentication\UserRepository\UserTrait; class OAuth2Adapter implements AuthenticationInterface { - use UserTrait; - /** * @var ResourceServer */ @@ -32,12 +29,22 @@ class OAuth2Adapter implements AuthenticationInterface */ protected $responseFactory; - public function __construct(ResourceServer $resourceServer, callable $responseFactory) - { + public function __construct( + ResourceServer $resourceServer, + callable $responseFactory, + callable $userFactory + ) { $this->resourceServer = $resourceServer; $this->responseFactory = function () use ($responseFactory) : ResponseInterface { return $responseFactory(); }; + $this->userFactory = function ( + string $identity, + array $roles = [], + array $details = [] + ) use ($userFactory) : UserInterface { + return $userFactory($identity, $roles, $details); + }; } /** @@ -47,9 +54,19 @@ public function authenticate(ServerRequestInterface $request) : ?UserInterface { try { $result = $this->resourceServer->validateAuthenticatedRequest($request); - $userId = $result->getAttribute('oauth_user_id', false); - if (false !== $userId) { - return $this->generateUser($userId, []); + $userId = $result->getAttribute('oauth_user_id', null); + $clientId = $result->getAttribute('oauth_client_id', null); + if (isset($userId) || isset($clientId)) { + return ($this->userFactory)( + $userId ?? $clientId ?? '', + [], + [ + 'oauth_user_id' => $userId, + 'oauth_client_id' => $clientId, + 'oauth_access_token_id' => $result->getAttribute('oauth_access_token_id', null), + 'oauth_scopes' => $result->getAttribute('oauth_scopes', null) + ] + ); } } catch (OAuthServerException $exception) { return null; diff --git a/src/OAuth2AdapterFactory.php b/src/OAuth2AdapterFactory.php index d35e52e..f8b6f91 100644 --- a/src/OAuth2AdapterFactory.php +++ b/src/OAuth2AdapterFactory.php @@ -13,6 +13,7 @@ use League\OAuth2\Server\ResourceServer; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; +use Zend\Expressive\Authentication\UserInterface; class OAuth2AdapterFactory { @@ -30,7 +31,8 @@ public function __invoke(ContainerInterface $container) : OAuth2Adapter return new OAuth2Adapter( $resourceServer, - $container->get(ResponseInterface::class) + $container->get(ResponseInterface::class), + $container->get(UserInterface::class) ); } } diff --git a/test/OAuth2AdapterFactoryTest.php b/test/OAuth2AdapterFactoryTest.php index aa08059..c9c6a7e 100644 --- a/test/OAuth2AdapterFactoryTest.php +++ b/test/OAuth2AdapterFactoryTest.php @@ -20,6 +20,7 @@ use Zend\Expressive\Authentication\AuthenticationInterface; use Zend\Expressive\Authentication\OAuth2\OAuth2Adapter; use Zend\Expressive\Authentication\OAuth2\OAuth2AdapterFactory; +use Zend\Expressive\Authentication\UserInterface; class OAuth2AdapterFactoryTest extends TestCase { @@ -43,6 +44,14 @@ public function setUp() $this->responseFactory = function () { return $this->response->reveal(); }; + $this->user = $this->prophesize(UserInterface::class); + $this->userFactory = function ( + string $identity, + array $roles = [], + array $details = [] + ) { + return $this->user->reveal($identity, $roles, $details); + }; } public function testConstructor() @@ -73,6 +82,10 @@ public function testFactoryRaisesTypeErrorForNonCallableResponseFactory() ->get(ResponseInterface::class) ->willReturn(new stdClass()); + $this->container + ->get(UserInterface::class) + ->willReturn($this->userFactory); + $factory = new OAuth2AdapterFactory(); $this->expectException(TypeError::class); @@ -92,6 +105,10 @@ public function testFactoryRaisesTypeErrorWhenResponseServiceProvidesResponseIns ->get(ResponseInterface::class) ->will([$this->response, 'reveal']); + $this->container + ->get(UserInterface::class) + ->willReturn($this->userFactory); + $factory = new OAuth2AdapterFactory(); $this->expectException(TypeError::class); @@ -114,6 +131,10 @@ public function testFactoryReturnsInstanceWhenAppropriateDependenciesArePresentI ->get(ResponseInterface::class) ->willReturn($this->responseFactory); + $this->container + ->get(UserInterface::class) + ->willReturn($this->userFactory); + $factory = new OAuth2AdapterFactory(); $adapter = $factory($this->container->reveal()); diff --git a/test/OAuth2AdapterTest.php b/test/OAuth2AdapterTest.php index bed3ce8..51dd4a3 100644 --- a/test/OAuth2AdapterTest.php +++ b/test/OAuth2AdapterTest.php @@ -17,6 +17,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Zend\Expressive\Authentication\AuthenticationInterface; +use Zend\Expressive\Authentication\DefaultUser; use Zend\Expressive\Authentication\OAuth2\OAuth2Adapter; use Zend\Expressive\Authentication\UserInterface; @@ -38,13 +39,21 @@ public function setUp() $this->responseFactory = function () { return $this->response->reveal(); }; + $this->userFactory = function ( + string $identity, + array $roles = [], + array $details = [] + ) { + return new DefaultUser($identity, $roles, $details); + }; } public function testConstructor() { $adapter = new OAuth2Adapter( $this->resourceServer->reveal(), - $this->responseFactory + $this->responseFactory, + $this->userFactory ); $this->assertInstanceOf(OAuth2Adapter::class, $adapter); $this->assertInstanceOf(AuthenticationInterface::class, $adapter); @@ -62,16 +71,18 @@ public function testOAuthServerExceptionRaisedDuringAuthenticateResultsInInvalid $adapter = new OAuth2Adapter( $this->resourceServer->reveal(), - $this->responseFactory + $this->responseFactory, + $this->userFactory ); $this->assertNull($adapter->authenticate($request->reveal())); } - public function testAuthenticateReturnsNullIfResourceServerDoesNotProduceAUserId() + public function testAuthenticateReturnsNullIfResourceServerDoesNotProduceAUserIdOrClientId() { $request = $this->prophesize(ServerRequestInterface::class); - $request->getAttribute('oauth_user_id', false)->willReturn(false); + $request->getAttribute('oauth_user_id', null)->willReturn(null); + $request->getAttribute('oauth_client_id', null)->willReturn(null); $this->resourceServer ->validateAuthenticatedRequest(Argument::that([$request, 'reveal'])) @@ -79,7 +90,8 @@ public function testAuthenticateReturnsNullIfResourceServerDoesNotProduceAUserId $adapter = new OAuth2Adapter( $this->resourceServer->reveal(), - $this->responseFactory + $this->responseFactory, + $this->userFactory ); $this->assertNull($adapter->authenticate($request->reveal())); @@ -88,7 +100,35 @@ public function testAuthenticateReturnsNullIfResourceServerDoesNotProduceAUserId public function testAuthenticateReturnsAUserIfTheResourceServerProducesAUserId() { $request = $this->prophesize(ServerRequestInterface::class); - $request->getAttribute('oauth_user_id', false)->willReturn('some-identifier'); + $request->getAttribute('oauth_user_id', null)->willReturn('some-identifier'); + $request->getAttribute('oauth_client_id', null)->willReturn(null); + $request->getAttribute('oauth_access_token_id', null)->willReturn(null); + $request->getAttribute('oauth_scopes', null)->willReturn(null); + + $this->resourceServer + ->validateAuthenticatedRequest(Argument::that([$request, 'reveal'])) + ->will([$request, 'reveal']); + + $adapter = new OAuth2Adapter( + $this->resourceServer->reveal(), + $this->responseFactory, + $this->userFactory + ); + + $user = $adapter->authenticate($request->reveal()); + + $this->assertInstanceOf(UserInterface::class, $user); + $this->assertSame('some-identifier', $user->getIdentity()); + $this->assertSame([], $user->getRoles()); + } + + public function testAuthenticateReturnsAClientIfTheResourceServerProducesAClientId() + { + $request = $this->prophesize(ServerRequestInterface::class); + $request->getAttribute('oauth_user_id', null)->willReturn(null); + $request->getAttribute('oauth_client_id', null)->willReturn('some-identifier'); + $request->getAttribute('oauth_access_token_id', null)->willReturn(null); + $request->getAttribute('oauth_scopes', null)->willReturn(null); $this->resourceServer ->validateAuthenticatedRequest(Argument::that([$request, 'reveal'])) @@ -96,14 +136,15 @@ public function testAuthenticateReturnsAUserIfTheResourceServerProducesAUserId() $adapter = new OAuth2Adapter( $this->resourceServer->reveal(), - $this->responseFactory + $this->responseFactory, + $this->userFactory ); $user = $adapter->authenticate($request->reveal()); $this->assertInstanceOf(UserInterface::class, $user); $this->assertSame('some-identifier', $user->getIdentity()); - $this->assertSame([], $user->getUserRoles()); + $this->assertSame([], $user->getRoles()); } public function testUnauthorizedResponseProducesAResponseWithAWwwAuthenticateHeader() @@ -119,7 +160,8 @@ public function testUnauthorizedResponseProducesAResponseWithAWwwAuthenticateHea $adapter = new OAuth2Adapter( $this->resourceServer->reveal(), - $this->responseFactory + $this->responseFactory, + $this->userFactory ); $this->assertSame( From 9728d43984de103d73783f0cdb933e8409bada50 Mon Sep 17 00:00:00 2001 From: Enrico Zimuel Date: Tue, 2 Oct 2018 18:12:40 +0200 Subject: [PATCH 2/3] Updated with feedback from #50 --- CHANGELOG.md | 2 +- docs/book/authorization-server.md | 6 ++++ docs/book/grant/auth_code.md | 6 ++++ docs/book/grant/client_credentials.md | 6 ++++ docs/book/grant/implicit.md | 6 ++++ docs/book/grant/password.md | 6 ++++ docs/book/grant/refresh_token.md | 6 ++++ docs/book/intro.md | 6 ++++ docs/book/usage.md | 6 ++++ docs/book/v1/authenticated-user.md | 51 ++++++++++++++++++++++----- docs/book/v1/authorization-server.md | 4 +-- mkdocs.yml | 9 +++++ src/OAuth2Adapter.php | 2 +- 13 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 docs/book/authorization-server.md create mode 100644 docs/book/grant/auth_code.md create mode 100644 docs/book/grant/client_credentials.md create mode 100644 docs/book/grant/implicit.md create mode 100644 docs/book/grant/password.md create mode 100644 docs/book/grant/refresh_token.md create mode 100644 docs/book/intro.md create mode 100644 docs/book/usage.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 31cbf53..bf3268c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ All notable changes to this project will be documented in this file, in reverse services instead of opening up a second connection for oauth. - [#42](https://github.com/zendframework/zend-expressive-authentication-oauth2/pull/42) Adds `TokenEndpointHandler`, `AuthorizationMiddleware` and `AuthorizationHandler` in the `Zend\Expressive\Authentication\OAuth2` namespace - to [implement an authorization server](docs/book/authorization-server.md). + to [implement an authorization server](https://docs.zendframework.com/zend-expressive-authentication-oauth2/v1/authorization-server/). ### Changed diff --git a/docs/book/authorization-server.md b/docs/book/authorization-server.md new file mode 100644 index 0000000..5203650 --- /dev/null +++ b/docs/book/authorization-server.md @@ -0,0 +1,6 @@ + + diff --git a/docs/book/grant/auth_code.md b/docs/book/grant/auth_code.md new file mode 100644 index 0000000..fe37c6b --- /dev/null +++ b/docs/book/grant/auth_code.md @@ -0,0 +1,6 @@ + + diff --git a/docs/book/grant/client_credentials.md b/docs/book/grant/client_credentials.md new file mode 100644 index 0000000..f63f96a --- /dev/null +++ b/docs/book/grant/client_credentials.md @@ -0,0 +1,6 @@ + + diff --git a/docs/book/grant/implicit.md b/docs/book/grant/implicit.md new file mode 100644 index 0000000..06419ca --- /dev/null +++ b/docs/book/grant/implicit.md @@ -0,0 +1,6 @@ + + diff --git a/docs/book/grant/password.md b/docs/book/grant/password.md new file mode 100644 index 0000000..938dc84 --- /dev/null +++ b/docs/book/grant/password.md @@ -0,0 +1,6 @@ + + diff --git a/docs/book/grant/refresh_token.md b/docs/book/grant/refresh_token.md new file mode 100644 index 0000000..24a8ddc --- /dev/null +++ b/docs/book/grant/refresh_token.md @@ -0,0 +1,6 @@ + + diff --git a/docs/book/intro.md b/docs/book/intro.md new file mode 100644 index 0000000..8edcf57 --- /dev/null +++ b/docs/book/intro.md @@ -0,0 +1,6 @@ + + diff --git a/docs/book/usage.md b/docs/book/usage.md new file mode 100644 index 0000000..fe265d8 --- /dev/null +++ b/docs/book/usage.md @@ -0,0 +1,6 @@ + + diff --git a/docs/book/v1/authenticated-user.md b/docs/book/v1/authenticated-user.md index b5b0906..166027b 100644 --- a/docs/book/v1/authenticated-user.md +++ b/docs/book/v1/authenticated-user.md @@ -1,7 +1,7 @@ # Authenticated user Once the user is authenticated, `zend-expressive-authentication-oauth2` stores -the user's credential in a PSR-7 attribute under the name `Zend\Expressive\Authentication\UserInterface`. +the user's authorization details in a PSR-7 attribute under the name `Zend\Expressive\Authentication\UserInterface`. This attribute contains an object that implements the [UserInterface](https://github.com/zendframework/zend-expressive-authentication/blob/master/src/UserInterface.php). @@ -17,8 +17,8 @@ including the following data: ] ``` -You can retrieve all these values using `getDetails()` or `getDetail($name)` -functions of `UserInterface`. Here is reported an examples: +You may retrieve these values using the `getDetails()` method or individually +using the `getDetail($name)` method of the user instance. As an example: ```php $user->getDetails(); // returns all the values, as array @@ -38,11 +38,46 @@ array. ## Customize the user's object -You can customize the user's object as you need but you must implement the -`UserInterface`. To customize the user object you need to change the service -with name `Zend\Expressive\Authentication\UserInterface` pointing to your -implementation. For instance, you can point to a `CustomUserFactory` class -that returns a `CustomUser` object (that implements `UserInterface`) as follows: +If you wish to provide a custom `Zend\Expressive\Authentication\UserInterface` +implementation, you will need to provide: + +- a custom implementation of the the interface +- a factory capable of generating instances of that interface +- a DI factory for generating the previous factory +- configuration wiring the `UserInterface` service to your factory. + +The factory noted in the second step should be a callable with the following +signature: + +```php +function ( + string $identity, + array $roles = [], + array $details = [] +) : Zend\Expressive\Authentication\UserInterface +``` + +As an example of the factory in the third point, you will create a standard DI +factory to return it. It could, for instance, compose a database adapter o pull +information and create your custom user implementation: + +```php +class CustomUserFactory +{ + public function __invoke(Psr\Container\ContainerInterface $container) : callable + { + $db = $container->get(Zend\Db\Adapter\AdapterInterface::class); + return function (string $identity, array $roles = [], array $details = []) use ($db) : Zend\Expressive\Authentication\UserInterface { + // get some data from $db + // return a new instance + return new MyCustomUserType(/* ... */); + }); + } +} +``` + +You will then need to wire this factory to the `UserInterface` service, +per the following example: ```php // config/autoload/custom-user.local.php diff --git a/docs/book/v1/authorization-server.md b/docs/book/v1/authorization-server.md index 243be71..41f7c61 100644 --- a/docs/book/v1/authorization-server.md +++ b/docs/book/v1/authorization-server.md @@ -8,8 +8,8 @@ your application is expected to provide the middleware to handle this. ## Add the token endpoint -This is the most simple part, since this library provides -`Zend\Expressive\Authentication\OAuth2\TokenEndpointHandler` to deal with it. +Adding the token endpoint involves routing to the provided +`Zend\Expressive\Authentication\OAuth2\TokenEndpointHandler`. This endpoint must accept POST requests. diff --git a/mkdocs.yml b/mkdocs.yml index 1989b01..bc2d4e0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -12,6 +12,15 @@ pages: - "Authorization code": v1/grant/auth_code.md - "Implicit": v1/grant/implicit.md - "Refresh token": v1/grant/refresh_token.md + - "_hidden-legacy-page-links": + - "_introduction": intro.md + - "_usage": usage.md + - "_authorizationserver": authorization-server.md + - "_grant_clientcredentials": grant/client_credentials.md + - "_grant_password": grant/password.md + - "_grant_authcode": grant/auth_code.md + - "_grant_implicit": grant/implicit.md + - "_grant_refreshtoken": grant/refresh_token.md site_name: OAuth2 middleware site_description: 'OAuth2 (server) for Expressive and PSR-7 applications.' repo_url: 'https://github.com/zendframework/zend-expressive-authentication-oauth2' diff --git a/src/OAuth2Adapter.php b/src/OAuth2Adapter.php index 379025b..455fcc2 100644 --- a/src/OAuth2Adapter.php +++ b/src/OAuth2Adapter.php @@ -58,7 +58,7 @@ public function authenticate(ServerRequestInterface $request) : ?UserInterface $clientId = $result->getAttribute('oauth_client_id', null); if (isset($userId) || isset($clientId)) { return ($this->userFactory)( - $userId ?? $clientId ?? '', + $userId ?? $clientId, [], [ 'oauth_user_id' => $userId, From a37aa8f5a7da8386d739c0e128376f0a68d51152 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 2 Oct 2018 17:03:33 -0500 Subject: [PATCH 3/3] Provides final edits for documentation --- docs/book/v1/authenticated-user.md | 58 +++++++++++--------- docs/book/v1/authorization-server.md | 68 +++++++++++++----------- docs/book/v1/grant/auth_code.md | 8 +-- docs/book/v1/grant/client_credentials.md | 7 +-- docs/book/v1/grant/implicit.md | 2 +- docs/book/v1/grant/password.md | 33 ++++++------ docs/book/v1/intro.md | 52 +++++++++--------- docs/book/v1/usage.md | 20 +++---- 8 files changed, 129 insertions(+), 119 deletions(-) diff --git a/docs/book/v1/authenticated-user.md b/docs/book/v1/authenticated-user.md index 166027b..c717749 100644 --- a/docs/book/v1/authenticated-user.md +++ b/docs/book/v1/authenticated-user.md @@ -1,49 +1,51 @@ # Authenticated user -Once the user is authenticated, `zend-expressive-authentication-oauth2` stores -the user's authorization details in a PSR-7 attribute under the name `Zend\Expressive\Authentication\UserInterface`. +Once the user is authenticated, zend-expressive-authentication-oauth2 stores the +user's authorization details in a PSR-7 attribute under the name +`Zend\Expressive\Authentication\UserInterface`, using an object implementing the +[interface of the same name](https://github.com/zendframework/zend-expressive-authentication/blob/master/src/UserInterface.php). -This attribute contains an object that implements the [UserInterface](https://github.com/zendframework/zend-expressive-authentication/blob/master/src/UserInterface.php). - -This object contains all the information generated by [thephpleage/oauth2-server](https://github.com/thephpleague/oauth2-server), -including the following data: +This object contains all information discovered and/or generated by +[thephpleage/oauth2-server](https://oauth2.thephpleague.com), including the +following data: ```php [ - 'oauth_user_id' => /* user's identifier (string) */, - 'oauth_client_id' => /* the client id (string) */, + 'oauth_user_id' => /* user's identifier (string) */, + 'oauth_client_id' => /* the client id (string) */, 'oauth_access_token_id' => /* the access token id (string) */, - 'oauth_scopes' => /* the scopes (mixed, usually an array) */ + 'oauth_scopes' => /* the scopes (mixed, usually an array) */ ] ``` -You may retrieve these values using the `getDetails()` method or individually -using the `getDetail($name)` method of the user instance. As an example: +You may retrieve all of these values using the `getDetails()` method, or +individually using the `getDetail($name)` method, of the user instance. As an +example: ```php $user->getDetails(); // returns all the values, as array +// Retrieves only the oauth_user_id value, using a default of boolean false: $userId = $user->getDetail('oauth_user_id', false); if (false !== $userId) { printf("The user ID is %s", $userId); } ``` -If you want to retrieve the identity of the user (or the client) you can also -use the `getIdentity()` function. This function returns the user's ID if it is -not null, otherwise it returns the client's ID. +If you want to retrieve the identity of the user (or the client), you can also +use the `getIdentity()` method. This method returns the user's ID if it is +not null; otherwise it returns the client's ID. -The `getRoles()` function of the `UserInterface` object returns always an empty -array. +**The `getRoles()` method of the user instance always returns an empty array.** -## Customize the user's object +## Customize the user instance If you wish to provide a custom `Zend\Expressive\Authentication\UserInterface` implementation, you will need to provide: -- a custom implementation of the the interface -- a factory capable of generating instances of that interface -- a DI factory for generating the previous factory +- a custom implementation of the the interface. +- a factory capable of generating instances of that implementation. +- a DI factory for generating the previous factory. - configuration wiring the `UserInterface` service to your factory. The factory noted in the second step should be a callable with the following @@ -58,16 +60,20 @@ function ( ``` As an example of the factory in the third point, you will create a standard DI -factory to return it. It could, for instance, compose a database adapter o pull +factory to return it. It could, for instance, compose a database adapter to pull information and create your custom user implementation: ```php +use Psr\Container\ContainerInterface; +use Zend\Db\Adapter\AdapterInterface as DbAdapter; +use Zend\Expressive\Authentication\UserInterface; + class CustomUserFactory { - public function __invoke(Psr\Container\ContainerInterface $container) : callable + public function __invoke(ContainerInterface $container) : callable { - $db = $container->get(Zend\Db\Adapter\AdapterInterface::class); - return function (string $identity, array $roles = [], array $details = []) use ($db) : Zend\Expressive\Authentication\UserInterface { + $db = $container->get(DbAdapter::class); + return function (string $identity, array $roles = [], array $details = []) use ($db) : UserInterface { // get some data from $db // return a new instance return new MyCustomUserType(/* ... */); @@ -85,7 +91,7 @@ return [ 'dependencies' => [ 'factories' => [ UserInterface::class => CustomUserFactory::class, - ] - ] + ], + ], ]; ``` diff --git a/docs/book/v1/authorization-server.md b/docs/book/v1/authorization-server.md index 41f7c61..ab998fc 100644 --- a/docs/book/v1/authorization-server.md +++ b/docs/book/v1/authorization-server.md @@ -1,17 +1,17 @@ # Implement an authorization server -This library provides the basics to implement an authorization server +This library provides the basics for implementing an authorization server for your application. Since there are authorization flows that require user interaction, -your application is expected to provide the middleware to handle this. +**your application is expected to provide the middleware to handle this**. ## Add the token endpoint Adding the token endpoint involves routing to the provided `Zend\Expressive\Authentication\OAuth2\TokenEndpointHandler`. -This endpoint must accept POST requests. +This endpoint **MUST** accept `POST` requests. For example: @@ -23,45 +23,51 @@ $app->post('/oauth2/token', OAuth2\TokenEndpointHandler::class); ## Add the authorization endpoint -The authorization endpoint is an url of to which the client redirects +The authorization endpoint is the URL to which the client redirects to obtain an access token or authorization code. -This endpoint must accept GET requests and should: +This endpoint **MUST** accept `GET` requests and should: - - Validate the request (especially for a valid client id and redirect url) - - Make sure the User is authenticated (for example by showing a login - prompt if needed) - - Optionally request the users consent to grant access to the client - - Redirect to a specified url of the client with success or error information +- Validate the request (especially for a valid client id and redirect url). + +- Make sure the user is authenticated (for example, by showing a login + prompt if needed). -The first and the last part is provided by this library. +- Optionally, request the user's consent to grant access to the client. -For example, to add the authorization endpoint you can declare a middleware pipe -to compose these parts: +- Redirect to a specified url of the client with success or error information. + +The first and the last items are provided by this library. + +For example, to add the authorization endpoint, you can declare a middleware +pipeline for the route as follows: ```php use Zend\Expressive\Authentication\OAuth2; +use Zend\Expressive\Session\SessionMiddleware; $app->route('/oauth2/authorize', [ + SessionMiddleware::class, + OAuth2\AuthorizationMiddleware::class, - // The followig middleware is provided by your application (see below) - Application\OAuthAuthorizationMiddleware::class, + // The following middleware is provided by your application (see below): + App\OAuthAuthorizationMiddleware::class, OAuth2\AuthorizationHandler::class ], ['GET', 'POST']); ``` -In your `Application\OAuthAuthorizationMiddleware`, you'll have access -to the `League\OAuth2\Server\RequestTypes\AuthorizationRequest` via the -PSR-7 request. Your middleware should populate the user entity with `setUser()` and the -user's consent decision with `setAuthorizationApproved()` to this authorization -request instance. +In your `App\OAuthAuthorizationMiddleware`, you'll have access to the +`League\OAuth2\Server\RequestTypes\AuthorizationRequest` via the PSR-7 request. +Your middleware should populate the `AuthorizationRequest`'s user entity via its +`setUser()` method, and the user's consent decision via the +`setAuthorizationApproved()`method. -```php -getAttribute(UserInterface::class); - // Assume some middleware handles and populates a session + // Assume the SessionMiddleware handles and populates a session // container $session = $request->getAttribute('session'); - // This is populated by the previous middleware + // This is populated by the previous middleware: /** @var AuthorizationRequest $authRequest */ $authRequest = $request->getAttribute(AuthorizationRequest::class); - // the user is authenticated + // The user is authenticated: if ($user) { $authRequest->setUser($user); - // Assume all clients are trusted, but you could - // handle consent here or within the next middleware - // as needed + // This assumes all clients are trusted, but you could + // handle consent here, or within the next middleware + // as needed. $authRequest->setAuthorizationApproved(true); return $handler->handle($request); diff --git a/docs/book/v1/grant/auth_code.md b/docs/book/v1/grant/auth_code.md index d716567..33733bc 100644 --- a/docs/book/v1/grant/auth_code.md +++ b/docs/book/v1/grant/auth_code.md @@ -8,14 +8,14 @@ third-party server with a 4-step flow as illustrated in this diagram: ![Authorization code diagram](auth_code.png) The web application sends a request (including the `client_id` and the -`redirect_uri`) to the authorization server asking for an Authorization code (1). -The authorization server shows an Allow/Deny page to the end-user requesting +`redirect_uri`) to the authorization server asking for an authorization code (1). +The authorization server shows an allow/deny page to the end-user requesting authorization for access. If the user clicks on "Allow", the server sends the authorization code to the web application using the `redirect_uri` (2). The web application can now perform a token request, passing the `client_id`, the `redirect_uri`, the `client_secret`, and the authentication code to prove that it is authorized to perform the request (3). The authorization server sends -the access token in response if the request is valid (4). +the access token in the response if the request is valid (4). ## Request the authorization code @@ -29,7 +29,7 @@ authorization server: the user will be redirected to a default location on completion. - `scope` with a space-delimited list of requested scope permissions. - `state` with a Cross-Site Request Forgery (CSRF) token. This parameter is - optional, but highly recommended. You can store the value of the CSRF token in + optional, but highly recommended. You can store the value of the CSRF token in the user’s session to be validated in the next step. The user will then be asked to login to the authorization server and approve the diff --git a/docs/book/v1/grant/client_credentials.md b/docs/book/v1/grant/client_credentials.md index d7e9969..20e1ac5 100644 --- a/docs/book/v1/grant/client_credentials.md +++ b/docs/book/v1/grant/client_credentials.md @@ -1,9 +1,10 @@ # Client credentials -The client credentials grant is used in machine-to-machine scenarios: for -example, a client making API requests that do not require a user's permission. +The client credentials grant is used in machine-to-machine scenarios. For +example, you would use it with a client making API requests that do not require +a user's permission. -The client sends a `POST` request with following body parameters to the +The client sends a `POST` request with the following body parameters to the authorization server: - `grant_type` = client_credentials. diff --git a/docs/book/v1/grant/implicit.md b/docs/book/v1/grant/implicit.md index 5ca2d4c..b8c162c 100644 --- a/docs/book/v1/grant/implicit.md +++ b/docs/book/v1/grant/implicit.md @@ -20,7 +20,7 @@ authorization server: user’s session to be validated in the next step. The user will then be asked to login to the authorization server and approve the -client request. If the user approves the request they will be redirected to the +client request. If the user approves the request, they will be redirected to the redirect URI with the following parameters in the query string arguments: - `token_type` = Bearer. diff --git a/docs/book/v1/grant/password.md b/docs/book/v1/grant/password.md index 30f7f69..a2101f5 100644 --- a/docs/book/v1/grant/password.md +++ b/docs/book/v1/grant/password.md @@ -1,18 +1,19 @@ # Password -This use case can be used to authenticate an API with user's password grant. -The typical scenario includes a Login web page with username and password that -is used to authenticate against a first-party API. Password grant is only -appropriate for **trusted clients**. If you build your own website as a client -of your API, then this is a great way to handle logging in. +This use case allows authentication to an API using the user's credentials +(generally a username and password). The typical scenario includes a "Login" +web page that captures a username and password combination that is used to +authenticate against a first-party API. Password grant is only appropriate for +**trusted clients**. If you build your own website as a client of your API, then +this is a great way to handle logging in. The client sends a POST request with following parameters: -- `grant_type` = password; -- `client_id` with the client’s ID; -- `client_secret` with the client’s secret; -- `scope` with a space-delimited list of requested scope permissions; -- `username` with the user’s username; +- `grant_type` = password. +- `client_id` with the client’s ID. +- `client_secret` with the client’s secret. +- `scope` with a space-delimited list of requested scope permissions. +- `username` with the user’s username. - `password` with the user’s password. The authorization server responds with a JSON as follows: @@ -26,9 +27,9 @@ The authorization server responds with a JSON as follows: } ``` -The `token_type` is the type of generated token (Bearer). The `expires_in` is -an integer representing the TTL (in seconds) of the access token. -The `refresh_token` a token that can be used to refresh the `access_token` when -expired. -The `access_token` contains a `JWT` signed with the authorization server’s -private key. This token must be used in the `Authorization` request HTTP header. +The `token_type` is the type of generated token (Bearer). The `expires_in` is an +integer representing the TTL (in seconds) of the access token. The +`refresh_token` a token that can be used to refresh the `access_token` when +expired. The `access_token` contains a `JWT` signed with the authorization +server’s private key. This token must be used in the `Authorization` request +HTTP header. diff --git a/docs/book/v1/intro.md b/docs/book/v1/intro.md index c7aaa34..8df0477 100644 --- a/docs/book/v1/intro.md +++ b/docs/book/v1/intro.md @@ -2,9 +2,9 @@ This component provides [OAuth2](https://oauth.net/2/) (server) authentication for [Expressive](https://docs.zendframework.com/zend-expressive/) and -[PSR-7](https://www.php-fig.org/psr/psr-7/) applications. It implements -`Zend\Expressive\Authentication\AuthenticationInterface`, and it be used as -an adapter for [zend-expressive-authentication](https://github.com/zendframework/zend-expressive-authentication). +[PSR-7](https://www.php-fig.org/psr/psr-7/)/[PSR-15](https://www.php-fig.org/psr/psr-15/) +applications. It implements `Zend\Expressive\Authentication\AuthenticationInterface`, +and it can be used as an adapter for [zend-expressive-authentication](https://github.com/zendframework/zend-expressive-authentication). This library uses the [league/oauth2-server](https://oauth2.thephpleague.com/) package for implementing the OAuth2 server. @@ -14,7 +14,7 @@ If you need an introduction to OAuth2, you can read the following references: - [OAuth2 documentation](https://apigility.org/documentation/auth/authentication-oauth2) from the Apigility project. - [An Introduction to OAuth 2](https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2) - by Digital Ocean. + by DigitalOcean. - The [OAuth2 specification](https://oauth.net/2/) itself, via its official website. @@ -22,15 +22,15 @@ If you need an introduction to OAuth2, you can read the following references: In order to implement the OAuth2 server, we first need to configure it. The first step is to generate new cryptographic keys. We need to execute the script -`bin/generate-oauth2-keys` in order to generate these keys. +`./vendor/bin/generate-oauth2-keys` in order to generate these keys. ```bash $ ./vendor/bin/generate-oauth2-keys ``` -This script will store the keys in the parent application `data` folder if found: +This script will store the keys in the application's `data` folder if found: -``` +```text Private key stored in: ./data/oauth/private.key Public key stored in: @@ -51,6 +51,8 @@ PSR-11 container (e.g. [zend-servicemanager](https://github.com/zendframework/ze The default values are: ```php +use League\OAuth2\Server\Grant; + return [ 'private_key' => __DIR__ . '/../data/oauth/private.key', 'public_key' => __DIR__ . '/../data/oauth/public.key', @@ -66,16 +68,11 @@ return [ // Set value to null to disable a grant 'grants' => [ - \League\OAuth2\Server\Grant\ClientCredentialsGrant::class - => \League\OAuth2\Server\Grant\ClientCredentialsGrant::class, - \League\OAuth2\Server\Grant\PasswordGrant::class - => \League\OAuth2\Server\Grant\PasswordGrant::class, - \League\OAuth2\Server\Grant\AuthCodeGrant::class - => \League\OAuth2\Server\Grant\AuthCodeGrant::class, - \League\OAuth2\Server\Grant\ImplicitGrant::class - => \League\OAuth2\Server\Grant\ImplicitGrant::class, - \League\OAuth2\Server\Grant\RefreshTokenGrant::class - => \League\OAuth2\Server\Grant\RefreshTokenGrant::class + Grant\ClientCredentialsGrant::class => Grant\ClientCredentialsGrant::class, + Grant\PasswordGrant::class => Grant\PasswordGrant::class, + Grant\AuthCodeGrant::class => Grant\AuthCodeGrant::class, + Grant\ImplicitGrant::class => Grant\ImplicitGrant::class, + Grant\RefreshTokenGrant::class => Grant\RefreshTokenGrant::class ], ]; ``` @@ -101,7 +98,7 @@ the `username`, and the `password`, if required. The SQL structure of this database is stored in the [data/oauth2.sql](https://github.com/zendframework/zend-expressive-authentication-oauth2/blob/master/data/oauth2.sql) file. -If you already have a PDO service configured, you can simply pass in the service +If you already have a PDO service configured, you can instead pass the service name to the `pdo` key as follows: ```php @@ -110,23 +107,22 @@ return [ ]; ``` -The `grants` array is for enabling/disabling grants. By default all the supported -grants are configured to be available. If you would like to disable any of the -supplied grants, simply change the value for the grant to NULL. Additionally, +The `grants` array is for enabling/disabling grants. By default, all the supported +grants are configured to be available. If you would like to disable any of the +supplied grants, change the value for the grant to `null`. Additionally, you can extend this array to add your own custom grants. - You need to provide an OAuth2 database yourself, or generate a [SQLite](https://www.sqlite.org) database with the following command (using `sqlite3` for GNU/Linux): ```bash -$ sqlite3 data/oauth2.sqlite < data/oauth2.sql +$ sqlite3 data/oauth2.sqlite < vendor/zendframework/zend-expressive-authentication-oauth2/data/oauth2.sql ``` You can also create some testing values using the `data/oauth2_test.sql` file: ```bash -$ sqlite3 data/oauth2.sqlite < data/oauth2_test.sql +$ sqlite3 data/oauth2.sqlite < vendor/zendframework/zend-expressive-authentication-oauth2/data/oauth2_test.sql ``` These commands will insert the following testing values: @@ -139,13 +135,13 @@ These commands will insert the following testing values: - a `test` scope. For security reason, the client `secret` and the user `password` are stored -using the `bcrypt` algorithm provided by [password_hash](http://php.net/manual/en/function.password-hash.php) -function of PHP. +using the `bcrypt` algorithm as used by the [password_hash](http://php.net/manual/en/function.password-hash.php) +function. ## Configure OAuth2 routes -As last step, in order to use the OAuth2 server you need to configure the routes +As the final step, in order to use the OAuth2 server you need to configure the routes for the **token endpoint** and **authorization**. You can read how add the **token endpoint** and the **authorization** routes in -the [Implement an authorization server](authorization-server) section. +the [Implement an authorization server](authorization-server.md) section. diff --git a/docs/book/v1/usage.md b/docs/book/v1/usage.md index 2d47cd1..faed762 100644 --- a/docs/book/v1/usage.md +++ b/docs/book/v1/usage.md @@ -2,7 +2,7 @@ If you successfully configured the OAuth2 server as detailed in the [installation](intro.md) section, you can request an access token using the -OAuth2 server route defined [before](intro.md#configure-the-oauth2-route) +OAuth2 server route you [defined](intro.md#configure-the-oauth2-route) (e.g. `/oauth`). You can require an access token using one of the following scenarios: @@ -18,11 +18,10 @@ You can require an access token using one of the following scenarios: This library uses the authentication abstraction of the `Zend\Expressive\Authentication\AuthenticationMiddleware` class provided by [zend-expressive-authentication](https://github.com/zendframework/zend-expressive-authentication). -In order to use OAuth2 we need to configure the service -`Zend\Expressive\Authentication\AuthenticationInterface` to resolve in -`Zend\Expressive\Authentication\OAuth2\OAuth2Adapter`. Using the -[zend-servicemanager](https://github.com/zendframework/zend-servicemanager) this -can be achieved using `aliases` with the following configuration: +In order to use OAuth2, we need to configure the service +`Zend\Expressive\Authentication\AuthenticationInterface` to resolve to +`Zend\Expressive\Authentication\OAuth2\OAuth2Adapter`. This can be achieved +using the following configuration: ```php use Zend\Expressive\Authentication; @@ -37,9 +36,10 @@ return [ ``` The previous configuration will instruct `zend-expressive-authentication` to use -the OAuth2 adapter. This adapter does not require a `Zend\Expressive\Authentication\UserRepositoryInterface`. -The OAuth2 database with user and client credentials is managed by the component -itself. +the OAuth2 adapter provided in this package. (Unlike other adapters, this +adapter does not require a `Zend\Expressive\Authentication\UserRepositoryInterface`; +the OAuth2 database with user and client credentials is managed by the component +itself.) When the service alias is configured, you can immediately begin authenticating your application/API by adding the `AuthenticationMiddleware` to either your @@ -57,4 +57,4 @@ $app->post('/api/users', [ # Providing an authorization server See the chapter [Authorization server](authorization-server.md) for details on how -to implement this. \ No newline at end of file +to implement this.