move oauth2 to vendor

This commit is contained in:
zotlabs 2017-03-14 09:09:05 +11:00
parent fc533107ed
commit 6c641b1834
132 changed files with 1324 additions and 3612 deletions

View File

@ -30,7 +30,8 @@
"ext-openssl" : "*", "ext-openssl" : "*",
"sabre/dav" : "~3.2", "sabre/dav" : "~3.2",
"michelf/php-markdown" : "^1.7", "michelf/php-markdown" : "^1.7",
"pixel418/markdownify": "^2.2" "pixel418/markdownify": "^2.2",
"bshaffer/oauth2-server-php": "~1.8"
}, },
"require-dev" : { "require-dev" : {
"php" : ">=5.6", "php" : ">=5.6",

2851
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
# Test Files #
test/config/test.sqlite
vendor
composer.lock
.idea

View File

@ -1,30 +0,0 @@
language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache
- vendor
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7
- hhvm
env:
global:
- SKIP_MONGO_TESTS=1
- secure: Bc5ZqvZ1YYpoPZNNuU2eCB8DS6vBYrAdfBtTenBs5NSxzb+Vjven4kWakbzaMvZjb/Ib7Uph7DGuOtJXpmxnvBXPLd707LZ89oFWN/yqQlZKCcm8iErvJCB5XL+/ONHj2iPdR242HJweMcat6bMCwbVWoNDidjtWMH0U2mYFy3M=
- secure: R3bXlymyFiY2k2jf7+fv/J8i34wtXTkmD4mCr5Ps/U+vn9axm2VtvR2Nj+r7LbRjn61gzFE/xIVjYft/wOyBOYwysrfriydrnRVS0owh6y+7EyOyQWbRX11vVQMf8o31QCQE5BY58V5AJZW3MjoOL0FVlTgySJiJvdw6Pv18v+E=
services:
- mongodb
- redis-server
- cassandra
before_install:
- phpenv config-rm xdebug.ini || return 0
install:
- composer install --no-interaction
before_script:
- psql -c 'create database oauth2_server_php;' -U postgres
after_script:
- php test/cleanup.php

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="test/bootstrap.php"
>
<testsuites>
<testsuite name="Oauth2 Test Suite">
<directory>./test/OAuth2/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src/OAuth2/</directory>
</whitelist>
</filter>
</phpunit>

2
vendor/autoload.php vendored
View File

@ -2,6 +2,6 @@
// autoload.php @generated by Composer // autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php'; require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit7b34d7e50a62201ec5d5e526a5b8b35d::getLoader(); return ComposerAutoloaderInit7b34d7e50a62201ec5d5e526a5b8b35d::getLoader();

View File

@ -8,6 +8,23 @@ To see the files changed for a given bug, go to https://github.com/bshaffer/oaut
To get the diff between two versions, go to https://github.com/bshaffer/oauth2-server-php/compare/v1.0...v1.1 To get the diff between two versions, go to https://github.com/bshaffer/oauth2-server-php/compare/v1.0...v1.1
To get the diff for a specific change, go to https://github.com/bshaffer/oauth2-server-php/commit/XXX where XXX is the change hash To get the diff for a specific change, go to https://github.com/bshaffer/oauth2-server-php/commit/XXX where XXX is the change hash
* 1.9.0 (2016-01-06)
PR: https://github.com/bshaffer/oauth2-server-php/pull/788
* bug #645 - Allow null for client_secret
* bug #651 - Fix bug in isPublicClient of Cassandra Storage
* bug #670 - Bug in client's scope restriction
* bug #672 - Implemented method to override the password hashing algorithm
* bug #698 - Fix Token Response's Content-Type to application/json
* bug #729 - Ensures unsetAccessToken and unsetRefreshToken return a bool
* bug #749 - Fix UserClaims for CodeIdToken
* bug #784 - RFC6750 compatibility
* bug #776 - Fix "redirect_uri_mismatch" for URIs with encoded characters
* bug #759 - no access token supplied to resource controller results in empty request body
* bug #773 - Use OpenSSL random method before attempting Mcrypt's.
* bug #790 - Add mongo db
* 1.8.0 (2015-09-18) * 1.8.0 (2015-09-18)
PR: https://github.com/bshaffer/oauth2-server-php/pull/643 PR: https://github.com/bshaffer/oauth2-server-php/pull/643
@ -93,6 +110,19 @@ To get the diff for a specific change, go to https://github.com/bshaffer/oauth2-
* bug #333 fixes Pdo storage for getClientKey * bug #333 fixes Pdo storage for getClientKey
* bug #336 fixes Redis storage for expireAuthorizationCode * bug #336 fixes Redis storage for expireAuthorizationCode
* 1.3 (2014-02-27)
PR: https://github.com/bshaffer/oauth2-server-php/pull/325
* bug #311 adds cassandra storage
* bug #298 fixes response code for user credentials grant type
* bug #318 adds 'use_crypto_tokens' config to Server class for better DX
* bug #320 pass client_id to getDefaultScope
* bug #324 better feedback when running tests
* bug #335 adds support for non-expiring refresh tokens
* bug #333 fixes Pdo storage for getClientKey
* bug #336 fixes Redis storage for expireAuthorizationCode
* 1.2 (2014-01-03) * 1.2 (2014-01-03)
PR: https://github.com/bshaffer/oauth2-server-php/pull/288 PR: https://github.com/bshaffer/oauth2-server-php/pull/288

View File

@ -0,0 +1,34 @@
{
"name": "bshaffer/oauth2-server-php",
"description":"OAuth2 Server for PHP",
"keywords":["oauth","oauth2","auth"],
"type":"library",
"license":"MIT",
"authors":[
{
"name":"Brent Shaffer",
"email": "bshafs@gmail.com",
"homepage":"http://brentertainment.com"
}
],
"homepage": "http://github.com/bshaffer/oauth2-server-php",
"autoload": {
"psr-0": { "OAuth2": "src/" }
},
"require":{
"php":">=5.3.9"
},
"require-dev": {
"aws/aws-sdk-php": "~2.8",
"firebase/php-jwt": "~2.2",
"predis/predis": "dev-master",
"thobbs/phpcassa": "dev-master",
"mongodb/mongodb": "^1.1"
},
"suggest": {
"predis/predis": "Required to use Redis storage",
"thobbs/phpcassa": "Required to use Cassandra storage",
"aws/aws-sdk-php": "~2.8 is required to use DynamoDB storage",
"firebase/php-jwt": "~1.1 is required to use MondoDB storage"
}
}

View File

@ -126,6 +126,11 @@ class AuthorizeController implements AuthorizeControllerInterface
return $params; return $params;
} }
/**
* @param RequestInterface $request
* @param ResponseInterface $response
* @return bool
*/
public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response) public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response)
{ {
// Make sure a valid client id was supplied (we can not redirect because we were unable to verify the URI) // Make sure a valid client id was supplied (we can not redirect because we were unable to verify the URI)
@ -333,7 +338,7 @@ class AuthorizeController implements AuthorizeControllerInterface
return false; // if either one is missing, assume INVALID return false; // if either one is missing, assume INVALID
} }
$registered_uris = explode(' ', $registeredUriString); $registered_uris = preg_split('/\s+/', $registeredUriString);
foreach ($registered_uris as $registered_uri) { foreach ($registered_uris as $registered_uri) {
if ($this->config['require_exact_redirect_uri']) { if ($this->config['require_exact_redirect_uri']) {
// the input uri is validated against the registered uri using exact match // the input uri is validated against the registered uri using exact match

View File

@ -83,7 +83,7 @@ class ResourceController implements ResourceControllerInterface
} elseif (!isset($token["expires"]) || !isset($token["client_id"])) { } elseif (!isset($token["expires"]) || !isset($token["client_id"])) {
$response->setError(401, 'malformed_token', 'Malformed token (missing "expires")'); $response->setError(401, 'malformed_token', 'Malformed token (missing "expires")');
} elseif (time() > $token["expires"]) { } elseif (time() > $token["expires"]) {
$response->setError(401, 'expired_token', 'The access token provided has expired'); $response->setError(401, 'invalid_token', 'The access token provided has expired');
} else { } else {
return $token; return $token;
} }

View File

@ -12,14 +12,33 @@ use OAuth2\RequestInterface;
use OAuth2\ResponseInterface; use OAuth2\ResponseInterface;
/** /**
* @see OAuth2\Controller\TokenControllerInterface * @see \OAuth2\Controller\TokenControllerInterface
*/ */
class TokenController implements TokenControllerInterface class TokenController implements TokenControllerInterface
{ {
/**
* @var AccessTokenInterface
*/
protected $accessToken; protected $accessToken;
/**
* @var array
*/
protected $grantTypes; protected $grantTypes;
/**
* @var ClientAssertionTypeInterface
*/
protected $clientAssertionType; protected $clientAssertionType;
/**
* @var Scope|ScopeInterface
*/
protected $scopeUtil; protected $scopeUtil;
/**
* @var ClientInterface
*/
protected $clientStorage; protected $clientStorage;
public function __construct(AccessTokenInterface $accessToken, ClientInterface $clientStorage, array $grantTypes = array(), ClientAssertionTypeInterface $clientAssertionType = null, ScopeInterface $scopeUtil = null) public function __construct(AccessTokenInterface $accessToken, ClientInterface $clientStorage, array $grantTypes = array(), ClientAssertionTypeInterface $clientAssertionType = null, ScopeInterface $scopeUtil = null)
@ -64,11 +83,11 @@ class TokenController implements TokenControllerInterface
* This would be called from the "/token" endpoint as defined in the spec. * This would be called from the "/token" endpoint as defined in the spec.
* You can call your endpoint whatever you want. * You can call your endpoint whatever you want.
* *
* @param $request - RequestInterface * @param RequestInterface $request Request object to grant access token
* Request object to grant access token * @param ResponseInterface $response
* *
* @throws InvalidArgumentException * @throws \InvalidArgumentException
* @throws LogicException * @throws \LogicException
* *
* @see http://tools.ietf.org/html/rfc6749#section-4 * @see http://tools.ietf.org/html/rfc6749#section-4
* @see http://tools.ietf.org/html/rfc6749#section-10.6 * @see http://tools.ietf.org/html/rfc6749#section-10.6
@ -208,10 +227,8 @@ class TokenController implements TokenControllerInterface
/** /**
* addGrantType * addGrantType
* *
* @param grantType - OAuth2\GrantTypeInterface * @param GrantTypeInterface $grantType the grant type to add for the specified identifier
* the grant type to add for the specified identifier * @param string $identifier a string passed in as "grant_type" in the response that will call this grantType
* @param identifier - string
* a string passed in as "grant_type" in the response that will call this grantType
*/ */
public function addGrantType(GrantTypeInterface $grantType, $identifier = null) public function addGrantType(GrantTypeInterface $grantType, $identifier = null)
{ {

View File

@ -17,7 +17,7 @@ class AuthorizationCode implements GrantTypeInterface
protected $authCode; protected $authCode;
/** /**
* @param OAuth2\Storage\AuthorizationCodeInterface $storage REQUIRED Storage class for retrieving authorization code information * @param \OAuth2\Storage\AuthorizationCodeInterface $storage REQUIRED Storage class for retrieving authorization code information
*/ */
public function __construct(AuthorizationCodeInterface $storage) public function __construct(AuthorizationCodeInterface $storage)
{ {
@ -49,7 +49,7 @@ class AuthorizationCode implements GrantTypeInterface
* @uri - http://tools.ietf.org/html/rfc6749#section-4.1.3 * @uri - http://tools.ietf.org/html/rfc6749#section-4.1.3
*/ */
if (isset($authCode['redirect_uri']) && $authCode['redirect_uri']) { if (isset($authCode['redirect_uri']) && $authCode['redirect_uri']) {
if (!$request->request('redirect_uri') || urldecode($request->request('redirect_uri')) != $authCode['redirect_uri']) { if (!$request->request('redirect_uri') || urldecode($request->request('redirect_uri')) != urldecode($authCode['redirect_uri'])) {
$response->setError(400, 'redirect_uri_mismatch', "The redirect URI is missing or do not match", "#section-4.1.3"); $response->setError(400, 'redirect_uri_mismatch', "The redirect URI is missing or do not match", "#section-4.1.3");
return false; return false;

View File

@ -176,7 +176,7 @@ class Response implements ResponseInterface
{ {
switch ($format) { switch ($format) {
case 'json': case 'json':
return json_encode($this->parameters); return $this->parameters ? json_encode($this->parameters) : '';
case 'xml': case 'xml':
// this only works for single-level arrays // this only works for single-level arrays
$xml = new \SimpleXMLElement('<response/>'); $xml = new \SimpleXMLElement('<response/>');

View File

@ -114,14 +114,14 @@ class AccessToken implements AccessTokenInterface
*/ */
protected function generateAccessToken() protected function generateAccessToken()
{ {
if (function_exists('mcrypt_create_iv')) { if (function_exists('openssl_random_pseudo_bytes')) {
$randomData = mcrypt_create_iv(20, MCRYPT_DEV_URANDOM); $randomData = openssl_random_pseudo_bytes(20);
if ($randomData !== false && strlen($randomData) === 20) { if ($randomData !== false && strlen($randomData) === 20) {
return bin2hex($randomData); return bin2hex($randomData);
} }
} }
if (function_exists('openssl_random_pseudo_bytes')) { if (function_exists('mcrypt_create_iv')) {
$randomData = openssl_random_pseudo_bytes(20); $randomData = mcrypt_create_iv(20, MCRYPT_DEV_URANDOM);
if ($randomData !== false && strlen($randomData) === 20) { if ($randomData !== false && strlen($randomData) === 20) {
return bin2hex($randomData); return bin2hex($randomData);
} }

View File

@ -85,10 +85,10 @@ class AuthorizationCode implements AuthorizationCodeInterface
protected function generateAuthorizationCode() protected function generateAuthorizationCode()
{ {
$tokenLen = 40; $tokenLen = 40;
if (function_exists('mcrypt_create_iv')) { if (function_exists('openssl_random_pseudo_bytes')) {
$randomData = mcrypt_create_iv(100, MCRYPT_DEV_URANDOM);
} elseif (function_exists('openssl_random_pseudo_bytes')) {
$randomData = openssl_random_pseudo_bytes(100); $randomData = openssl_random_pseudo_bytes(100);
} elseif (function_exists('mcrypt_create_iv')) {
$randomData = mcrypt_create_iv(100, MCRYPT_DEV_URANDOM);
} elseif (@file_exists('/dev/urandom')) { // Get 100 bytes of random data } elseif (@file_exists('/dev/urandom')) { // Get 100 bytes of random data
$randomData = file_get_contents('/dev/urandom', false, null, 0, 100) . uniqid(mt_rand(), true); $randomData = file_get_contents('/dev/urandom', false, null, 0, 100) . uniqid(mt_rand(), true);
} else { } else {

View File

@ -47,20 +47,50 @@ class Server implements ResourceControllerInterface,
UserInfoControllerInterface UserInfoControllerInterface
{ {
// misc properties // misc properties
/**
* @var Response
*/
protected $response; protected $response;
/**
* @var array
*/
protected $config; protected $config;
/**
* @var array
*/
protected $storages; protected $storages;
// servers // servers
/**
* @var AuthorizeControllerInterface
*/
protected $authorizeController; protected $authorizeController;
/**
* @var TokenControllerInterface
*/
protected $tokenController; protected $tokenController;
/**
* @var ResourceControllerInterface
*/
protected $resourceController; protected $resourceController;
/**
* @var UserInfoControllerInterface
*/
protected $userInfoController; protected $userInfoController;
// config classes // config classes
protected $grantTypes; protected $grantTypes;
protected $responseTypes; protected $responseTypes;
protected $tokenType; protected $tokenType;
/**
* @var ScopeInterface
*/
protected $scopeUtil; protected $scopeUtil;
protected $clientAssertionType; protected $clientAssertionType;
@ -92,9 +122,9 @@ class Server implements ResourceControllerInterface,
* @param array $grantTypes An array of OAuth2\GrantType\GrantTypeInterface to use for granting access tokens * @param array $grantTypes An array of OAuth2\GrantType\GrantTypeInterface to use for granting access tokens
* @param array $responseTypes Response types to use. array keys should be "code" and and "token" for * @param array $responseTypes Response types to use. array keys should be "code" and and "token" for
* Access Token and Authorization Code response types * Access Token and Authorization Code response types
* @param OAuth2\TokenType\TokenTypeInterface $tokenType The token type object to use. Valid token types are "bearer" and "mac" * @param \OAuth2\TokenType\TokenTypeInterface $tokenType The token type object to use. Valid token types are "bearer" and "mac"
* @param OAuth2\ScopeInterface $scopeUtil The scope utility class to use to validate scope * @param \OAuth2\ScopeInterface $scopeUtil The scope utility class to use to validate scope
* @param OAuth2\ClientAssertionType\ClientAssertionTypeInterface $clientAssertionType The method in which to verify the client identity. Default is HttpBasic * @param \OAuth2\ClientAssertionType\ClientAssertionTypeInterface $clientAssertionType The method in which to verify the client identity. Default is HttpBasic
* *
* @ingroup oauth2_section_7 * @ingroup oauth2_section_7
*/ */
@ -180,6 +210,8 @@ class Server implements ResourceControllerInterface,
/** /**
* every getter deserves a setter * every getter deserves a setter
*
* @param AuthorizeControllerInterface $authorizeController
*/ */
public function setAuthorizeController(AuthorizeControllerInterface $authorizeController) public function setAuthorizeController(AuthorizeControllerInterface $authorizeController)
{ {
@ -188,6 +220,8 @@ class Server implements ResourceControllerInterface,
/** /**
* every getter deserves a setter * every getter deserves a setter
*
* @param TokenControllerInterface $tokenController
*/ */
public function setTokenController(TokenControllerInterface $tokenController) public function setTokenController(TokenControllerInterface $tokenController)
{ {
@ -196,6 +230,8 @@ class Server implements ResourceControllerInterface,
/** /**
* every getter deserves a setter * every getter deserves a setter
*
* @param ResourceControllerInterface $resourceController
*/ */
public function setResourceController(ResourceControllerInterface $resourceController) public function setResourceController(ResourceControllerInterface $resourceController)
{ {
@ -204,6 +240,8 @@ class Server implements ResourceControllerInterface,
/** /**
* every getter deserves a setter * every getter deserves a setter
*
* @param UserInfoControllerInterface $userInfoController
*/ */
public function setUserInfoController(UserInfoControllerInterface $userInfoController) public function setUserInfoController(UserInfoControllerInterface $userInfoController)
{ {
@ -214,14 +252,16 @@ class Server implements ResourceControllerInterface,
* Return claims about the authenticated end-user. * Return claims about the authenticated end-user.
* This would be called from the "/UserInfo" endpoint as defined in the spec. * This would be called from the "/UserInfo" endpoint as defined in the spec.
* *
* @param $request - OAuth2\RequestInterface * @param $request - \OAuth2\RequestInterface
* Request object to grant access token * Request object to grant access token
* *
* @param $response - OAuth2\ResponseInterface * @param $response - \OAuth2\ResponseInterface
* Response object containing error messages (failure) or user claims (success) * Response object containing error messages (failure) or user claims (success)
* *
* @throws InvalidArgumentException * @return ResponseInterface
* @throws LogicException *
* @throws \InvalidArgumentException
* @throws \LogicException
* *
* @see http://openid.net/specs/openid-connect-core-1_0.html#UserInfo * @see http://openid.net/specs/openid-connect-core-1_0.html#UserInfo
*/ */
@ -238,14 +278,16 @@ class Server implements ResourceControllerInterface,
* This would be called from the "/token" endpoint as defined in the spec. * This would be called from the "/token" endpoint as defined in the spec.
* Obviously, you can call your endpoint whatever you want. * Obviously, you can call your endpoint whatever you want.
* *
* @param $request - OAuth2\RequestInterface * @param $request - \OAuth2\RequestInterface
* Request object to grant access token * Request object to grant access token
* *
* @param $response - OAuth2\ResponseInterface * @param $response - \OAuth2\ResponseInterface
* Response object containing error messages (failure) or access token (success) * Response object containing error messages (failure) or access token (success)
* *
* @throws InvalidArgumentException * @return ResponseInterface
* @throws LogicException *
* @throws \InvalidArgumentException
* @throws \LogicException
* *
* @see http://tools.ietf.org/html/rfc6749#section-4 * @see http://tools.ietf.org/html/rfc6749#section-4
* @see http://tools.ietf.org/html/rfc6749#section-10.6 * @see http://tools.ietf.org/html/rfc6749#section-10.6
@ -306,11 +348,14 @@ class Server implements ResourceControllerInterface,
* list of space-delimited strings. * list of space-delimited strings.
* - state: (optional) An opaque value used by the client to maintain * - state: (optional) An opaque value used by the client to maintain
* state between the request and callback. * state between the request and callback.
* @param ResponseInterface $response
* @param $is_authorized * @param $is_authorized
* TRUE or FALSE depending on whether the user authorized the access. * TRUE or FALSE depending on whether the user authorized the access.
* @param $user_id * @param $user_id
* Identifier of user who authorized the client * Identifier of user who authorized the client
* *
* @return Response
*
* @see http://tools.ietf.org/html/rfc6749#section-4 * @see http://tools.ietf.org/html/rfc6749#section-4
* *
* @ingroup oauth2_section_4 * @ingroup oauth2_section_4
@ -464,6 +509,8 @@ class Server implements ResourceControllerInterface,
/** /**
* every getter deserves a setter * every getter deserves a setter
*
* @param ScopeInterface $scopeUtil
*/ */
public function setScopeUtil($scopeUtil) public function setScopeUtil($scopeUtil)
{ {
@ -473,7 +520,7 @@ class Server implements ResourceControllerInterface,
protected function createDefaultAuthorizeController() protected function createDefaultAuthorizeController()
{ {
if (!isset($this->storages['client'])) { if (!isset($this->storages['client'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\ClientInterface to use the authorize server"); throw new \LogicException('You must supply a storage object implementing \OAuth2\Storage\ClientInterface to use the authorize server');
} }
if (0 == count($this->responseTypes)) { if (0 == count($this->responseTypes)) {
$this->responseTypes = $this->getDefaultResponseTypes(); $this->responseTypes = $this->getDefaultResponseTypes();
@ -505,7 +552,7 @@ class Server implements ResourceControllerInterface,
foreach ($this->grantTypes as $grantType) { foreach ($this->grantTypes as $grantType) {
if (!$grantType instanceof ClientAssertionTypeInterface) { if (!$grantType instanceof ClientAssertionTypeInterface) {
if (!isset($this->storages['client_credentials'])) { if (!isset($this->storages['client_credentials'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\ClientCredentialsInterface to use the token server"); throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\ClientCredentialsInterface to use the token server');
} }
$config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_credentials_in_request_body allow_public_clients'))); $config = array_intersect_key($this->config, array_flip(explode(' ', 'allow_credentials_in_request_body allow_public_clients')));
$this->clientAssertionType = new HttpBasic($this->storages['client_credentials'], $config); $this->clientAssertionType = new HttpBasic($this->storages['client_credentials'], $config);
@ -515,7 +562,7 @@ class Server implements ResourceControllerInterface,
} }
if (!isset($this->storages['client'])) { if (!isset($this->storages['client'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\ClientInterface to use the token server"); throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\ClientInterface to use the token server');
} }
$accessTokenResponseType = $this->getAccessTokenResponseType(); $accessTokenResponseType = $this->getAccessTokenResponseType();
@ -531,7 +578,7 @@ class Server implements ResourceControllerInterface,
$this->storages['access_token'] = $this->createDefaultJwtAccessTokenStorage(); $this->storages['access_token'] = $this->createDefaultJwtAccessTokenStorage();
} }
} elseif (!isset($this->storages['access_token'])) { } elseif (!isset($this->storages['access_token'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use JwtAccessTokens to use the resource server"); throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use JwtAccessTokens to use the resource server');
} }
if (!$this->tokenType) { if (!$this->tokenType) {
@ -551,11 +598,11 @@ class Server implements ResourceControllerInterface,
$this->storages['access_token'] = $this->createDefaultJwtAccessTokenStorage(); $this->storages['access_token'] = $this->createDefaultJwtAccessTokenStorage();
} }
} elseif (!isset($this->storages['access_token'])) { } elseif (!isset($this->storages['access_token'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use JwtAccessTokens to use the UserInfo server"); throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use JwtAccessTokens to use the UserInfo server');
} }
if (!isset($this->storages['user_claims'])) { if (!isset($this->storages['user_claims'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\OpenID\Storage\UserClaimsInterface to use the UserInfo server"); throw new \LogicException('You must supply a storage object implementing OAuth2\OpenID\Storage\UserClaimsInterface to use the UserInfo server');
} }
if (!$this->tokenType) { if (!$this->tokenType) {
@ -593,7 +640,7 @@ class Server implements ResourceControllerInterface,
$config = array_intersect_key($this->config, array_flip(explode(' ', 'enforce_redirect auth_code_lifetime'))); $config = array_intersect_key($this->config, array_flip(explode(' ', 'enforce_redirect auth_code_lifetime')));
if ($this->config['use_openid_connect']) { if ($this->config['use_openid_connect']) {
if (!$this->storages['authorization_code'] instanceof OpenIDAuthorizationCodeInterface) { if (!$this->storages['authorization_code'] instanceof OpenIDAuthorizationCodeInterface) {
throw new \LogicException("Your authorization_code storage must implement OAuth2\OpenID\Storage\AuthorizationCodeInterface to work when 'use_openid_connect' is true"); throw new \LogicException('Your authorization_code storage must implement OAuth2\OpenID\Storage\AuthorizationCodeInterface to work when "use_openid_connect" is true');
} }
$responseTypes['code'] = new OpenIDAuthorizationCodeResponseType($this->storages['authorization_code'], $config); $responseTypes['code'] = new OpenIDAuthorizationCodeResponseType($this->storages['authorization_code'], $config);
$responseTypes['code id_token'] = new CodeIdToken($responseTypes['code'], $responseTypes['id_token']); $responseTypes['code id_token'] = new CodeIdToken($responseTypes['code'], $responseTypes['id_token']);
@ -603,7 +650,7 @@ class Server implements ResourceControllerInterface,
} }
if (count($responseTypes) == 0) { if (count($responseTypes) == 0) {
throw new \LogicException("You must supply an array of response_types in the constructor or implement a OAuth2\Storage\AuthorizationCodeInterface storage object or set 'allow_implicit' to true and implement a OAuth2\Storage\AccessTokenInterface storage object"); throw new \LogicException('You must supply an array of response_types in the constructor or implement a OAuth2\Storage\AuthorizationCodeInterface storage object or set "allow_implicit" to true and implement a OAuth2\Storage\AccessTokenInterface storage object');
} }
return $responseTypes; return $responseTypes;
@ -630,7 +677,7 @@ class Server implements ResourceControllerInterface,
if (isset($this->storages['authorization_code'])) { if (isset($this->storages['authorization_code'])) {
if ($this->config['use_openid_connect']) { if ($this->config['use_openid_connect']) {
if (!$this->storages['authorization_code'] instanceof OpenIDAuthorizationCodeInterface) { if (!$this->storages['authorization_code'] instanceof OpenIDAuthorizationCodeInterface) {
throw new \LogicException("Your authorization_code storage must implement OAuth2\OpenID\Storage\AuthorizationCodeInterface to work when 'use_openid_connect' is true"); throw new \LogicException('Your authorization_code storage must implement OAuth2\OpenID\Storage\AuthorizationCodeInterface to work when "use_openid_connect" is true');
} }
$grantTypes['authorization_code'] = new OpenIDAuthorizationCodeGrantType($this->storages['authorization_code']); $grantTypes['authorization_code'] = new OpenIDAuthorizationCodeGrantType($this->storages['authorization_code']);
} else { } else {
@ -639,7 +686,7 @@ class Server implements ResourceControllerInterface,
} }
if (count($grantTypes) == 0) { if (count($grantTypes) == 0) {
throw new \LogicException("Unable to build default grant types - You must supply an array of grant_types in the constructor"); throw new \LogicException('Unable to build default grant types - You must supply an array of grant_types in the constructor');
} }
return $grantTypes; return $grantTypes;
@ -682,7 +729,7 @@ class Server implements ResourceControllerInterface,
protected function createDefaultJwtAccessTokenStorage() protected function createDefaultJwtAccessTokenStorage()
{ {
if (!isset($this->storages['public_key'])) { if (!isset($this->storages['public_key'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens"); throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens');
} }
$tokenStorage = null; $tokenStorage = null;
if (!empty($this->config['store_encrypted_token_string']) && isset($this->storages['access_token'])) { if (!empty($this->config['store_encrypted_token_string']) && isset($this->storages['access_token'])) {
@ -698,7 +745,7 @@ class Server implements ResourceControllerInterface,
protected function createDefaultJwtAccessTokenResponseType() protected function createDefaultJwtAccessTokenResponseType()
{ {
if (!isset($this->storages['public_key'])) { if (!isset($this->storages['public_key'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens"); throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens');
} }
$tokenStorage = null; $tokenStorage = null;
@ -719,7 +766,7 @@ class Server implements ResourceControllerInterface,
protected function createDefaultAccessTokenResponseType() protected function createDefaultAccessTokenResponseType()
{ {
if (!isset($this->storages['access_token'])) { if (!isset($this->storages['access_token'])) {
throw new \LogicException("You must supply a response type implementing OAuth2\ResponseType\AccessTokenInterface, or a storage object implementing OAuth2\Storage\AccessTokenInterface to use the token server"); throw new \LogicException('You must supply a response type implementing OAuth2\ResponseType\AccessTokenInterface, or a storage object implementing OAuth2\Storage\AccessTokenInterface to use the token server');
} }
$refreshStorage = null; $refreshStorage = null;
@ -736,10 +783,10 @@ class Server implements ResourceControllerInterface,
protected function createDefaultIdTokenResponseType() protected function createDefaultIdTokenResponseType()
{ {
if (!isset($this->storages['user_claims'])) { if (!isset($this->storages['user_claims'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\OpenID\Storage\UserClaimsInterface to use openid connect"); throw new \LogicException('You must supply a storage object implementing OAuth2\OpenID\Storage\UserClaimsInterface to use openid connect');
} }
if (!isset($this->storages['public_key'])) { if (!isset($this->storages['public_key'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use openid connect"); throw new \LogicException('You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use openid connect');
} }
$config = array_intersect_key($this->config, array_flip(explode(' ', 'issuer id_lifetime'))); $config = array_intersect_key($this->config, array_flip(explode(' ', 'issuer id_lifetime')));

View File

@ -71,7 +71,7 @@ interface AuthorizationCodeInterface
public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null); public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null);
/** /**
* once an Authorization Code is used, it must be exipired * once an Authorization Code is used, it must be expired
* *
* @see http://tools.ietf.org/html/rfc6749#section-4.1.2 * @see http://tools.ietf.org/html/rfc6749#section-4.1.2
* *

View File

@ -240,7 +240,7 @@ class Cassandra implements AuthorizationCodeInterface,
return false; return false;
} }
return empty($client['client_secret']);; return empty($client['client_secret']);
} }
/* ClientInterface */ /* ClientInterface */

View File

@ -22,6 +22,7 @@ class Mongo implements AuthorizationCodeInterface,
UserCredentialsInterface, UserCredentialsInterface,
RefreshTokenInterface, RefreshTokenInterface,
JwtBearerInterface, JwtBearerInterface,
PublicKeyInterface,
OpenIDAuthorizationCodeInterface OpenIDAuthorizationCodeInterface
{ {
protected $db; protected $db;
@ -46,6 +47,7 @@ class Mongo implements AuthorizationCodeInterface,
'refresh_token_table' => 'oauth_refresh_tokens', 'refresh_token_table' => 'oauth_refresh_tokens',
'code_table' => 'oauth_authorization_codes', 'code_table' => 'oauth_authorization_codes',
'user_table' => 'oauth_users', 'user_table' => 'oauth_users',
'key_table' => 'oauth_keys',
'jwt_table' => 'oauth_jwt', 'jwt_table' => 'oauth_jwt',
), $config); ), $config);
} }
@ -336,4 +338,55 @@ class Mongo implements AuthorizationCodeInterface,
//TODO: Needs mongodb implementation. //TODO: Needs mongodb implementation.
throw new \Exception('setJti() for the MongoDB driver is currently unimplemented.'); throw new \Exception('setJti() for the MongoDB driver is currently unimplemented.');
} }
public function getPublicKey($client_id = null)
{
if ($client_id) {
$result = $this->collection('key_table')->findOne(array(
'client_id' => $client_id
));
if ($result) {
return $result['public_key'];
}
}
$result = $this->collection('key_table')->findOne(array(
'client_id' => null
));
return is_null($result) ? false : $result['public_key'];
}
public function getPrivateKey($client_id = null)
{
if ($client_id) {
$result = $this->collection('key_table')->findOne(array(
'client_id' => $client_id
));
if ($result) {
return $result['private_key'];
}
}
$result = $this->collection('key_table')->findOne(array(
'client_id' => null
));
return is_null($result) ? false : $result['private_key'];
}
public function getEncryptionAlgorithm($client_id = null)
{
if ($client_id) {
$result = $this->collection('key_table')->findOne(array(
'client_id' => $client_id
));
if ($result) {
return $result['encryption_algorithm'];
}
}
$result = $this->collection('key_table')->findOne(array(
'client_id' => null
));
return is_null($result) ? 'RS256' : $result['encryption_algorithm'];
}
} }

View File

@ -0,0 +1,380 @@
<?php
namespace OAuth2\Storage;
use MongoDB\Client;
use MongoDB\Database;
use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface;
/**
* Simple MongoDB storage for all storage types
*
* NOTE: This class is meant to get users started
* quickly. If your application requires further
* customization, extend this class or create your own.
*
* NOTE: Passwords are stored in plaintext, which is never
* a good idea. Be sure to override this for your application
*
* @author Julien Chaumond <chaumond@gmail.com>
*/
class MongoDB implements AuthorizationCodeInterface,
UserCredentialsInterface,
AccessTokenInterface,
ClientCredentialsInterface,
RefreshTokenInterface,
JwtBearerInterface,
PublicKeyInterface,
OpenIDAuthorizationCodeInterface
{
protected $db;
protected $config;
public function __construct($connection, $config = array())
{
if ($connection instanceof Database) {
$this->db = $connection;
} else {
if (!is_array($connection)) {
throw new \InvalidArgumentException('First argument to OAuth2\Storage\Mongo must be an instance of MongoDB\Database or a configuration array');
}
$server = sprintf('mongodb://%s:%d', $connection['host'], $connection['port']);
$m = new Client($server);
$this->db = $m->selectDatabase($connection['database']);
}
$this->config = array_merge(array(
'client_table' => 'oauth_clients',
'access_token_table' => 'oauth_access_tokens',
'refresh_token_table' => 'oauth_refresh_tokens',
'code_table' => 'oauth_authorization_codes',
'user_table' => 'oauth_users',
'jwt_table' => 'oauth_jwt',
'jti_table' => 'oauth_jti',
'scope_table' => 'oauth_scopes',
'key_table' => 'oauth_keys',
), $config);
}
/* ClientCredentialsInterface */
public function checkClientCredentials($client_id, $client_secret = null)
{
if ($result = $this->collection('client_table')->findOne(array('client_id' => $client_id))) {
return $result['client_secret'] == $client_secret;
}
return false;
}
public function isPublicClient($client_id)
{
if (!$result = $this->collection('client_table')->findOne(array('client_id' => $client_id))) {
return false;
}
return empty($result['client_secret']);
}
/* ClientInterface */
public function getClientDetails($client_id)
{
$result = $this->collection('client_table')->findOne(array('client_id' => $client_id));
return is_null($result) ? false : $result;
}
public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null)
{
if ($this->getClientDetails($client_id)) {
$result = $this->collection('client_table')->updateOne(
array('client_id' => $client_id),
array('$set' => array(
'client_secret' => $client_secret,
'redirect_uri' => $redirect_uri,
'grant_types' => $grant_types,
'scope' => $scope,
'user_id' => $user_id,
))
);
return $result->getMatchedCount() > 0;
}
$client = array(
'client_id' => $client_id,
'client_secret' => $client_secret,
'redirect_uri' => $redirect_uri,
'grant_types' => $grant_types,
'scope' => $scope,
'user_id' => $user_id,
);
$result = $this->collection('client_table')->insertOne($client);
return $result->getInsertedCount() > 0;
}
public function checkRestrictedGrantType($client_id, $grant_type)
{
$details = $this->getClientDetails($client_id);
if (isset($details['grant_types'])) {
$grant_types = explode(' ', $details['grant_types']);
return in_array($grant_type, $grant_types);
}
// if grant_types are not defined, then none are restricted
return true;
}
/* AccessTokenInterface */
public function getAccessToken($access_token)
{
$token = $this->collection('access_token_table')->findOne(array('access_token' => $access_token));
return is_null($token) ? false : $token;
}
public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null)
{
// if it exists, update it.
if ($this->getAccessToken($access_token)) {
$result = $this->collection('access_token_table')->updateOne(
array('access_token' => $access_token),
array('$set' => array(
'client_id' => $client_id,
'expires' => $expires,
'user_id' => $user_id,
'scope' => $scope
))
);
return $result->getMatchedCount() > 0;
}
$token = array(
'access_token' => $access_token,
'client_id' => $client_id,
'expires' => $expires,
'user_id' => $user_id,
'scope' => $scope
);
$result = $this->collection('access_token_table')->insertOne($token);
return $result->getInsertedCount() > 0;
}
public function unsetAccessToken($access_token)
{
$result = $this->collection('access_token_table')->deleteOne(array(
'access_token' => $access_token
));
return $result->getDeletedCount() > 0;
}
/* AuthorizationCodeInterface */
public function getAuthorizationCode($code)
{
$code = $this->collection('code_table')->findOne(array(
'authorization_code' => $code
));
return is_null($code) ? false : $code;
}
public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null)
{
// if it exists, update it.
if ($this->getAuthorizationCode($code)) {
$result = $this->collection('code_table')->updateOne(
array('authorization_code' => $code),
array('$set' => array(
'client_id' => $client_id,
'user_id' => $user_id,
'redirect_uri' => $redirect_uri,
'expires' => $expires,
'scope' => $scope,
'id_token' => $id_token,
))
);
return $result->getMatchedCount() > 0;
}
$token = array(
'authorization_code' => $code,
'client_id' => $client_id,
'user_id' => $user_id,
'redirect_uri' => $redirect_uri,
'expires' => $expires,
'scope' => $scope,
'id_token' => $id_token,
);
$result = $this->collection('code_table')->insertOne($token);
return $result->getInsertedCount() > 0;
}
public function expireAuthorizationCode($code)
{
$result = $this->collection('code_table')->deleteOne(array(
'authorization_code' => $code
));
return $result->getDeletedCount() > 0;
}
/* UserCredentialsInterface */
public function checkUserCredentials($username, $password)
{
if ($user = $this->getUser($username)) {
return $this->checkPassword($user, $password);
}
return false;
}
public function getUserDetails($username)
{
if ($user = $this->getUser($username)) {
$user['user_id'] = $user['username'];
}
return $user;
}
/* RefreshTokenInterface */
public function getRefreshToken($refresh_token)
{
$token = $this->collection('refresh_token_table')->findOne(array(
'refresh_token' => $refresh_token
));
return is_null($token) ? false : $token;
}
public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null)
{
$token = array(
'refresh_token' => $refresh_token,
'client_id' => $client_id,
'user_id' => $user_id,
'expires' => $expires,
'scope' => $scope
);
$result = $this->collection('refresh_token_table')->insertOne($token);
return $result->getInsertedCount() > 0;
}
public function unsetRefreshToken($refresh_token)
{
$result = $this->collection('refresh_token_table')->deleteOne(array(
'refresh_token' => $refresh_token
));
return $result->getDeletedCount() > 0;
}
// plaintext passwords are bad! Override this for your application
protected function checkPassword($user, $password)
{
return $user['password'] == $password;
}
public function getUser($username)
{
$result = $this->collection('user_table')->findOne(array('username' => $username));
return is_null($result) ? false : $result;
}
public function setUser($username, $password, $firstName = null, $lastName = null)
{
if ($this->getUser($username)) {
$result = $this->collection('user_table')->updateOne(
array('username' => $username),
array('$set' => array(
'password' => $password,
'first_name' => $firstName,
'last_name' => $lastName
))
);
return $result->getMatchedCount() > 0;
}
$user = array(
'username' => $username,
'password' => $password,
'first_name' => $firstName,
'last_name' => $lastName
);
$result = $this->collection('user_table')->insertOne($user);
return $result->getInsertedCount() > 0;
}
public function getClientKey($client_id, $subject)
{
$result = $this->collection('jwt_table')->findOne(array(
'client_id' => $client_id,
'subject' => $subject
));
return is_null($result) ? false : $result['key'];
}
public function getClientScope($client_id)
{
if (!$clientDetails = $this->getClientDetails($client_id)) {
return false;
}
if (isset($clientDetails['scope'])) {
return $clientDetails['scope'];
}
return null;
}
public function getJti($client_id, $subject, $audience, $expires, $jti)
{
//TODO: Needs mongodb implementation.
throw new \Exception('getJti() for the MongoDB driver is currently unimplemented.');
}
public function setJti($client_id, $subject, $audience, $expires, $jti)
{
//TODO: Needs mongodb implementation.
throw new \Exception('setJti() for the MongoDB driver is currently unimplemented.');
}
public function getPublicKey($client_id = null)
{
if ($client_id) {
$result = $this->collection('key_table')->findOne(array(
'client_id' => $client_id
));
if ($result) {
return $result['public_key'];
}
}
$result = $this->collection('key_table')->findOne(array(
'client_id' => null
));
return is_null($result) ? false : $result['public_key'];
}
public function getPrivateKey($client_id = null)
{
if ($client_id) {
$result = $this->collection('key_table')->findOne(array(
'client_id' => $client_id
));
if ($result) {
return $result['private_key'];
}
}
$result = $this->collection('key_table')->findOne(array(
'client_id' => null
));
return is_null($result) ? false : $result['private_key'];
}
public function getEncryptionAlgorithm($client_id = null)
{
if ($client_id) {
$result = $this->collection('key_table')->findOne(array(
'client_id' => $client_id
));
if ($result) {
return $result['encryption_algorithm'];
}
}
$result = $this->collection('key_table')->findOne(array(
'client_id' => null
));
return is_null($result) ? 'RS256' : $result['encryption_algorithm'];
}
// Helper function to access a MongoDB collection by `type`:
protected function collection($name)
{
return $this->db->{$this->config[$name]};
}
}

View File

@ -74,7 +74,7 @@ interface RefreshTokenInterface
* and provide a descriptive fail message. * and provide a descriptive fail message.
* *
* @param $refresh_token * @param $refresh_token
* Refresh token to be expirse. * Refresh token to be expired.
* *
* @ingroup oauth2_section_6 * @ingroup oauth2_section_6
*/ */

View File

@ -20,6 +20,7 @@ class ResourceControllerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($response->getStatusCode(), 401); $this->assertEquals($response->getStatusCode(), 401);
$this->assertNull($response->getParameter('error')); $this->assertNull($response->getParameter('error'));
$this->assertNull($response->getParameter('error_description')); $this->assertNull($response->getParameter('error_description'));
$this->assertEquals('', $response->getResponseBody());
} }
public function testMalformedHeader() public function testMalformedHeader()
@ -100,7 +101,7 @@ class ResourceControllerTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($allow); $this->assertFalse($allow);
$this->assertEquals($response->getStatusCode(), 401); $this->assertEquals($response->getStatusCode(), 401);
$this->assertEquals($response->getParameter('error'), 'expired_token'); $this->assertEquals($response->getParameter('error'), 'invalid_token');
$this->assertEquals($response->getParameter('error_description'), 'The access token provided has expired'); $this->assertEquals($response->getParameter('error_description'), 'The access token provided has expired');
} }

View File

@ -93,6 +93,22 @@ class AuthorizationCodeTest extends \PHPUnit_Framework_TestCase
$this->assertArrayHasKey('access_token', $token); $this->assertArrayHasKey('access_token', $token);
} }
public function testValidRedirectUri()
{
$server = $this->getTestServer();
$request = TestRequest::createPost(array(
'grant_type' => 'authorization_code', // valid grant type
'client_id' => 'Test Client ID', // valid client id
'redirect_uri' => 'http://brentertainment.com/voil%C3%A0', // valid client id
'client_secret' => 'TestSecret', // valid client secret
'code' => 'testcode-redirect-uri', // valid code
));
$token = $server->grantAccessToken($request, new Response());
$this->assertNotNull($token);
$this->assertArrayHasKey('access_token', $token);
}
public function testValidCodeNoScope() public function testValidCodeNoScope()
{ {
$server = $this->getTestServer(); $server = $this->getTestServer();

Some files were not shown because too many files have changed in this diff Show More