Compare commits

...

60 Commits
5.1.0 ... 5.1.5

Author SHA1 Message Date
Alex Bilbie
8e5df6d628 Updated changelog 2017-07-11 07:31:36 +01:00
Alex Bilbie
295e90c27d Trigger an E_USER_DEPRECATED notice instead of an error 2017-07-11 07:31:30 +01:00
Alex Bilbie
788ccb8605 Trigger E_USER_NOTICE instead of throwing an exception if key cannot be chmod to 600 2017-07-11 07:30:39 +01:00
Alex Bilbie
26889abdd3 5.1.4 not 5.1.14 2017-07-01 18:37:54 +01:00
Alex Bilbie
0f19a6f41c Removed HHVM from .travis.yml 2017-07-01 18:34:53 +01:00
Alex Bilbie
4e996ab3f1 Updated README 2017-07-01 18:34:32 +01:00
Alex Bilbie
55f93f9400 Merge pull request #752 from thephpleague/analysis-qBDGNm
Apply fixes from StyleCI
2017-07-01 17:20:19 +01:00
Alex Bilbie
aee1779432 Apply fixes from StyleCI 2017-07-01 16:19:23 +00:00
Alex Bilbie
09c167ac43 Updated changelog and readme 2017-07-01 17:17:55 +01:00
Alex Bilbie
765a01021b Updated error message 2017-07-01 16:45:29 +01:00
Alex Bilbie
0706d66c76 Don’t pad and shuffle the payload if an encryption key has been set 2017-07-01 16:45:29 +01:00
Alex Bilbie
e123fe82d0 Ignore error_log messages in code coverage 2017-07-01 16:45:29 +01:00
Alex Bilbie
107cfc3678 Updated examples 2017-07-01 16:45:29 +01:00
Alex Bilbie
1954120c3d Use catch all exception 2017-07-01 16:45:29 +01:00
Alex Bilbie
dd5eee150d Ensure response type also has access to the encryption key 2017-07-01 16:45:29 +01:00
Alex Bilbie
76c1349181 Updated random_compat version 2017-07-01 16:45:29 +01:00
Alex Bilbie
1af4012df4 New property on AuthorizationServer to receive an encryption key which is used for future encryption/decryption instead of keybased encryption/decryption 2017-07-01 16:45:29 +01:00
Alex Bilbie
4a717104fa Shuffle the contents of the authorization code payload 2017-07-01 16:45:29 +01:00
Alex Bilbie
63530443fe Better error checking when saving a temporary key to ensure file was written successfully and the server is the exclusive mode 2017-07-01 16:44:57 +01:00
Alex Bilbie
2f8de3d230 Ensure the server is the exclusive owner of the key 2017-07-01 16:44:51 +01:00
Alex Bilbie
57d199b889 Stricter validation of code challenge value to match RFC 7636 requirements 2017-07-01 16:44:43 +01:00
Alex Bilbie
6bdd108145 Escape scope parameter to reduce pontential XSS vector 2017-07-01 16:43:31 +01:00
Alex Bilbie
bf7084a147 Merge pull request #709 from toby-griffiths/fix-refresh-token-ttl
Corrected DateInterval from 1 min to 1 month
2017-03-02 14:06:27 +00:00
Toby Griffiths
13c608b849 Corrected DateInterval from 1 min to 1 month 2017-03-01 13:08:42 +00:00
Alex Bilbie
ded7c1ed47 Mentioned PHP 7.1 support 2017-02-02 17:29:06 +00:00
Alex Bilbie
0da70c916a Merge pull request #690 from Jalle19/patch-1
Fix typo in the first README sentence
2016-12-23 07:42:23 +00:00
Sam Stenvall
90cb1bf012 Fix typo in the first README sentence 2016-12-23 00:30:54 +02:00
Alex Bilbie
b32204bd91 Merge pull request #682 from wilsonge/patch-1
Fix middleware example fatal error
2016-11-08 13:18:13 +00:00
George Wilson
518c1fcec5 Fix middleware example fatal error 2016-11-08 12:27:49 +00:00
Alex Bilbie
6946592553 Merge pull request #671 from duncan3dc/patch-1
[Travis] Test on PHP 7.1
2016-10-16 16:58:15 +01:00
Craig Duncan
25580b98b7 [Travis] Test on PHP 7.1 2016-10-16 16:48:44 +01:00
Alex Bilbie
f78dc2eca0 Updated README 2016-10-12 15:08:15 +01:00
Alex Bilbie
105b3116dc Merge pull request #669 from jeremykendall/fix/www-authenticate-header
Fix WWW-Authenticate entry in $headers array
2016-10-12 15:05:19 +01:00
jeremykendall
01677a564e Fix WWW-Authenticate entry in $headers array
In this context the header name should be the array key and the header
value the array value.
2016-10-11 22:27:24 -05:00
Alex Bilbie
4c4b0633b1 Merge pull request #668 from er0k/increase-ssl-key-length
Increase the recommended RSA key length from 1024 to 2048 bits
2016-10-11 14:27:16 +01:00
er0k
c4a75b2880 Increase the recommended RSA key length from 1024 to 2048 bits 2016-10-11 09:24:27 -04:00
Alex Bilbie
e091d48127 Changelog bump 2016-09-19 10:23:42 +01:00
Alex Bilbie
a798cfdc5d Merge pull request #656 from thephpleague/issue-650-fix
Fix for #650
2016-09-19 10:19:05 +01:00
Alex Bilbie
56e8d374fb Fix broken tests 2016-09-19 10:06:00 +01:00
Alex Bilbie
b1bfff7325 Don't pass in user because we don't know who user is 2016-09-19 10:05:55 +01:00
Alex Bilbie
32cde01ab2 Merge pull request #657 from thephpleague/analysis-86wPg4
Applied fixes from StyleCI
2016-09-13 15:19:56 +01:00
Alex Bilbie
11ccc305d0 Applied fixes from StyleCI 2016-09-13 14:17:09 +00:00
Alex Bilbie
d7df2f7e24 Fix for #650 2016-09-13 15:16:58 +01:00
Alex Bilbie
b8b92e5925 Changelog update 2016-07-26 15:42:03 -04:00
Alex Bilbie
0ebdcd2ab8 Merge pull request #614 from lookyman/better-tests
Improved tests
2016-07-25 12:17:28 -04:00
Alex Bilbie
9dee08ba3d Merge pull request #625 from juliangut/key-file
Key file auto-generation from string
2016-07-19 17:24:12 +01:00
Julián Gutiérrez
065ef5db99 CryptKey tests 2016-07-19 17:15:36 +02:00
Julián Gutiérrez
039537ebe2 touch! 2016-07-19 15:06:32 +02:00
Julián Gutiérrez
d8930af5ee key file auto-generation from string 2016-07-19 15:01:31 +02:00
Alex Bilbie
ada8d20be6 Merge pull request #624 from iansltx/bearer-token-response-params
Allow easy addition of custom fields to Bearer token response
2016-07-16 16:38:23 +01:00
Ian Littman
090c01d3d1 Allow easy addition of custom fields to Bearer token response 2016-07-16 10:27:33 -05:00
Alex Bilbie
4b6ba5859c Merge pull request #621 from pounard/master
while(array_shift()) makes the AuthorizationServer class configuratio…
2016-07-13 11:02:26 +01:00
Pierre Rineau
57323f38f7 while(array_shift()) makes the AuthorizationServer class configuration mutable 2016-07-13 12:03:05 +02:00
Alex Bilbie
46cd448a47 Merge pull request #616 from lookyman/phpdoc
Updated PHPDoc
2016-07-10 09:32:13 +01:00
Lukáš Unger
c874c59b9c Explicitly compare to false when checking not instanceof 2016-07-09 12:09:21 +02:00
Lukáš Unger
c3a4670c11 Updated PHPDoc 2016-07-09 02:01:53 +02:00
Lukáš Unger
17b6e2a207 tests: Fix missing redirect uri test, add redirect uri mismatch test 2016-07-08 16:04:14 +02:00
Lukáš Unger
54422a244f tests: AuthCodeGrantTest additional tests 2016-07-08 15:31:29 +02:00
Lukáš Unger
9899aa1f99 tests: ImplicitGrantTest additional tests 2016-07-08 15:30:59 +02:00
Lukáš Unger
32efd091a1 tests: use MockBuilder everywhere 2016-07-08 15:29:21 +02:00
63 changed files with 1127 additions and 412 deletions

View File

@@ -11,7 +11,7 @@ php:
- 5.5
- 5.6
- 7.0
- hhvm
- 7.1
install:
- travis_retry composer install --no-interaction --prefer-source
@@ -21,4 +21,4 @@ script:
branches:
only:
- master
- master

View File

@@ -1,5 +1,38 @@
# Changelog
## 5.1.5 (released 2017-07-11)
To address feedback from the security release the following two changes have been made:
* If an RSA key cannot be `chmod`'ed to 600 then it will now throw a `E_USER_NOTICE` instead of an exception.
* Not using the new encryption key method on `AuthorizationServer` will set throw an `E_USER_DEPRECATED` message instead of an error.
## 5.1.4 (released 2017-07-01)
* Fixed multiple security vulnerabilities as a result of a security audit paid for by the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source). All users of this library are encouraged to update as soon as possible to this version or version 6.0 or greater.
* It is recommended on each `AuthorizationServer` instance you set the `setEncryptionKey()`. This will result in stronger encryption being used. If this method is not set messages will be sent to the defined error handling routines (using `error_log`). Please see the examples and documentation for examples.
* TravisCI now tests PHP 7.1 (Issue #671)
* Fix middleware example fatal error (Issue #682)
* Fix typo in the first README sentence (Issue #690)
* Corrected DateInterval from 1 min to 1 month (Issue #709)
## 5.1.3 (released 2016-10-12)
* Fixed WWW-Authenticate header (Issue #669)
* Increase the recommended RSA key length from 1024 to 2048 bits (Issue #668)
## 5.1.2 (released 2016-09-19)
* Fixed `finalizeScopes` call (Issue #650)
## 5.1.1 (released 2016-07-26)
* Improved test suite (Issue #614)
* Updated docblocks (Issue #616)
* Replace `array_shift` with `foreach` loop (Issue #621)
* Allow easy addition of custom fields to Bearer token response (Issue #624)
* Key file auto-generation from string (Issue #625)
## 5.1.0 (released 2016-06-28)
* Implemented RFC7636 (Issue #574)

View File

@@ -1,5 +1,11 @@
# PHP OAuth 2.0 Server
### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning:
### Security Notice
### Please upgrade to version `>=5.1.4` (backwards compatible) or `6.x` (one tiny breaking change) to fix some potential security vulnerabilities
### :warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning::warning:
[![Latest Version](http://img.shields.io/packagist/v/league/oauth2-server.svg?style=flat-square)](https://github.com/thephpleague/oauth2-server/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
[![Build Status](https://img.shields.io/travis/thephpleague/oauth2-server/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/oauth2-server)
@@ -7,7 +13,7 @@
[![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/oauth2-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-server)
[![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-server.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-server)
`league/oauth2-server` is a a standards compliant implementation of an [OAuth 2.0](https://tools.ietf.org/html/rfc6749) authorization server written in PHP which makes working with OAuth 2.0 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.
`league/oauth2-server` is a standards compliant implementation of an [OAuth 2.0](https://tools.ietf.org/html/rfc6749) authorization server written in PHP which makes working with OAuth 2.0 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:
@@ -33,6 +39,7 @@ The following versions of PHP are supported:
* PHP 5.5 (>=5.5.9)
* PHP 5.6
* PHP 7.0
* PHP 7.1
* HHVM
The `openssl` extension is also required.
@@ -72,6 +79,8 @@ This package is released under the MIT License. See the bundled [LICENSE](https:
This code is principally developed and maintained by [Alex Bilbie](https://twitter.com/alexbilbie).
Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors)
Special thanks to [all of these awesome contributors](https://github.com/thephpleague/oauth2-server/contributors).
Additional thanks go to the [Mozilla Secure Open Source Fund](https://wiki.mozilla.org/MOSS/Secure_Open_Source) for funding a security audit of this library.
The initial code was developed as part of the [Linkey](http://linkey.blogs.lincoln.ac.uk) project which was funded by [JISC](http://jisc.ac.uk) under the Access and Identity Management programme.

View File

@@ -8,8 +8,9 @@
"ext-openssl": "*",
"league/event": "^2.1",
"lcobucci/jwt": "^3.1",
"paragonie/random_compat": "^1.1 || ^2.0",
"psr/http-message": "^1.0"
"paragonie/random_compat": "^2.0",
"psr/http-message": "^1.0",
"defuse/php-encryption": "^2.1"
},
"require-dev": {
"phpunit/phpunit": "^4.8 || ^5.0",

View File

@@ -3,7 +3,7 @@
## Installation
0. Run `composer install` in this directory to install dependencies
0. Create a private key `openssl genrsa -out private.key 1024`
0. Create a private key `openssl genrsa -out private.key 2048`
0. Create a public key `openssl rsa -in private.key -pubout > public.key`
0. `cd` into the public directory
0. Start a PHP server `php -S localhost:4444`

View File

@@ -5,8 +5,9 @@
"require-dev": {
"league/event": "^2.1",
"lcobucci/jwt": "^3.1",
"paragonie/random_compat": "^1.1",
"psr/http-message": "^1.0"
"paragonie/random_compat": "^2.0",
"psr/http-message": "^1.0",
"defuse/php-encryption": "^2.1"
},
"autoload": {
"psr-4": {

178
examples/composer.lock generated
View File

@@ -4,23 +4,25 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "48bcb7a3514d7c7f271c554ba1440124",
"content-hash": "e41be75973527cb9d63f27ad14ac8624",
"content-hash": "9813ed7c3b6dcf107f44df9392935b8f",
"packages": [
{
"name": "container-interop/container-interop",
"version": "1.1.0",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/container-interop/container-interop.git",
"reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e"
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e",
"reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e",
"url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
"shasum": ""
},
"require": {
"psr/container": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
@@ -32,7 +34,8 @@
"MIT"
],
"description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
"time": "2014-12-30 15:22:37"
"homepage": "https://github.com/container-interop/container-interop",
"time": "2017-02-14T19:40:03+00:00"
},
{
"name": "nikic/fast-route",
@@ -75,7 +78,7 @@
"router",
"routing"
],
"time": "2015-06-18 19:15:47"
"time": "2015-06-18T19:15:47+00:00"
},
{
"name": "pimple/pimple",
@@ -121,20 +124,69 @@
"container",
"dependency injection"
],
"time": "2015-09-11 15:10:35"
"time": "2015-09-11T15:10:35+00:00"
},
{
"name": "psr/http-message",
"version": "1.0",
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298"
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
@@ -162,6 +214,7 @@
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
@@ -170,7 +223,7 @@
"request",
"response"
],
"time": "2015-05-04 20:22:00"
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "slim/slim",
@@ -236,22 +289,85 @@
"micro",
"router"
],
"time": "2015-12-07 14:11:09"
"time": "2015-12-07T14:11:09+00:00"
}
],
"packages-dev": [
{
"name": "lcobucci/jwt",
"version": "3.1.1",
"name": "defuse/php-encryption",
"version": "v2.1.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
"reference": "afea8e682e911a21574fd8519321b32522fa25b5"
"url": "https://github.com/defuse/php-encryption.git",
"reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/afea8e682e911a21574fd8519321b32522fa25b5",
"reference": "afea8e682e911a21574fd8519321b32522fa25b5",
"url": "https://api.github.com/repos/defuse/php-encryption/zipball/5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689",
"reference": "5176f5abb38d3ea8a6e3ac6cd3bbb54d8185a689",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"paragonie/random_compat": "~2.0",
"php": ">=5.4.0"
},
"require-dev": {
"nikic/php-parser": "^2.0|^3.0",
"phpunit/phpunit": "^4|^5"
},
"bin": [
"bin/generate-defuse-key"
],
"type": "library",
"autoload": {
"psr-4": {
"Defuse\\Crypto\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Hornby",
"email": "taylor@defuse.ca",
"homepage": "https://defuse.ca/"
},
{
"name": "Scott Arciszewski",
"email": "info@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "Secure PHP Encryption Library",
"keywords": [
"aes",
"authenticated encryption",
"cipher",
"crypto",
"cryptography",
"encrypt",
"encryption",
"openssl",
"security",
"symmetric key cryptography"
],
"time": "2017-05-18T21:28:48+00:00"
},
{
"name": "lcobucci/jwt",
"version": "3.2.1",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
"reference": "ddce703826f9c5229781933b1a39069e38e6a0f3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/ddce703826f9c5229781933b1a39069e38e6a0f3",
"reference": "ddce703826f9c5229781933b1a39069e38e6a0f3",
"shasum": ""
},
"require": {
@@ -259,7 +375,7 @@
"php": ">=5.5"
},
"require-dev": {
"mdanter/ecc": "~0.3",
"mdanter/ecc": "~0.3.1",
"mikey179/vfsstream": "~1.5",
"phpmd/phpmd": "~2.2",
"phpunit/php-invoker": "~1.1",
@@ -296,7 +412,7 @@
"JWS",
"jwt"
],
"time": "2016-03-24 22:46:13"
"time": "2016-10-31T20:09:32+00:00"
},
{
"name": "league/event",
@@ -346,20 +462,20 @@
"event",
"listener"
],
"time": "2015-05-21 12:24:47"
"time": "2015-05-21T12:24:47+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v1.4.1",
"version": "v2.0.10",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "c7e26a21ba357863de030f0b9e701c7d04593774"
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/c7e26a21ba357863de030f0b9e701c7d04593774",
"reference": "c7e26a21ba357863de030f0b9e701c7d04593774",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d",
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d",
"shasum": ""
},
"require": {
@@ -394,7 +510,7 @@
"pseudorandom",
"random"
],
"time": "2016-03-18 20:34:03"
"time": "2017-03-13T16:27:32+00:00"
}
],
"aliases": [],

View File

@@ -31,7 +31,6 @@ $app->add(
$app->get(
'/users',
function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
$users = [
[
'id' => 123,
@@ -70,4 +69,4 @@ $app->get(
}
);
$app->run();
$app->run();

View File

@@ -46,6 +46,7 @@ $app = new App([
$privateKeyPath,
$publicKeyPath
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
// Enable the authentication code grant on the server with a token TTL of 1 hour
$server->enableGrantType(

View File

@@ -30,9 +30,9 @@ $app = new App([
$accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface
// Path to public and private keys
$privateKey = 'file://'.__DIR__.'/../private.key';
$privateKey = 'file://' . __DIR__ . '/../private.key';
//$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase
$publicKey = 'file://'.__DIR__.'/../public.key';
$publicKey = 'file://' . __DIR__ . '/../public.key';
// Setup the authorization server
$server = new AuthorizationServer(
@@ -42,6 +42,7 @@ $app = new App([
$privateKey,
$publicKey
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
// Enable the client credentials grant on the server
$server->enableGrantType(

View File

@@ -42,6 +42,7 @@ $app = new App([
$privateKeyPath,
$publicKeyPath
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
// Enable the implicit grant on the server with a token TTL of 1 hour
$server->enableGrantType(new ImplicitGrant(new \DateInterval('PT1H')));

View File

@@ -12,6 +12,7 @@ use League\OAuth2\Server\Grant\AuthCodeGrant;
use League\OAuth2\Server\Grant\RefreshTokenGrant;
use League\OAuth2\Server\Middleware\AuthorizationServerMiddleware;
use League\OAuth2\Server\Middleware\ResourceServerMiddleware;
use League\OAuth2\Server\ResourceServer;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\AuthCodeRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
@@ -47,6 +48,7 @@ $app = new App([
$privateKeyPath,
$publicKeyPath
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
// Enable the authentication code grant on the server with a token TTL of 1 hour
$server->enableGrantType(
@@ -61,7 +63,17 @@ $app = new App([
// Enable the refresh token grant on the server with a token TTL of 1 month
$server->enableGrantType(
new RefreshTokenGrant($refreshTokenRepository),
new \DateInterval('PT1M')
new \DateInterval('P1M')
);
return $server;
},
ResourceServer::class => function () {
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';
$server = new ResourceServer(
new AccessTokenRepository(),
$publicKeyPath
);
return $server;
@@ -94,6 +106,6 @@ $app->group('/api', function () {
return $response->withBody($body);
});
})->add(new ResourceServerMiddleware($app->getContainer()->get(AuthorizationServer::class)));
})->add(new ResourceServerMiddleware($app->getContainer()->get(ResourceServer::class)));
$app->run();

View File

@@ -23,9 +23,10 @@ $app = new App([
new ClientRepository(), // instance of ClientRepositoryInterface
new AccessTokenRepository(), // instance of AccessTokenRepositoryInterface
new ScopeRepository(), // instance of ScopeRepositoryInterface
'file://'.__DIR__.'/../private.key', // path to private key
'file://'.__DIR__.'/../public.key' // path to public key
'file://' . __DIR__ . '/../private.key', // path to private key
'file://' . __DIR__ . '/../public.key' // path to public key
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
$grant = new PasswordGrant(
new UserRepository(), // instance of UserRepositoryInterface
@@ -54,19 +55,17 @@ $app->post(
// Try to respond to the access token request
return $server->respondToAccessTokenRequest($request, $response);
} catch (OAuthServerException $exception) {
// All instances of OAuthServerException can be converted to a PSR-7 response
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
// Catch unexpected exceptions
$body = $response->getBody();
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
return $response->withStatus(500)->withBody($body);
}
}
);

View File

@@ -17,7 +17,6 @@ use OAuth2ServerExamples\Repositories\ScopeRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
use Zend\Diactoros\Stream;
include __DIR__ . '/../vendor/autoload.php';
@@ -43,6 +42,7 @@ $app = new App([
$privateKeyPath,
$publicKeyPath
);
$server->setEncryptionKey('lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen');
// Enable the refresh token grant on the server
$grant = new RefreshTokenGrant($refreshTokenRepository);
@@ -66,10 +66,9 @@ $app->post('/access_token', function (ServerRequestInterface $request, ResponseI
} catch (OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
$response->getBody()->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
return $response->withStatus(500);
}
});

View File

@@ -54,7 +54,7 @@ class ScopeRepository implements ScopeRepositoryInterface
$scope->setIdentifier('email');
$scopes[] = $scope;
}
return $scopes;
}
}

View File

@@ -9,7 +9,6 @@
namespace League\OAuth2\Server;
use DateInterval;
use League\Event\EmitterAwareInterface;
use League\Event\EmitterAwareTrait;
use League\OAuth2\Server\Exception\OAuthServerException;
@@ -27,8 +26,10 @@ class AuthorizationServer implements EmitterAwareInterface
{
use EmitterAwareTrait;
const ENCRYPTION_KEY_ERROR = 'You must set the encryption key going forward to improve the security of this library - see this page for more information https://oauth2.thephpleague.com/v5-security-improvements/';
/**
* @var \League\OAuth2\Server\Grant\GrantTypeInterface[]
* @var GrantTypeInterface[]
*/
protected $enabledGrantTypes = [];
@@ -38,44 +39,49 @@ class AuthorizationServer implements EmitterAwareInterface
protected $grantTypeAccessTokenTTL = [];
/**
* @var \League\OAuth2\Server\CryptKey
* @var CryptKey
*/
protected $privateKey;
/**
* @var \League\OAuth2\Server\CryptKey
* @var CryptKey
*/
protected $publicKey;
/**
* @var ResponseTypeInterface
* @var null|ResponseTypeInterface
*/
protected $responseType;
/**
* @var \League\OAuth2\Server\Repositories\ClientRepositoryInterface
* @var ClientRepositoryInterface
*/
private $clientRepository;
/**
* @var \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface
* @var AccessTokenRepositoryInterface
*/
private $accessTokenRepository;
/**
* @var \League\OAuth2\Server\Repositories\ScopeRepositoryInterface
* @var ScopeRepositoryInterface
*/
private $scopeRepository;
/**
* @var string
*/
private $encryptionKey;
/**
* New server instance.
*
* @param \League\OAuth2\Server\Repositories\ClientRepositoryInterface $clientRepository
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
* @param \League\OAuth2\Server\Repositories\ScopeRepositoryInterface $scopeRepository
* @param \League\OAuth2\Server\CryptKey|string $privateKey
* @param \League\OAuth2\Server\CryptKey|string $publicKey
* @param null|\League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
* @param ClientRepositoryInterface $clientRepository
* @param AccessTokenRepositoryInterface $accessTokenRepository
* @param ScopeRepositoryInterface $scopeRepository
* @param CryptKey|string $privateKey
* @param CryptKey|string $publicKey
* @param null|ResponseTypeInterface $responseType
*/
public function __construct(
ClientRepositoryInterface $clientRepository,
@@ -89,12 +95,12 @@ class AuthorizationServer implements EmitterAwareInterface
$this->accessTokenRepository = $accessTokenRepository;
$this->scopeRepository = $scopeRepository;
if (!$privateKey instanceof CryptKey) {
if ($privateKey instanceof CryptKey === false) {
$privateKey = new CryptKey($privateKey);
}
$this->privateKey = $privateKey;
if (!$publicKey instanceof CryptKey) {
if ($publicKey instanceof CryptKey === false) {
$publicKey = new CryptKey($publicKey);
}
$this->publicKey = $publicKey;
@@ -102,15 +108,25 @@ class AuthorizationServer implements EmitterAwareInterface
$this->responseType = $responseType;
}
/**
* Set the encryption key
*
* @param string $key
*/
public function setEncryptionKey($key)
{
$this->encryptionKey = $key;
}
/**
* Enable a grant type on the server.
*
* @param \League\OAuth2\Server\Grant\GrantTypeInterface $grantType
* @param \DateInterval $accessTokenTTL
* @param GrantTypeInterface $grantType
* @param null|\DateInterval $accessTokenTTL
*/
public function enableGrantType(GrantTypeInterface $grantType, DateInterval $accessTokenTTL = null)
public function enableGrantType(GrantTypeInterface $grantType, \DateInterval $accessTokenTTL = null)
{
if ($accessTokenTTL instanceof DateInterval === false) {
if ($accessTokenTTL instanceof \DateInterval === false) {
$accessTokenTTL = new \DateInterval('PT1H');
}
@@ -121,6 +137,13 @@ class AuthorizationServer implements EmitterAwareInterface
$grantType->setPublicKey($this->publicKey);
$grantType->setEmitter($this->getEmitter());
if ($this->encryptionKey === null) {
// @codeCoverageIgnoreStart
trigger_error(self::ENCRYPTION_KEY_ERROR, E_USER_DEPRECATED);
// @codeCoverageIgnoreEnd
}
$grantType->setEncryptionKey($this->encryptionKey);
$this->enabledGrantTypes[$grantType->getIdentifier()] = $grantType;
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()] = $accessTokenTTL;
}
@@ -128,22 +151,23 @@ class AuthorizationServer implements EmitterAwareInterface
/**
* Validate an authorization request
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param ServerRequestInterface $request
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return \League\OAuth2\Server\RequestTypes\AuthorizationRequest|null
* @return AuthorizationRequest
*/
public function validateAuthorizationRequest(ServerRequestInterface $request)
{
$authRequest = null;
$enabledGrantTypes = $this->enabledGrantTypes;
while ($authRequest === null && $grantType = array_shift($enabledGrantTypes)) {
/** @var \League\OAuth2\Server\Grant\GrantTypeInterface $grantType */
if ($grantType->canRespondToAuthorizationRequest($request)) {
$authRequest = $grantType->validateAuthorizationRequest($request);
if ($this->encryptionKey === null) {
// @codeCoverageIgnoreStart
trigger_error(self::ENCRYPTION_KEY_ERROR, E_USER_DEPRECATED);
// @codeCoverageIgnoreEnd
}
return $authRequest;
foreach ($this->enabledGrantTypes as $grantType) {
if ($grantType->canRespondToAuthorizationRequest($request)) {
return $grantType->validateAuthorizationRequest($request);
}
}
@@ -153,10 +177,10 @@ class AuthorizationServer implements EmitterAwareInterface
/**
* Complete an authorization request
*
* @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest
* @param \Psr\Http\Message\ResponseInterface $response
* @param AuthorizationRequest $authRequest
* @param ResponseInterface $response
*
* @return \Psr\Http\Message\ResponseInterface
* @return ResponseInterface
*/
public function completeAuthorizationRequest(AuthorizationRequest $authRequest, ResponseInterface $response)
{
@@ -168,29 +192,27 @@ class AuthorizationServer implements EmitterAwareInterface
/**
* Return an access token response.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \Psr\Http\Message\ResponseInterface $response
* @param ServerRequestInterface $request
* @param ResponseInterface $response
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return \Psr\Http\Message\ResponseInterface
* @return ResponseInterface
*/
public function respondToAccessTokenRequest(ServerRequestInterface $request, ResponseInterface $response)
{
$tokenResponse = null;
while ($tokenResponse === null && $grantType = array_shift($this->enabledGrantTypes)) {
/** @var \League\OAuth2\Server\Grant\GrantTypeInterface $grantType */
foreach ($this->enabledGrantTypes as $grantType) {
if ($grantType->canRespondToAccessTokenRequest($request)) {
$tokenResponse = $grantType->respondToAccessTokenRequest(
$request,
$this->getResponseType(),
$this->grantTypeAccessTokenTTL[$grantType->getIdentifier()]
);
}
}
if ($tokenResponse instanceof ResponseTypeInterface) {
return $tokenResponse->generateHttpResponse($response);
if ($tokenResponse instanceof ResponseTypeInterface) {
return $tokenResponse->generateHttpResponse($response);
}
}
}
throw OAuthServerException::unsupportedGrantType();
@@ -203,11 +225,12 @@ class AuthorizationServer implements EmitterAwareInterface
*/
protected function getResponseType()
{
if (!$this->responseType instanceof ResponseTypeInterface) {
if ($this->responseType instanceof ResponseTypeInterface === false) {
$this->responseType = new BearerTokenResponse();
}
$this->responseType->setPrivateKey($this->privateKey);
$this->responseType->setEncryptionKey($this->encryptionKey);
return $this->responseType;
}

View File

@@ -22,14 +22,12 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
use CryptTrait;
/**
* @var \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface
* @var AccessTokenRepositoryInterface
*/
private $accessTokenRepository;
/**
* BearerTokenValidator constructor.
*
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
* @param AccessTokenRepositoryInterface $accessTokenRepository
*/
public function __construct(AccessTokenRepositoryInterface $accessTokenRepository)
{
@@ -77,7 +75,7 @@ class BearerTokenValidator implements AuthorizationValidatorInterface
} catch (\InvalidArgumentException $exception) {
// JWT couldn't be parsed so return the request as is
throw OAuthServerException::accessDenied($exception->getMessage());
} catch(\RuntimeException $exception){
} catch (\RuntimeException $exception) {
//JWR couldn't be parsed so return the request as is
throw OAuthServerException::accessDenied('Error while decoding to JSON');
}

View File

@@ -8,17 +8,21 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server;
class CryptKey
{
const RSA_KEY_PATTERN =
'/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----\n)(.|\n)+(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)$/';
/**
* @var string
*/
protected $keyPath;
/**
* @var string
* @var null|string
*/
protected $passPhrase;
@@ -28,6 +32,10 @@ class CryptKey
*/
public function __construct($keyPath, $passPhrase = null)
{
if (preg_match(self::RSA_KEY_PATTERN, $keyPath)) {
$keyPath = $this->saveKeyToFile($keyPath);
}
if (strpos($keyPath, 'file://') !== 0) {
$keyPath = 'file://' . $keyPath;
}
@@ -36,10 +44,61 @@ class CryptKey
throw new \LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
}
// Verify the permissions of the key
$keyPathPerms = decoct(fileperms($keyPath) & 0777);
if ($keyPathPerms !== '600') {
// Attempt to correct the permissions
if (chmod($keyPath, 0600) === false) {
// @codeCoverageIgnoreStart
trigger_error(
sprintf(
'Key file "%s" permissions are not correct, should be 600 instead of %s, unable to automatically resolve the issue',
$keyPath,
$keyPathPerms
),
E_USER_NOTICE
);
// @codeCoverageIgnoreEnd
}
}
$this->keyPath = $keyPath;
$this->passPhrase = $passPhrase;
}
/**
* @param string $key
*
* @throws \RuntimeException
*
* @return string
*/
private function saveKeyToFile($key)
{
$tmpDir = sys_get_temp_dir();
$keyPath = $tmpDir . '/' . sha1($key) . '.key';
if (!file_exists($keyPath) && !touch($keyPath)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('"%s" key file could not be created', $keyPath);
// @codeCoverageIgnoreEnd
}
if (file_put_contents($keyPath, $key) === false) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('Unable to write key file to temporary directory "%s"', $tmpDir);
// @codeCoverageIgnoreEnd
}
if (chmod($keyPath, 0600) === false) {
// @codeCoverageIgnoreStart
throw new \RuntimeException('The key file "%s" file mode could not be changed with chmod to 600', $keyPath);
// @codeCoverageIgnoreEnd
}
return 'file://' . $keyPath;
}
/**
* Retrieve key path.
*

View File

@@ -8,24 +8,32 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server;
use Defuse\Crypto\Crypto;
trait CryptTrait
{
/**
* @var \League\OAuth2\Server\CryptKey
* @var CryptKey
*/
protected $privateKey;
/**
* @var \League\OAuth2\Server\CryptKey
* @var CryptKey
*/
protected $publicKey;
/**
* @var string
*/
protected $encryptionKey;
/**
* Set path to private key.
*
* @param \League\OAuth2\Server\CryptKey $privateKey
* @param CryptKey $privateKey
*/
public function setPrivateKey(CryptKey $privateKey)
{
@@ -35,7 +43,7 @@ trait CryptTrait
/**
* Set path to public key.
*
* @param \League\OAuth2\Server\CryptKey $publicKey
* @param CryptKey $publicKey
*/
public function setPublicKey(CryptKey $publicKey)
{
@@ -47,10 +55,16 @@ trait CryptTrait
*
* @param string $unencryptedData
*
* @throws \LogicException
*
* @return string
*/
protected function encrypt($unencryptedData)
{
if ($this->encryptionKey !== null) {
return Crypto::encryptWithPassword($unencryptedData, $this->encryptionKey);
}
$privateKey = openssl_pkey_get_private($this->privateKey->getKeyPath(), $this->privateKey->getPassPhrase());
$privateKeyDetails = @openssl_pkey_get_details($privateKey);
if ($privateKeyDetails === null) {
@@ -88,6 +102,10 @@ trait CryptTrait
*/
protected function decrypt($encryptedData)
{
if ($this->encryptionKey !== null) {
return Crypto::decryptWithPassword($encryptedData, $this->encryptionKey);
}
$publicKey = openssl_pkey_get_public($this->publicKey->getKeyPath());
$publicKeyDetails = @openssl_pkey_get_details($publicKey);
if ($publicKeyDetails === null) {
@@ -115,4 +133,14 @@ trait CryptTrait
return $output;
}
/**
* Set the encryption key
*
* @param string $key
*/
public function setEncryptionKey($key = null)
{
$this->encryptionKey = $key;
}
}

View File

@@ -16,7 +16,7 @@ interface AccessTokenEntityInterface extends TokenInterface
/**
* Generate a JWT from the access token
*
* @param \League\OAuth2\Server\CryptKey $privateKey
* @param CryptKey $privateKey
*
* @return string
*/

View File

@@ -42,14 +42,14 @@ interface RefreshTokenEntityInterface
/**
* Set the access token that the refresh token was associated with.
*
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
* @param AccessTokenEntityInterface $accessToken
*/
public function setAccessToken(AccessTokenEntityInterface $accessToken);
/**
* Get the access token that the refresh token was originally associated with.
*
* @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface
* @return AccessTokenEntityInterface
*/
public function getAccessToken();
}

View File

@@ -63,14 +63,14 @@ interface TokenInterface
/**
* Set the client that the token was issued to.
*
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
* @param ClientEntityInterface $client
*/
public function setClient(ClientEntityInterface $client);
/**
* Associate a scope with the token.
*
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface $scope
* @param ScopeEntityInterface $scope
*/
public function addScope(ScopeEntityInterface $scope);

View File

@@ -21,7 +21,7 @@ trait AccessTokenTrait
/**
* Generate a JWT from the access token
*
* @param \League\OAuth2\Server\CryptKey $privateKey
* @param CryptKey $privateKey
*
* @return string
*/

View File

@@ -11,8 +11,14 @@ namespace League\OAuth2\Server\Entities\Traits;
trait ClientTrait
{
/**
* @var string
*/
protected $name;
/**
* @var string|string[]
*/
protected $redirectUri;
/**

View File

@@ -9,7 +9,6 @@
namespace League\OAuth2\Server\Entities\Traits;
use DateTime;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
trait RefreshTokenTrait
@@ -20,7 +19,7 @@ trait RefreshTokenTrait
protected $accessToken;
/**
* @var DateTime
* @var \DateTime
*/
protected $expiryDateTime;
@@ -43,7 +42,7 @@ trait RefreshTokenTrait
/**
* Get the token's expiry date time.
*
* @return DateTime
* @return \DateTime
*/
public function getExpiryDateTime()
{
@@ -53,9 +52,9 @@ trait RefreshTokenTrait
/**
* Set the date time when the token expires.
*
* @param DateTime $dateTime
* @param \DateTime $dateTime
*/
public function setExpiryDateTime(DateTime $dateTime)
public function setExpiryDateTime(\DateTime $dateTime)
{
$this->expiryDateTime = $dateTime;
}

View File

@@ -9,7 +9,6 @@
namespace League\OAuth2\Server\Entities\Traits;
use DateTime;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
@@ -21,7 +20,7 @@ trait TokenEntityTrait
protected $scopes = [];
/**
* @var DateTime
* @var \DateTime
*/
protected $expiryDateTime;
@@ -38,7 +37,7 @@ trait TokenEntityTrait
/**
* Associate a scope with the token.
*
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface $scope
* @param ScopeEntityInterface $scope
*/
public function addScope(ScopeEntityInterface $scope)
{
@@ -58,7 +57,7 @@ trait TokenEntityTrait
/**
* Get the token's expiry date time.
*
* @return DateTime
* @return \DateTime
*/
public function getExpiryDateTime()
{
@@ -68,9 +67,9 @@ trait TokenEntityTrait
/**
* Set the date time when the token expires.
*
* @param DateTime $dateTime
* @param \DateTime $dateTime
*/
public function setExpiryDateTime(DateTime $dateTime)
public function setExpiryDateTime(\DateTime $dateTime)
{
$this->expiryDateTime = $dateTime;
}
@@ -108,7 +107,7 @@ trait TokenEntityTrait
/**
* Set the client that the token was issued to.
*
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
* @param ClientEntityInterface $client
*/
public function setClient(ClientEntityInterface $client)
{

View File

@@ -69,7 +69,7 @@ class OAuthServerException extends \Exception
* Invalid request error.
*
* @param string $parameter The invalid parameter
* @param string|null $hint
* @param null|string $hint
*
* @return static
*/
@@ -105,7 +105,10 @@ class OAuthServerException extends \Exception
public static function invalidScope($scope, $redirectUri = null)
{
$errorMessage = 'The requested scope is invalid, unknown, or malformed';
$hint = sprintf('Check the `%s` scope', $scope);
$hint = sprintf(
'Check the `%s` scope',
htmlspecialchars($scope, ENT_QUOTES, 'UTF-8', false)
);
return new static($errorMessage, 5, 'invalid_scope', 400, $hint, $redirectUri);
}
@@ -143,7 +146,7 @@ class OAuthServerException extends \Exception
/**
* Invalid refresh token.
*
* @param string|null $hint
* @param null|string $hint
*
* @return static
*/
@@ -155,8 +158,8 @@ class OAuthServerException extends \Exception
/**
* Access denied.
*
* @param string|null $hint
* @param string|null $redirectUri
* @param null|string $hint
* @param null|string $redirectUri
*
* @return static
*/
@@ -203,11 +206,10 @@ class OAuthServerException extends \Exception
/**
* Generate a HTTP response.
*
* @param \Psr\Http\Message\ResponseInterface $response
* @param bool $useFragment True if errors should be in the URI fragment instead of
* query string
* @param ResponseInterface $response
* @param bool $useFragment True if errors should be in the URI fragment instead of query string
*
* @return \Psr\Http\Message\ResponseInterface
* @return ResponseInterface
*/
public function generateHttpResponse(ResponseInterface $response, $useFragment = false)
{
@@ -268,7 +270,7 @@ class OAuthServerException extends \Exception
) {
$authScheme = 'Bearer';
}
$headers[] = 'WWW-Authenticate: ' . $authScheme . ' realm="OAuth"';
$headers['WWW-Authenticate'] = $authScheme . ' realm="OAuth"';
}
// @codeCoverageIgnoreEnd
return $headers;

View File

@@ -9,7 +9,6 @@
namespace League\OAuth2\Server\Exception;
class UniqueTokenIdentifierConstraintViolationException extends OAuthServerException
{
public static function create()

View File

@@ -13,7 +13,9 @@ namespace League\OAuth2\Server\Grant;
use League\Event\EmitterAwareTrait;
use League\OAuth2\Server\CryptTrait;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
@@ -54,17 +56,17 @@ abstract class AbstractGrant implements GrantTypeInterface
protected $scopeRepository;
/**
* @var \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface
* @var AuthCodeRepositoryInterface
*/
protected $authCodeRepository;
/**
* @var \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface
* @var RefreshTokenRepositoryInterface
*/
protected $refreshTokenRepository;
/**
* @var \League\OAuth2\Server\Repositories\UserRepositoryInterface
* @var UserRepositoryInterface
*/
protected $userRepository;
@@ -98,7 +100,7 @@ abstract class AbstractGrant implements GrantTypeInterface
}
/**
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
*/
public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
{
@@ -106,7 +108,7 @@ abstract class AbstractGrant implements GrantTypeInterface
}
/**
* @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository
* @param AuthCodeRepositoryInterface $authCodeRepository
*/
public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository)
{
@@ -114,7 +116,7 @@ abstract class AbstractGrant implements GrantTypeInterface
}
/**
* @param \League\OAuth2\Server\Repositories\UserRepositoryInterface $userRepository
* @param UserRepositoryInterface $userRepository
*/
public function setUserRepository(UserRepositoryInterface $userRepository)
{
@@ -132,11 +134,11 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Validate the client.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param ServerRequestInterface $request
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return \League\OAuth2\Server\Entities\ClientEntityInterface
* @return ClientEntityInterface
*/
protected function validateClient(ServerRequestInterface $request)
{
@@ -157,7 +159,7 @@ abstract class AbstractGrant implements GrantTypeInterface
true
);
if (!$client instanceof ClientEntityInterface) {
if ($client instanceof ClientEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient();
}
@@ -189,9 +191,9 @@ abstract class AbstractGrant implements GrantTypeInterface
* @param string $scopes
* @param string $redirectUri
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
* @return ScopeEntityInterface[]
*/
public function validateScopes(
$scopes,
@@ -208,7 +210,7 @@ abstract class AbstractGrant implements GrantTypeInterface
foreach ($scopesList as $scopeItem) {
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
if (!$scope instanceof ScopeEntityInterface) {
if ($scope instanceof ScopeEntityInterface === false) {
throw OAuthServerException::invalidScope($scopeItem, $redirectUri);
}
@@ -221,9 +223,9 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Retrieve request parameter.
*
* @param string $parameter
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param mixed $default
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
@@ -241,7 +243,8 @@ abstract class AbstractGrant implements GrantTypeInterface
* not exist, or is otherwise an invalid HTTP Basic header, return
* [null, null].
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param ServerRequestInterface $request
*
* @return string[]|null[]
*/
protected function getBasicAuthCredentials(ServerRequestInterface $request)
@@ -269,9 +272,9 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Retrieve query string parameter.
*
* @param string $parameter
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param mixed $default
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
@@ -283,9 +286,9 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Retrieve cookie parameter.
*
* @param string $parameter
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param mixed $default
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
@@ -297,9 +300,9 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Retrieve server parameter.
*
* @param string $parameter
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param mixed $default
* @param string $parameter
* @param ServerRequestInterface $request
* @param mixed $default
*
* @return null|string
*/
@@ -311,12 +314,15 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Issue an access token.
*
* @param \DateInterval $accessTokenTTL
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
* @param string $userIdentifier
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
* @param \DateInterval $accessTokenTTL
* @param ClientEntityInterface $client
* @param string $userIdentifier
* @param ScopeEntityInterface[] $scopes
*
* @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
*
* @return AccessTokenEntityInterface
*/
protected function issueAccessToken(
\DateInterval $accessTokenTTL,
@@ -339,6 +345,7 @@ abstract class AbstractGrant implements GrantTypeInterface
$accessToken->setIdentifier($this->generateUniqueIdentifier());
try {
$this->accessTokenRepository->persistNewAccessToken($accessToken);
return $accessToken;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
@@ -351,13 +358,16 @@ abstract class AbstractGrant implements GrantTypeInterface
/**
* Issue an auth code.
*
* @param \DateInterval $authCodeTTL
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
* @param string $userIdentifier
* @param string $redirectUri
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
* @param \DateInterval $authCodeTTL
* @param ClientEntityInterface $client
* @param string $userIdentifier
* @param string $redirectUri
* @param ScopeEntityInterface[] $scopes
*
* @return \League\OAuth2\Server\Entities\AuthCodeEntityInterface
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
*
* @return AuthCodeEntityInterface
*/
protected function issueAuthCode(
\DateInterval $authCodeTTL,
@@ -382,6 +392,7 @@ abstract class AbstractGrant implements GrantTypeInterface
$authCode->setIdentifier($this->generateUniqueIdentifier());
try {
$this->authCodeRepository->persistNewAuthCode($authCode);
return $authCode;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
@@ -392,9 +403,12 @@ abstract class AbstractGrant implements GrantTypeInterface
}
/**
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
* @param AccessTokenEntityInterface $accessToken
*
* @return \League\OAuth2\Server\Entities\RefreshTokenEntityInterface
* @throws OAuthServerException
* @throws UniqueTokenIdentifierConstraintViolationException
*
* @return RefreshTokenEntityInterface
*/
protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
{
@@ -408,6 +422,7 @@ abstract class AbstractGrant implements GrantTypeInterface
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
try {
$this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
return $refreshToken;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
@@ -422,7 +437,7 @@ abstract class AbstractGrant implements GrantTypeInterface
*
* @param int $length
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return string
*/

View File

@@ -9,7 +9,6 @@
namespace League\OAuth2\Server\Grant;
use DateInterval;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
@@ -35,9 +34,9 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
private $enableCodeExchangeProof = false;
/**
* @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
* @param \DateInterval $authCodeTTL
* @param AuthCodeRepositoryInterface $authCodeRepository
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
* @param \DateInterval $authCodeTTL
*/
public function __construct(
AuthCodeRepositoryInterface $authCodeRepository,
@@ -58,18 +57,18 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
/**
* Respond to an access token request.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
* @param ServerRequestInterface $request
* @param ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
* @return ResponseTypeInterface
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
\DateInterval $accessTokenTTL
) {
// Validate request
$client = $this->validateClient($request);
@@ -108,7 +107,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
foreach ($authCodePayload->scopes as $scopeId) {
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId);
if (!$scope instanceof ScopeEntityInterface) {
if ($scope instanceof ScopeEntityInterface === false) {
// @codeCoverageIgnoreStart
throw OAuthServerException::invalidScope($scopeId);
// @codeCoverageIgnoreEnd
@@ -265,6 +264,13 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
throw OAuthServerException::invalidRequest('code_challenge');
}
if (preg_match('/^[A-Za-z0-9-._~]{43,128}$/', $codeChallenge) !== 1) {
throw OAuthServerException::invalidRequest(
'code_challenge',
'The code_challenge must be between 43 and 128 characters'
);
}
$codeChallengeMethod = $this->getQueryStringParameter('code_challenge_method', $request, 'plain');
if (in_array($codeChallengeMethod, ['plain', 'S256']) === false) {
throw OAuthServerException::invalidRequest(
@@ -305,6 +311,31 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
$authorizationRequest->getScopes()
);
$payload = [
'client_id' => $authCode->getClient()->getIdentifier(),
'redirect_uri' => $authCode->getRedirectUri(),
'auth_code_id' => $authCode->getIdentifier(),
'scopes' => $authCode->getScopes(),
'user_id' => $authCode->getUserIdentifier(),
'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'),
'code_challenge' => $authorizationRequest->getCodeChallenge(),
'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(),
];
if ($this->encryptionKey === null) {
// Add padding to vary the length of the payload
$payload['_padding'] = base64_encode(random_bytes(mt_rand(8, 256)));
// Shuffle the payload so that the structure is no longer know and obvious
$keys = array_keys($payload);
shuffle($keys);
$shuffledPayload = [];
foreach ($keys as $key) {
$shuffledPayload[$key] = $payload[$key];
}
} else {
$shuffledPayload = $payload;
}
$response = new RedirectResponse();
$response->setRedirectUri(
$this->makeRedirectUri(
@@ -312,16 +343,7 @@ class AuthCodeGrant extends AbstractAuthorizeGrant
[
'code' => $this->encrypt(
json_encode(
[
'client_id' => $authCode->getClient()->getIdentifier(),
'redirect_uri' => $authCode->getRedirectUri(),
'auth_code_id' => $authCode->getIdentifier(),
'scopes' => $authCode->getScopes(),
'user_id' => $authCode->getUserIdentifier(),
'expire_time' => (new \DateTime())->add($this->authCodeTTL)->format('U'),
'code_challenge' => $authorizationRequest->getCodeChallenge(),
'code_challenge_method ' => $authorizationRequest->getCodeChallengeMethod(),
]
$shuffledPayload
)
),
'state' => $authorizationRequest->getState(),

View File

@@ -8,6 +8,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Grant;
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;

View File

@@ -8,6 +8,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Grant;
use League\Event\EmitterAwareInterface;
@@ -41,11 +42,11 @@ interface GrantTypeInterface extends EmitterAwareInterface
/**
* Respond to an incoming request.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
* @param ServerRequestInterface $request
* @param ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
*
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
* @return ResponseTypeInterface
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
@@ -56,7 +57,7 @@ interface GrantTypeInterface extends EmitterAwareInterface
/**
* The grant type should return true if it is able to response to an authorization request
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param ServerRequestInterface $request
*
* @return bool
*/
@@ -69,7 +70,7 @@ interface GrantTypeInterface extends EmitterAwareInterface
* If the validation is successful an AuthorizationRequest object will be returned. This object can be safely
* serialized in a user's session, and can be used during user authentication and authorization.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param ServerRequestInterface $request
*
* @return AuthorizationRequest
*/
@@ -80,9 +81,9 @@ interface GrantTypeInterface extends EmitterAwareInterface
* The AuthorizationRequest object's $userId property must be set to the authenticated user and the
* $authorizationApproved property must reflect their desire to authorize or deny the client.
*
* @param \League\OAuth2\Server\RequestTypes\AuthorizationRequest $authorizationRequest
* @param AuthorizationRequest $authorizationRequest
*
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
* @return ResponseTypeInterface
*/
public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest);
@@ -91,7 +92,7 @@ interface GrantTypeInterface extends EmitterAwareInterface
*
* For example most grant types will check that the $_POST['grant_type'] property matches it's identifier property.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param ServerRequestInterface $request
*
* @return bool
*/
@@ -100,35 +101,42 @@ interface GrantTypeInterface extends EmitterAwareInterface
/**
* Set the client repository.
*
* @param \League\OAuth2\Server\Repositories\ClientRepositoryInterface $clientRepository
* @param ClientRepositoryInterface $clientRepository
*/
public function setClientRepository(ClientRepositoryInterface $clientRepository);
/**
* Set the access token repository.
*
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
* @param AccessTokenRepositoryInterface $accessTokenRepository
*/
public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository);
/**
* Set the scope repository.
*
* @param \League\OAuth2\Server\Repositories\ScopeRepositoryInterface $scopeRepository
* @param ScopeRepositoryInterface $scopeRepository
*/
public function setScopeRepository(ScopeRepositoryInterface $scopeRepository);
/**
* Set the path to the private key.
*
* @param \League\OAuth2\Server\CryptKey $privateKey
* @param CryptKey $privateKey
*/
public function setPrivateKey(CryptKey $privateKey);
/**
* Set the path to the public key.
*
* @param \League\OAuth2\Server\CryptKey $publicKey
* @param CryptKey $publicKey
*/
public function setPublicKey(CryptKey $publicKey);
/**
* Set the encryption key
*
* @param string|null $key
*/
public function setEncryptionKey($key = null);
}

View File

@@ -45,7 +45,7 @@ class ImplicitGrant extends AbstractAuthorizeGrant
}
/**
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
*
* @throw \LogicException
*/
@@ -75,11 +75,11 @@ class ImplicitGrant extends AbstractAuthorizeGrant
/**
* Respond to an incoming request.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
* @param ServerRequestInterface $request
* @param ResponseTypeInterface $responseType
* @param \DateInterval $accessTokenTTL
*
* @return \League\OAuth2\Server\ResponseTypes\ResponseTypeInterface
* @return ResponseTypeInterface
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
@@ -151,6 +151,13 @@ class ImplicitGrant extends AbstractAuthorizeGrant
: $client->getRedirectUri()
);
// Finalize the requested scopes
$scopes = $this->scopeRepository->finalizeScopes(
$scopes,
$this->getIdentifier(),
$client
);
$stateParameter = $this->getQueryStringParameter('state', $request);
$authorizationRequest = new AuthorizationRequest();

View File

@@ -8,6 +8,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Grant;
use League\OAuth2\Server\Entities\ClientEntityInterface;
@@ -25,8 +26,8 @@ use Psr\Http\Message\ServerRequestInterface;
class PasswordGrant extends AbstractGrant
{
/**
* @param \League\OAuth2\Server\Repositories\UserRepositoryInterface $userRepository
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
* @param UserRepositoryInterface $userRepository
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
*/
public function __construct(
UserRepositoryInterface $userRepository,
@@ -66,12 +67,12 @@ class PasswordGrant extends AbstractGrant
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $client
* @param ServerRequestInterface $request
* @param ClientEntityInterface $client
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return \League\OAuth2\Server\Entities\UserEntityInterface
* @return UserEntityInterface
*/
protected function validateUser(ServerRequestInterface $request, ClientEntityInterface $client)
{
@@ -91,7 +92,7 @@ class PasswordGrant extends AbstractGrant
$this->getIdentifier(),
$client
);
if (!$user instanceof UserEntityInterface) {
if ($user instanceof UserEntityInterface === false) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::USER_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidCredentials();

View File

@@ -8,6 +8,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Grant;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
@@ -23,7 +24,7 @@ use Psr\Http\Message\ServerRequestInterface;
class RefreshTokenGrant extends AbstractGrant
{
/**
* @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
* @param RefreshTokenRepositoryInterface $refreshTokenRepository
*/
public function __construct(RefreshTokenRepositoryInterface $refreshTokenRepository)
{
@@ -50,7 +51,7 @@ class RefreshTokenGrant extends AbstractGrant
$scopes = array_map(function ($scopeId) use ($client) {
$scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeId);
if (!$scope instanceof ScopeEntityInterface) {
if ($scope instanceof ScopeEntityInterface === false) {
// @codeCoverageIgnoreStart
throw OAuthServerException::invalidScope($scopeId);
// @codeCoverageIgnoreEnd
@@ -84,10 +85,10 @@ class RefreshTokenGrant extends AbstractGrant
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param string $clientId
* @param ServerRequestInterface $request
* @param string $clientId
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return array
*/
@@ -101,7 +102,7 @@ class RefreshTokenGrant extends AbstractGrant
// Validate refresh token
try {
$refreshToken = $this->decrypt($encryptedRefreshToken);
} catch (\LogicException $e) {
} catch (\Exception $e) {
throw OAuthServerException::invalidRefreshToken('Cannot decrypt the refresh token');
}

View File

@@ -17,14 +17,12 @@ use Psr\Http\Message\ServerRequestInterface;
class AuthorizationServerMiddleware
{
/**
* @var \League\OAuth2\Server\AuthorizationServer
* @var AuthorizationServer
*/
private $server;
/**
* AuthorizationServerMiddleware constructor.
*
* @param \League\OAuth2\Server\AuthorizationServer $server
* @param AuthorizationServer $server
*/
public function __construct(AuthorizationServer $server)
{
@@ -32,11 +30,11 @@ class AuthorizationServerMiddleware
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \Psr\Http\Message\ResponseInterface $response
* @param callable $next
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return \Psr\Http\Message\ResponseInterface
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{

View File

@@ -17,14 +17,12 @@ use Psr\Http\Message\ServerRequestInterface;
class ResourceServerMiddleware
{
/**
* @var \League\OAuth2\Server\ResourceServer
* @var ResourceServer
*/
private $server;
/**
* ResourceServerMiddleware constructor.
*
* @param \League\OAuth2\Server\ResourceServer $server
* @param ResourceServer $server
*/
public function __construct(ResourceServer $server)
{
@@ -32,9 +30,9 @@ class ResourceServerMiddleware
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \Psr\Http\Message\ResponseInterface $response
* @param callable $next
* @param ServerRequestInterface $request
* @param ResponseInterface $response
* @param callable $next
*
* @return \Psr\Http\Message\ResponseInterface
*/

View File

@@ -11,6 +11,7 @@ namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
/**
* Access token interface.
@@ -20,9 +21,9 @@ interface AccessTokenRepositoryInterface extends RepositoryInterface
/**
* Create a new access token
*
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
* @param mixed $userIdentifier
* @param ClientEntityInterface $clientEntity
* @param ScopeEntityInterface[] $scopes
* @param mixed $userIdentifier
*
* @return AccessTokenEntityInterface
*/
@@ -31,7 +32,7 @@ interface AccessTokenRepositoryInterface extends RepositoryInterface
/**
* Persists a new access token to permanent storage.
*
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessTokenEntity
* @param AccessTokenEntityInterface $accessTokenEntity
*/
public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity);

View File

@@ -6,6 +6,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\AuthCodeEntityInterface;
@@ -18,14 +19,14 @@ interface AuthCodeRepositoryInterface extends RepositoryInterface
/**
* Creates a new AuthCode
*
* @return \League\OAuth2\Server\Entities\AuthCodeEntityInterface
* @return AuthCodeEntityInterface
*/
public function getNewAuthCode();
/**
* Persists a new auth code to permanent storage.
*
* @param \League\OAuth2\Server\Entities\AuthCodeEntityInterface $authCodeEntity
* @param AuthCodeEntityInterface $authCodeEntity
*/
public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity);

View File

@@ -6,8 +6,11 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
/**
* Client storage interface.
*/
@@ -19,10 +22,10 @@ interface ClientRepositoryInterface extends RepositoryInterface
* @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
* @param bool $mustValidateSecret If true the client must attempt to validate the secret if the client
* is confidential
*
* @return \League\OAuth2\Server\Entities\ClientEntityInterface
* @return ClientEntityInterface
*/
public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true);
}

View File

@@ -6,6 +6,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
@@ -25,7 +26,7 @@ interface RefreshTokenRepositoryInterface extends RepositoryInterface
/**
* Create a new refresh token_name.
*
* @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshTokenEntity
* @param RefreshTokenEntityInterface $refreshTokenEntity
*/
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity);

View File

@@ -6,6 +6,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\Repositories;
/**

View File

@@ -22,7 +22,7 @@ interface ScopeRepositoryInterface extends RepositoryInterface
*
* @param string $identifier The scope identifier
*
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface
* @return ScopeEntityInterface
*/
public function getScopeEntityByIdentifier($identifier);
@@ -30,12 +30,12 @@ interface ScopeRepositoryInterface extends RepositoryInterface
* 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
* @param ScopeEntityInterface[] $scopes
* @param string $grantType
* @param ClientEntityInterface $clientEntity
* @param null|string $userIdentifier
*
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
* @return ScopeEntityInterface[]
*/
public function finalizeScopes(
array $scopes,

View File

@@ -10,18 +10,19 @@
namespace League\OAuth2\Server\Repositories;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
interface UserRepositoryInterface extends RepositoryInterface
{
/**
* Get a user entity.
*
* @param string $username
* @param string $password
* @param string $grantType The grant type used
* @param \League\OAuth2\Server\Entities\ClientEntityInterface $clientEntity
* @param string $username
* @param string $password
* @param string $grantType The grant type used
* @param ClientEntityInterface $clientEntity
*
* @return \League\OAuth2\Server\Entities\UserEntityInterface
* @return UserEntityInterface
*/
public function getUserEntityByUserCredentials(
$username,

View File

@@ -6,6 +6,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server;
use League\Event\Event;
@@ -18,15 +19,15 @@ class RequestEvent extends Event
const REFRESH_TOKEN_CLIENT_FAILED = 'refresh_token.client.failed';
/**
* @var \Psr\Http\Message\ServerRequestInterface
* @var ServerRequestInterface
*/
private $request;
/**
* RequestEvent constructor.
*
* @param string $name
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param string $name
* @param ServerRequestInterface $request
*/
public function __construct($name, ServerRequestInterface $request)
{

View File

@@ -66,12 +66,14 @@ class AuthorizationRequest
/**
* The code challenge (if provided)
*
* @var string
*/
protected $codeChallenge;
/**
* The code challenge method (if provided)
*
* @var string
*/
protected $codeChallengeMethod;
@@ -125,7 +127,7 @@ class AuthorizationRequest
}
/**
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
* @return ScopeEntityInterface[]
*/
public function getScopes()
{
@@ -133,9 +135,9 @@ class AuthorizationRequest
}
/**
* @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
* @param ScopeEntityInterface[] $scopes
*/
public function setScopes($scopes)
public function setScopes(array $scopes)
{
$this->scopes = $scopes;
}

View File

@@ -6,34 +6,38 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server;
use League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface;
use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use Psr\Http\Message\ServerRequestInterface;
class ResourceServer
{
/**
* @var \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface
* @var AccessTokenRepositoryInterface
*/
private $accessTokenRepository;
/**
* @var \League\OAuth2\Server\CryptKey|string
* @var CryptKey
*/
private $publicKey;
/**
* @var \League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface|null
* @var null|AuthorizationValidatorInterface
*/
private $authorizationValidator;
/**
* New server instance.
*
* @param \League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface $accessTokenRepository
* @param \League\OAuth2\Server\CryptKey|string $publicKey
* @param null|\League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface $authorizationValidator
* @param AccessTokenRepositoryInterface $accessTokenRepository
* @param CryptKey|string $publicKey
* @param null|AuthorizationValidatorInterface $authorizationValidator
*/
public function __construct(
AccessTokenRepositoryInterface $accessTokenRepository,
@@ -42,7 +46,7 @@ class ResourceServer
) {
$this->accessTokenRepository = $accessTokenRepository;
if (!$publicKey instanceof CryptKey) {
if ($publicKey instanceof CryptKey === false) {
$publicKey = new CryptKey($publicKey);
}
$this->publicKey = $publicKey;
@@ -51,11 +55,11 @@ class ResourceServer
}
/**
* @return \League\OAuth2\Server\AuthorizationValidators\AuthorizationValidatorInterface
* @return AuthorizationValidatorInterface
*/
protected function getAuthorizationValidator()
{
if (!$this->authorizationValidator instanceof AuthorizationValidatorInterface) {
if ($this->authorizationValidator instanceof AuthorizationValidatorInterface === false) {
$this->authorizationValidator = new BearerTokenValidator($this->accessTokenRepository);
}
@@ -67,11 +71,11 @@ class ResourceServer
/**
* Determine the access token validity.
*
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param ServerRequestInterface $request
*
* @throws \League\OAuth2\Server\Exception\OAuthServerException
* @throws OAuthServerException
*
* @return \Psr\Http\Message\ServerRequestInterface
* @return ServerRequestInterface
*/
public function validateAuthenticatedRequest(ServerRequestInterface $request)
{

View File

@@ -8,6 +8,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\ResponseTypes;
use League\OAuth2\Server\CryptTrait;
@@ -19,12 +20,12 @@ abstract class AbstractResponseType implements ResponseTypeInterface
use CryptTrait;
/**
* @var \League\OAuth2\Server\Entities\AccessTokenEntityInterface
* @var AccessTokenEntityInterface
*/
protected $accessToken;
/**
* @var \League\OAuth2\Server\Entities\RefreshTokenEntityInterface
* @var RefreshTokenEntityInterface
*/
protected $refreshToken;

View File

@@ -1,6 +1,6 @@
<?php
/**
* OAuth 2.0 Bearer Token Type.
* OAuth 2.0 Bearer Token Response.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
@@ -8,8 +8,10 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\ResponseTypes;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use Psr\Http\Message\ResponseInterface;
@@ -47,6 +49,8 @@ class BearerTokenResponse extends AbstractResponseType
$responseParams['refresh_token'] = $refreshToken;
}
$responseParams = array_merge($this->getExtraParams($this->accessToken), $responseParams);
$response = $response
->withStatus(200)
->withHeader('pragma', 'no-cache')
@@ -57,4 +61,18 @@ class BearerTokenResponse extends AbstractResponseType
return $response;
}
/**
* Add custom fields to your Bearer Token response here, then override
* AuthorizationServer::getResponseType() to pull in your version of
* this class rather than the default.
*
* @param AccessTokenEntityInterface $accessToken
*
* @return array
*/
protected function getExtraParams(AccessTokenEntityInterface $accessToken)
{
return [];
}
}

View File

@@ -1,4 +1,13 @@
<?php
/**
* OAuth 2.0 Redirect Response.
*
* @author Alex Bilbie <hello@alexbilbie.com>
* @copyright Copyright (c) Alex Bilbie
* @license http://mit-license.org/
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\ResponseTypes;

View File

@@ -8,6 +8,7 @@
*
* @link https://github.com/thephpleague/oauth2-server
*/
namespace League\OAuth2\Server\ResponseTypes;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
@@ -17,12 +18,12 @@ use Psr\Http\Message\ResponseInterface;
interface ResponseTypeInterface
{
/**
* @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
* @param AccessTokenEntityInterface $accessToken
*/
public function setAccessToken(AccessTokenEntityInterface $accessToken);
/**
* @param \League\OAuth2\Server\Entities\RefreshTokenEntityInterface $refreshToken
* @param RefreshTokenEntityInterface $refreshToken
*/
public function setRefreshToken(RefreshTokenEntityInterface $refreshToken);
@@ -32,4 +33,11 @@ interface ResponseTypeInterface
* @return ResponseInterface
*/
public function generateHttpResponse(ResponseInterface $response);
/**
* Set the encryption key
*
* @param string|null $key
*/
public function setEncryptionKey($key = null);
}

View File

@@ -29,13 +29,14 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
public function testRespondToRequestInvalidGrantType()
{
$server = new AuthorizationServer(
$this->getMock(ClientRepositoryInterface::class),
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
$this->getMockBuilder(ClientRepositoryInterface::class)->getMock(),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/private.key',
'file://' . __DIR__ . '/Stubs/public.key',
new StubResponseType()
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M'));
@@ -49,13 +50,13 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
public function testRespondToRequest()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepository->method('getClientEntity')->willReturn(new ClientEntity());
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessTokenRepositoryMock = $this->getMock(AccessTokenRepositoryInterface::class);
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$server = new AuthorizationServer(
@@ -66,6 +67,7 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
'file://' . __DIR__ . '/Stubs/public.key',
new StubResponseType()
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M'));
@@ -78,15 +80,16 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
public function testGetResponseType()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$server = new AuthorizationServer(
$clientRepository,
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/private.key',
'file://' . __DIR__ . '/Stubs/public.key'
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$abstractGrantReflection = new \ReflectionClass($server);
$method = $abstractGrantReflection->getMethod('getResponseType');
@@ -97,22 +100,23 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
public function testCompleteAuthorizationRequest()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$server = new AuthorizationServer(
$clientRepository,
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/private.key',
'file://' . __DIR__ . '/Stubs/public.key'
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$authCodeRepository = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
$authCodeRepository->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
$grant = new AuthCodeGrant(
$authCodeRepository,
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
@@ -139,19 +143,20 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
$server = new AuthorizationServer(
$clientRepositoryMock,
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/private.key',
'file://' . __DIR__ . '/Stubs/public.key'
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$server->enableGrantType($grant);
$request = new ServerRequest(
@@ -178,12 +183,13 @@ class AuthorizationServerTest extends \PHPUnit_Framework_TestCase
public function testValidateAuthorizationRequestUnregistered()
{
$server = new AuthorizationServer(
$this->getMock(ClientRepositoryInterface::class),
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
$this->getMockBuilder(ClientRepositoryInterface::class)->getMock(),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/private.key',
'file://' . __DIR__ . '/Stubs/public.key'
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$request = new ServerRequest(
[],

36
tests/CryptKeyTest.php Normal file
View File

@@ -0,0 +1,36 @@
<?php
namespace LeagueTests\Utils;
use League\OAuth2\Server\CryptKey;
class CryptKeyTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \LogicException
*/
public function testNoFile()
{
new CryptKey('undefined file');
}
public function testKeyCreation()
{
$keyFile = __DIR__ . '/Stubs/public.key';
$key = new CryptKey($keyFile, 'secret');
$this->assertEquals('file://' . $keyFile, $key->getKeyPath());
$this->assertEquals('secret', $key->getPassPhrase());
}
public function testKeyFileCreation()
{
$keyContent = file_get_contents(__DIR__ . '/Stubs/public.key');
$key = new CryptKey($keyContent);
$this->assertEquals(
'file://' . sys_get_temp_dir() . '/' . sha1($keyContent) . '.key',
$key->getKeyPath()
);
}
}

View File

@@ -326,7 +326,7 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
public function testIssueRefreshToken()
{
$refreshTokenRepoMock = $this->getMock(RefreshTokenRepositoryInterface::class);
$refreshTokenRepoMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$refreshTokenRepoMock
->expects($this->once())
->method('getNewRefreshToken')
@@ -350,7 +350,7 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
public function testIssueAccessToken()
{
$accessTokenRepoMock = $this->getMock(AccessTokenRepositoryInterface::class);
$accessTokenRepoMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessTokenRepoMock->method('getNewToken')->willReturn(new AccessTokenEntity());
/** @var AbstractGrant $grantMock */
@@ -374,7 +374,7 @@ class AbstractGrantTest extends \PHPUnit_Framework_TestCase
public function testIssueAuthCode()
{
$authCodeRepoMock = $this->getMock(AuthCodeRepositoryInterface::class);
$authCodeRepoMock = $this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock();
$authCodeRepoMock->expects($this->once())->method('getNewAuthCode')->willReturn(new AuthCodeEntity());
/** @var AbstractGrant $grantMock */

View File

@@ -40,8 +40,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
public function testGetIdentifier()
{
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
@@ -51,8 +51,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
public function testCanRespondToAuthorizationRequest()
{
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
@@ -81,8 +81,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -93,9 +93,9 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
null,
null,
'php://input',
$headers = [],
$cookies = [],
$queryParams = [
[],
[],
[
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
@@ -113,8 +113,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -125,9 +125,9 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
null,
null,
'php://input',
$headers = [],
$cookies = [],
$queryParams = [
[],
[],
[
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
@@ -137,7 +137,6 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest);
}
public function testValidateAuthorizationRequestCodeChallenge()
{
$client = new ClientEntity();
@@ -146,8 +145,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
@@ -159,19 +158,130 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
null,
null,
'php://input',
$headers = [],
$cookies = [],
$queryParams = [
[],
[],
[
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_challenge' => 'FOOBAR',
'code_challenge' => str_repeat('A', 43),
]
);
$this->assertTrue($grant->validateAuthorizationRequest($request) instanceof AuthorizationRequest);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
*/
public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooShort()
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$request = new ServerRequest(
[],
[],
null,
null,
'php://input',
[],
[],
[
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_challenge' => str_repeat('A', 42),
]
);
$grant->validateAuthorizationRequest($request);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
*/
public function testValidateAuthorizationRequestCodeChallengeInvalidLengthTooLong()
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$request = new ServerRequest(
[],
[],
null,
null,
'php://input',
[],
[],
[
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_challenge' => str_repeat('A', 129),
]
);
$grant->validateAuthorizationRequest($request);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
*/
public function testValidateAuthorizationRequestCodeChallengeInvalidCharacters()
{
$client = new ClientEntity();
$client->setRedirectUri('http://foo/bar');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
$grant->setClientRepository($clientRepositoryMock);
$request = new ServerRequest(
[],
[],
null,
null,
'php://input',
[],
[],
[
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
'code_challenge' => str_repeat('A', 42) . '!',
]
);
$grant->validateAuthorizationRequest($request);
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 3
@@ -181,8 +291,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -213,8 +323,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock->method('getClientEntity')->willReturn(null);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -248,8 +358,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -260,9 +370,9 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
null,
null,
'php://input',
$headers = [],
$cookies = [],
$queryParams = [
[],
[],
[
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://bar',
@@ -284,8 +394,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -296,9 +406,9 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
null,
null,
'php://input',
$headers = [],
$cookies = [],
$queryParams = [
[],
[],
[
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://bar',
@@ -320,8 +430,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
@@ -333,9 +443,9 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
null,
null,
'php://input',
$headers = [],
$cookies = [],
$queryParams = [
[],
[],
[
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
@@ -357,8 +467,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
@@ -370,9 +480,9 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
null,
null,
'php://input',
$headers = [],
$cookies = [],
$queryParams = [
[],
[],
[
'response_type' => 'code',
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
@@ -397,7 +507,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$grant = new AuthCodeGrant(
$authCodeRepository,
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
@@ -424,7 +534,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$grant = new AuthCodeGrant(
$authCodeRepository,
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
@@ -456,8 +566,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -524,8 +634,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
@@ -596,8 +706,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
@@ -652,20 +762,18 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
*/
public function testRespondToAccessTokenRequestMissingRedirectUri()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
$grant->setAccessTokenRepository($accessTokenRepositoryMock);
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$grant->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$request = new ServerRequest(
[],
@@ -677,11 +785,69 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
[],
[],
[
'client_id' => 'foo',
'grant_type' => 'authorization_code',
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
'auth_code_id' => uniqid(),
'expire_time' => time() + 3600,
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
]
)
),
]
);
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
}
/**
* @expectedException \League\OAuth2\Server\Exception\OAuthServerException
* @expectedExceptionCode 3
*/
public function testRespondToAccessTokenRequestRedirectUriMismatch()
{
$client = new ClientEntity();
$client->setIdentifier('foo');
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
$grant->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$request = new ServerRequest(
[],
[],
null,
'POST',
'php://input',
[],
[],
[],
[
'client_id' => 'foo',
'grant_type' => 'authorization_code',
'redirect_uri' => 'http://bar/foo',
'code' => $this->cryptStub->doEncrypt(
json_encode(
[
'auth_code_id' => uniqid(),
'expire_time' => time() + 3600,
'client_id' => 'foo',
'redirect_uri' => 'http://foo/bar',
]
)
),
]
);
/* @var StubResponseType $response */
$grant->respondToAccessTokenRequest($request, new StubResponseType(), new \DateInterval('PT10M'));
}
@@ -700,8 +866,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -746,8 +912,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -811,7 +977,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$grant = new AuthCodeGrant(
$authCodeRepositoryMock,
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -871,8 +1037,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -932,8 +1098,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willReturnSelf();
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -989,8 +1155,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
@@ -1039,7 +1205,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($e->getHint(), 'Failed to verify `code_verifier`.');
}
}
public function testRespondToAccessTokenRequestBadCodeVerifierS256()
{
$client = new ClientEntity();
@@ -1062,8 +1228,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
@@ -1135,8 +1301,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->method('getNewRefreshToken')->willReturn(new RefreshTokenEntity());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->enableCodeExchangeProof();
@@ -1201,7 +1367,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$grant = new AuthCodeGrant(
$authCodeRepository,
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
@@ -1229,7 +1395,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$grant = new AuthCodeGrant(
$authCodeRepository,
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
@@ -1257,7 +1423,7 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$grant = new AuthCodeGrant(
$authCodeRepository,
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
@@ -1290,8 +1456,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->expects($this->at(1))->method('persistNewRefreshToken');
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -1362,8 +1528,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willThrowException(OAuthServerException::serverError('something bad happened'));
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -1434,8 +1600,8 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$refreshTokenRepositoryMock->method('persistNewRefreshToken')->willThrowException(UniqueTokenIdentifierConstraintViolationException::create());
$grant = new AuthCodeGrant(
$this->getMock(AuthCodeRepositoryInterface::class),
$this->getMock(RefreshTokenRepositoryInterface::class),
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->setClientRepository($clientRepositoryMock);
@@ -1479,4 +1645,18 @@ class AuthCodeGrantTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($response->getAccessToken() instanceof AccessTokenEntityInterface);
$this->assertTrue($response->getRefreshToken() instanceof RefreshTokenEntityInterface);
}
/**
* @expectedException \LogicException
*/
public function testCompleteAuthorizationRequestNoUser()
{
$grant = new AuthCodeGrant(
$this->getMockBuilder(AuthCodeRepositoryInterface::class)->getMock(),
$this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock(),
new \DateInterval('PT10M')
);
$grant->completeAuthorizationRequest(new AuthorizationRequest());
}
}

View File

@@ -8,11 +8,14 @@ use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationExcep
use League\OAuth2\Server\Grant\ImplicitGrant;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use League\OAuth2\Server\ResponseTypes\RedirectResponse;
use LeagueTests\Stubs\AccessTokenEntity;
use LeagueTests\Stubs\ClientEntity;
use LeagueTests\Stubs\CryptTraitStub;
use LeagueTests\Stubs\ScopeEntity;
use LeagueTests\Stubs\StubResponseType;
use LeagueTests\Stubs\UserEntity;
use Zend\Diactoros\ServerRequest;
@@ -85,8 +88,14 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->setClientRepository($clientRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$request = new ServerRequest(
[],
@@ -113,8 +122,14 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase
$clientRepositoryMock = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepositoryMock->method('getClientEntity')->willReturn($client);
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeEntity = new ScopeEntity();
$scopeRepositoryMock->method('getScopeEntityByIdentifier')->willReturn($scopeEntity);
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->setClientRepository($clientRepositoryMock);
$grant->setScopeRepository($scopeRepositoryMock);
$request = new ServerRequest(
[],
@@ -369,4 +384,32 @@ class ImplicitGrantTest extends \PHPUnit_Framework_TestCase
$grant->completeAuthorizationRequest($authRequest);
}
/**
* @expectedException \LogicException
*/
public function testSetRefreshTokenTTL()
{
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->setRefreshTokenTTL(new \DateInterval('PT10M'));
}
/**
* @expectedException \LogicException
*/
public function testSetRefreshTokenRepository()
{
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$grant->setRefreshTokenRepository($refreshTokenRepositoryMock);
}
/**
* @expectedException \LogicException
*/
public function testCompleteAuthorizationRequestNoUser()
{
$grant = new ImplicitGrant(new \DateInterval('PT10M'));
$grant->completeAuthorizationRequest(new AuthorizationRequest());
}
}

View File

@@ -21,8 +21,8 @@ class PasswordGrantTest extends \PHPUnit_Framework_TestCase
{
public function testGetIdentifier()
{
$userRepositoryMock = $this->getMock(UserRepositoryInterface::class);
$refreshTokenRepositoryMock = $this->getMock(RefreshTokenRepositoryInterface::class);
$userRepositoryMock = $this->getMockBuilder(UserRepositoryInterface::class)->getMock();
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$grant = new PasswordGrant($userRepositoryMock, $refreshTokenRepositoryMock);
$this->assertEquals('password', $grant->getIdentifier());

View File

@@ -32,7 +32,7 @@ class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase
public function testGetIdentifier()
{
$refreshTokenRepositoryMock = $this->getMock(RefreshTokenRepositoryInterface::class);
$refreshTokenRepositoryMock = $this->getMockBuilder(RefreshTokenRepositoryInterface::class)->getMock();
$grant = new RefreshTokenGrant($refreshTokenRepositoryMock);
$this->assertEquals('refresh_token', $grant->getIdentifier());

View File

@@ -19,13 +19,13 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
{
public function testValidResponse()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepository->method('getClientEntity')->willReturn(new ClientEntity());
$scopeRepositoryMock = $this->getMockBuilder(ScopeRepositoryInterface::class)->getMock();
$scopeRepositoryMock->method('finalizeScopes')->willReturnArgument(0);
$accessRepositoryMock = $this->getMock(AccessTokenRepositoryInterface::class);
$accessRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$accessRepositoryMock->method('getNewToken')->willReturn(new AccessTokenEntity());
$server = new AuthorizationServer(
@@ -36,6 +36,7 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
'file://' . __DIR__ . '/../Stubs/public.key',
new StubResponseType()
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$server->enableGrantType(new ClientCredentialsGrant());
@@ -58,17 +59,18 @@ class AuthorizationServerMiddlewareTest extends \PHPUnit_Framework_TestCase
public function testOAuthErrorResponse()
{
$clientRepository = $this->getMock(ClientRepositoryInterface::class);
$clientRepository = $this->getMockBuilder(ClientRepositoryInterface::class)->getMock();
$clientRepository->method('getClientEntity')->willReturn(null);
$server = new AuthorizationServer(
$clientRepository,
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMock(ScopeRepositoryInterface::class),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
$this->getMockBuilder(ScopeRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/../Stubs/private.key',
'file://' . __DIR__ . '/../Stubs/public.key',
new StubResponseType()
);
$server->setEncryptionKey(base64_encode(random_bytes(36)));
$server->enableGrantType(new ClientCredentialsGrant(), new \DateInterval('PT1M'));

View File

@@ -16,7 +16,7 @@ class ResourceServerMiddlewareTest extends \PHPUnit_Framework_TestCase
public function testValidResponse()
{
$server = new ResourceServer(
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/../Stubs/public.key'
);
@@ -51,7 +51,7 @@ class ResourceServerMiddlewareTest extends \PHPUnit_Framework_TestCase
public function testValidResponseExpiredToken()
{
$server = new ResourceServer(
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/../Stubs/public.key'
);
@@ -86,7 +86,7 @@ class ResourceServerMiddlewareTest extends \PHPUnit_Framework_TestCase
public function testErrorResponse()
{
$server = new ResourceServer(
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/../Stubs/public.key'
);

View File

@@ -13,7 +13,7 @@ class ResourceServerTest extends \PHPUnit_Framework_TestCase
public function testValidateAuthenticatedRequest()
{
$server = new ResourceServer(
$this->getMock(AccessTokenRepositoryInterface::class),
$this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock(),
'file://' . __DIR__ . '/Stubs/public.key'
);

View File

@@ -61,6 +61,53 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase
$this->assertTrue(isset($json->refresh_token));
}
public function testGenerateHttpResponseWithExtraParams()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
$responseType = new BearerTokenResponseWithParams($accessTokenRepositoryMock);
$responseType->setPrivateKey(new CryptKey('file://' . __DIR__ . '/../Stubs/private.key'));
$responseType->setPublicKey(new CryptKey('file://' . __DIR__ . '/../Stubs/public.key'));
$client = new ClientEntity();
$client->setIdentifier('clientName');
$scope = new ScopeEntity();
$scope->setIdentifier('basic');
$accessToken = new AccessTokenEntity();
$accessToken->setIdentifier('abcdef');
$accessToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H')));
$accessToken->setClient($client);
$accessToken->addScope($scope);
$refreshToken = new RefreshTokenEntity();
$refreshToken->setIdentifier('abcdef');
$refreshToken->setAccessToken($accessToken);
$refreshToken->setExpiryDateTime((new \DateTime())->add(new \DateInterval('PT1H')));
$responseType->setAccessToken($accessToken);
$responseType->setRefreshToken($refreshToken);
$response = $responseType->generateHttpResponse(new Response());
$this->assertTrue($response instanceof ResponseInterface);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals('no-cache', $response->getHeader('pragma')[0]);
$this->assertEquals('no-store', $response->getHeader('cache-control')[0]);
$this->assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]);
$response->getBody()->rewind();
$json = json_decode($response->getBody()->getContents());
$this->assertEquals('Bearer', $json->token_type);
$this->assertTrue(isset($json->expires_in));
$this->assertTrue(isset($json->access_token));
$this->assertTrue(isset($json->refresh_token));
$this->assertTrue(isset($json->foo));
$this->assertEquals('bar', $json->foo);
}
public function testDetermineAccessTokenInHeaderValidToken()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();
@@ -226,7 +273,7 @@ class BearerResponseTypeTest extends \PHPUnit_Framework_TestCase
);
}
}
public function testDetermineMissingBearerInHeader()
{
$accessTokenRepositoryMock = $this->getMockBuilder(AccessTokenRepositoryInterface::class)->getMock();

View File

@@ -0,0 +1,14 @@
<?php
namespace LeagueTests\ResponseTypes;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
class BearerTokenResponseWithParams extends BearerTokenResponse
{
protected function getExtraParams(AccessTokenEntityInterface $accessToken)
{
return ['foo' => 'bar', 'token_type' => 'Should not overwrite'];
}
}