summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--CHANGELOG.md28
-rw-r--r--README.md2
-rw-r--r--src/OAuth2/Controller/AuthorizeController.php21
-rw-r--r--src/OAuth2/Encryption/Jwt.php17
-rw-r--r--src/OAuth2/OpenID/Controller/AuthorizeController.php6
-rw-r--r--src/OAuth2/OpenID/Controller/AuthorizeControllerInterface.php2
-rw-r--r--src/OAuth2/OpenID/ResponseType/IdTokenToken.php (renamed from src/OAuth2/OpenID/ResponseType/TokenIdToken.php)2
-rw-r--r--src/OAuth2/OpenID/ResponseType/IdTokenTokenInterface.php (renamed from src/OAuth2/OpenID/ResponseType/TokenIdTokenInterface.php)2
-rw-r--r--src/OAuth2/ResponseType/AccessToken.php29
-rw-r--r--src/OAuth2/ResponseType/JwtAccessToken.php (renamed from src/OAuth2/ResponseType/CryptoToken.php)19
-rw-r--r--src/OAuth2/Server.php80
-rwxr-xr-xsrc/OAuth2/Storage/CouchbaseDB.php334
-rw-r--r--src/OAuth2/Storage/JwtAccessToken.php (renamed from src/OAuth2/Storage/CryptoToken.php)26
-rw-r--r--src/OAuth2/Storage/JwtAccessTokenInterface.php (renamed from src/OAuth2/Storage/CryptoTokenInterface.php)2
-rw-r--r--src/OAuth2/Storage/Mongo.php3
-rw-r--r--src/OAuth2/Storage/Pdo.php37
-rw-r--r--test/OAuth2/OpenID/Controller/AuthorizeControllerTest.php48
-rw-r--r--test/OAuth2/OpenID/ResponseType/IdTokenTokenTest.php (renamed from test/OAuth2/OpenID/ResponseType/TokenIdTokenTest.php)18
-rw-r--r--test/OAuth2/RequestTest.php10
-rw-r--r--test/OAuth2/ResponseType/JwtAccessTokenTest.php (renamed from test/OAuth2/ResponseType/CryptoTokenTest.php)62
-rw-r--r--test/OAuth2/ServerTest.php44
-rw-r--r--test/OAuth2/Storage/JwtAccessTokenTest.php (renamed from test/OAuth2/Storage/CryptoTokenTest.php)14
-rwxr-xr-x[-rw-r--r--]test/lib/OAuth2/Storage/BaseTest.php2
-rwxr-xr-x[-rw-r--r--]test/lib/OAuth2/Storage/Bootstrap.php77
25 files changed, 742 insertions, 146 deletions
diff --git a/.travis.yml b/.travis.yml
index 4046941..8f2ab49 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,9 +6,8 @@ php:
- 5.6
- hhvm
env:
- matrix:
- - SKIP_MONGO_TESTS=1
global:
+ - SKIP_MONGO_TESTS=1
- secure: Bc5ZqvZ1YYpoPZNNuU2eCB8DS6vBYrAdfBtTenBs5NSxzb+Vjven4kWakbzaMvZjb/Ib7Uph7DGuOtJXpmxnvBXPLd707LZ89oFWN/yqQlZKCcm8iErvJCB5XL+/ONHj2iPdR242HJweMcat6bMCwbVWoNDidjtWMH0U2mYFy3M=
- secure: R3bXlymyFiY2k2jf7+fv/J8i34wtXTkmD4mCr5Ps/U+vn9axm2VtvR2Nj+r7LbRjn61gzFE/xIVjYft/wOyBOYwysrfriydrnRVS0owh6y+7EyOyQWbRX11vVQMf8o31QCQE5BY58V5AJZW3MjoOL0FVlTgySJiJvdw6Pv18v+E=
services:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 00813f1..23ea53c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,34 @@ 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 for a specific change, go to https://github.com/bshaffer/oauth2-server-php/commit/XXX where XXX is the change hash
+* 1.6 (2014-01-16)
+
+ PR: https://github.com/bshaffer/oauth2-server-php/pull/496
+
+ * bug 437 - renames CryptoToken to JwtAccessToken / use_crypto_tokens to use_jwt_access_tokens
+ * bug 447 - Adds a Couchbase storage implementation
+ * bug 460 - Rename JWT claims to match spec
+ * bug 470 - order does not matter for multi-valued response types
+ * bug 471 - Make validateAuthorizeRequest available for POST in addition to GET
+ * bug 475 - Adds JTI table definitiion
+ * bug 481 - better randomness for generating access tokens
+ * bug 480 - Use hash_equals() for signature verification (prevents remote timing attacks)
+ * bugs 489, 491, 498 - misc other fixes
+
+* 1.5 (2014-08-27)
+
+ PR: https://github.com/bshaffer/oauth2-server-php/pull/446
+
+ * bug #399 - Add DynamoDB Support
+ * bug #404 - renamed error name for malformed/expired tokens
+ * bug #412 - Openid connect: fixes for claims with more than one scope / Add support for the prompt parameter ('consent' and 'none')
+ * bug #411 - fixes xml output
+ * bug #413 - fixes invalid format error
+ * bug #401 - fixes code standards / whitespace
+ * bug #354 - bundles PDO SQL with the library
+ * [BC] bug #397 - refresh tokens should not be encrypted
+ * bug #423 - makes "scope" optional for refresh token storage
+
* 1.4 (2014-06-12)
PR: https://github.com/bshaffer/oauth2-server-php/pull/392
diff --git a/README.md b/README.md
index febe177..4ceda6c 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
oauth2-server-php
=================
-[![Build Status](https://secure.travis-ci.org/bshaffer/oauth2-server-php.png)](http://travis-ci.org/bshaffer/oauth2-server-php)
+[![Build Status](https://travis-ci.org/bshaffer/oauth2-server-php.svg?branch=develop)](https://travis-ci.org/bshaffer/oauth2-server-php)
[![Total Downloads](https://poser.pugx.org/bshaffer/oauth2-server-php/downloads.png)](https://packagist.org/packages/bshaffer/oauth2-server-php)
diff --git a/src/OAuth2/Controller/AuthorizeController.php b/src/OAuth2/Controller/AuthorizeController.php
index 66d1cd6..7a3327b 100644
--- a/src/OAuth2/Controller/AuthorizeController.php
+++ b/src/OAuth2/Controller/AuthorizeController.php
@@ -129,7 +129,7 @@ class AuthorizeController implements AuthorizeControllerInterface
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)
- if (!$client_id = $request->query("client_id")) {
+ if (!$client_id = $request->query('client_id', $request->request('client_id'))) {
// We don't have a good URI to use
$response->setError(400, 'invalid_client', "No client id supplied");
@@ -149,7 +149,7 @@ class AuthorizeController implements AuthorizeControllerInterface
// @see http://tools.ietf.org/html/rfc6749#section-3.1.2
// @see http://tools.ietf.org/html/rfc6749#section-4.1.2.1
// @see http://tools.ietf.org/html/rfc6749#section-4.2.2.1
- if ($supplied_redirect_uri = $request->query('redirect_uri')) {
+ if ($supplied_redirect_uri = $request->query('redirect_uri', $request->request('redirect_uri'))) {
// validate there is no fragment supplied
$parts = parse_url($supplied_redirect_uri);
if (isset($parts['fragment']) && $parts['fragment']) {
@@ -182,8 +182,16 @@ class AuthorizeController implements AuthorizeControllerInterface
}
// Select the redirect URI
- $response_type = $request->query('response_type');
- $state = $request->query('state');
+ $response_type = $request->query('response_type', $request->request('response_type'));
+
+ // for multiple-valued response types - make them alphabetical
+ if (false !== strpos($response_type, ' ')) {
+ $types = explode(' ', $response_type);
+ sort($types);
+ $response_type = ltrim(implode(' ', $types));
+ }
+
+ $state = $request->query('state', $request->request('state'));
// type and client_id are required
if (!$response_type || !in_array($response_type, $this->getValidResponseTypes())) {
@@ -191,6 +199,7 @@ class AuthorizeController implements AuthorizeControllerInterface
return false;
}
+
if ($response_type == self::RESPONSE_TYPE_AUTHORIZATION_CODE) {
if (!isset($this->responseTypes['code'])) {
$response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unsupported_response_type', 'authorization code grant type not supported', null);
@@ -282,9 +291,9 @@ class AuthorizeController implements AuthorizeControllerInterface
// Add our params to the parsed uri
foreach ($params as $k => $v) {
if (isset($parse_url[$k])) {
- $parse_url[$k] .= "&" . http_build_query($v);
+ $parse_url[$k] .= "&" . http_build_query($v, '', '&');
} else {
- $parse_url[$k] = http_build_query($v);
+ $parse_url[$k] = http_build_query($v, '', '&');
}
}
diff --git a/src/OAuth2/Encryption/Jwt.php b/src/OAuth2/Encryption/Jwt.php
index 824ac13..181cead 100644
--- a/src/OAuth2/Encryption/Jwt.php
+++ b/src/OAuth2/Encryption/Jwt.php
@@ -69,7 +69,10 @@ class Jwt implements EncryptionInterface
case'HS256':
case'HS384':
case'HS512':
- return $this->sign($input, $key, $algo) === $signature;
+ return $this->hash_equals(
+ $this->sign($input, $key, $algo),
+ $signature
+ );
case 'RS256':
return openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA256') ? OPENSSL_ALGO_SHA256 : 'sha256') === 1;
@@ -149,4 +152,16 @@ class Jwt implements EncryptionInterface
'alg' => $algorithm,
);
}
+
+ protected function hash_equals($a, $b)
+ {
+ if (function_exists('hash_equals')) {
+ return hash_equals($a, $b);
+ }
+ $diff = strlen($a) ^ strlen($b);
+ for ($i = 0; $i < strlen($a) && $i < strlen($b); $i++) {
+ $diff |= ord($a[$i]) ^ ord($b[$i]);
+ }
+ return $diff === 0;
+ }
}
diff --git a/src/OAuth2/OpenID/Controller/AuthorizeController.php b/src/OAuth2/OpenID/Controller/AuthorizeController.php
index c6fe4f9..d130878 100644
--- a/src/OAuth2/OpenID/Controller/AuthorizeController.php
+++ b/src/OAuth2/OpenID/Controller/AuthorizeController.php
@@ -57,8 +57,8 @@ class AuthorizeController extends BaseAuthorizeController implements AuthorizeCo
$nonce = $request->query('nonce');
- // Validate required nonce for "id_token" and "token id_token"
- if (!$nonce && in_array($this->getResponseType(), array(self::RESPONSE_TYPE_ID_TOKEN, self::RESPONSE_TYPE_TOKEN_ID_TOKEN))) {
+ // Validate required nonce for "id_token" and "id_token token"
+ if (!$nonce && in_array($this->getResponseType(), array(self::RESPONSE_TYPE_ID_TOKEN, self::RESPONSE_TYPE_ID_TOKEN_TOKEN))) {
$response->setError(400, 'invalid_nonce', 'This application requires you specify a nonce parameter');
return false;
@@ -75,7 +75,7 @@ class AuthorizeController extends BaseAuthorizeController implements AuthorizeCo
self::RESPONSE_TYPE_ACCESS_TOKEN,
self::RESPONSE_TYPE_AUTHORIZATION_CODE,
self::RESPONSE_TYPE_ID_TOKEN,
- self::RESPONSE_TYPE_TOKEN_ID_TOKEN,
+ self::RESPONSE_TYPE_ID_TOKEN_TOKEN,
);
}
diff --git a/src/OAuth2/OpenID/Controller/AuthorizeControllerInterface.php b/src/OAuth2/OpenID/Controller/AuthorizeControllerInterface.php
index 62704e7..b8a26db 100644
--- a/src/OAuth2/OpenID/Controller/AuthorizeControllerInterface.php
+++ b/src/OAuth2/OpenID/Controller/AuthorizeControllerInterface.php
@@ -5,5 +5,5 @@ namespace OAuth2\OpenID\Controller;
interface AuthorizeControllerInterface
{
const RESPONSE_TYPE_ID_TOKEN = 'id_token';
- const RESPONSE_TYPE_TOKEN_ID_TOKEN = 'token id_token';
+ const RESPONSE_TYPE_ID_TOKEN_TOKEN = 'id_token token';
}
diff --git a/src/OAuth2/OpenID/ResponseType/TokenIdToken.php b/src/OAuth2/OpenID/ResponseType/IdTokenToken.php
index 5567569..498ab32 100644
--- a/src/OAuth2/OpenID/ResponseType/TokenIdToken.php
+++ b/src/OAuth2/OpenID/ResponseType/IdTokenToken.php
@@ -4,7 +4,7 @@ namespace OAuth2\OpenID\ResponseType;
use OAuth2\ResponseType\AccessTokenInterface;
-class TokenIdToken implements TokenIdTokenInterface
+class IdTokenToken implements IdTokenTokenInterface
{
protected $accessToken;
protected $idToken;
diff --git a/src/OAuth2/OpenID/ResponseType/TokenIdTokenInterface.php b/src/OAuth2/OpenID/ResponseType/IdTokenTokenInterface.php
index 1b4c513..ac13e20 100644
--- a/src/OAuth2/OpenID/ResponseType/TokenIdTokenInterface.php
+++ b/src/OAuth2/OpenID/ResponseType/IdTokenTokenInterface.php
@@ -4,6 +4,6 @@ namespace OAuth2\OpenID\ResponseType;
use OAuth2\ResponseType\ResponseTypeInterface;
-interface TokenIdTokenInterface extends ResponseTypeInterface
+interface IdTokenTokenInterface extends ResponseTypeInterface
{
}
diff --git a/src/OAuth2/ResponseType/AccessToken.php b/src/OAuth2/ResponseType/AccessToken.php
index b05bf87..98cf41c 100644
--- a/src/OAuth2/ResponseType/AccessToken.php
+++ b/src/OAuth2/ResponseType/AccessToken.php
@@ -114,18 +114,27 @@ class AccessToken implements AccessTokenInterface
*/
protected function generateAccessToken()
{
- $tokenLen = 40;
if (function_exists('mcrypt_create_iv')) {
- $randomData = mcrypt_create_iv(100, MCRYPT_DEV_URANDOM);
- } elseif (function_exists('openssl_random_pseudo_bytes')) {
- $randomData = openssl_random_pseudo_bytes(100);
- } 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);
- } else {
- $randomData = mt_rand() . mt_rand() . mt_rand() . mt_rand() . microtime(true) . uniqid(mt_rand(), true);
+ $randomData = mcrypt_create_iv(20, MCRYPT_DEV_URANDOM);
+ if ($randomData !== false && strlen($randomData) === 20) {
+ return bin2hex($randomData);
+ }
}
-
- return substr(hash('sha512', $randomData), 0, $tokenLen);
+ if (function_exists('openssl_random_pseudo_bytes')) {
+ $randomData = openssl_random_pseudo_bytes(20);
+ if ($randomData !== false && strlen($randomData) === 20) {
+ return bin2hex($randomData);
+ }
+ }
+ if (@file_exists('/dev/urandom')) { // Get 100 bytes of random data
+ $randomData = file_get_contents('/dev/urandom', false, null, 0, 20);
+ if ($randomData !== false && strlen($randomData) === 20) {
+ return bin2hex($randomData);
+ }
+ }
+ // Last resort which you probably should just get rid of:
+ $randomData = mt_rand() . mt_rand() . mt_rand() . mt_rand() . microtime(true) . uniqid(mt_rand(), true);
+ return substr(hash('sha512', $randomData), 0, 40);
}
/**
diff --git a/src/OAuth2/ResponseType/CryptoToken.php b/src/OAuth2/ResponseType/JwtAccessToken.php
index 08ff853..b918238 100644
--- a/src/OAuth2/ResponseType/CryptoToken.php
+++ b/src/OAuth2/ResponseType/JwtAccessToken.php
@@ -13,7 +13,7 @@ use OAuth2\Storage\Memory;
*
* @author Brent Shaffer <bshafs at gmail dot com>
*/
-class CryptoToken extends AccessToken
+class JwtAccessToken extends AccessToken
{
protected $publicKeyStorage;
protected $encryptionUtil;
@@ -29,6 +29,7 @@ class CryptoToken extends AccessToken
$this->publicKeyStorage = $publicKeyStorage;
$config = array_merge(array(
'store_encrypted_token_string' => true,
+ 'issuer' => ''
), $config);
if (is_null($tokenStorage)) {
// a pass-thru, so we can call the parent constructor
@@ -60,11 +61,13 @@ class CryptoToken extends AccessToken
{
// token to encrypt
$expires = time() + $this->config['access_lifetime'];
- $cryptoToken = array(
+ $jwtAccessToken = array(
'id' => $this->generateAccessToken(),
- 'client_id' => $client_id,
- 'user_id' => $user_id,
- 'expires' => $expires,
+ 'iss' => $this->config['issuer'],
+ 'aud' => $client_id,
+ 'sub' => $user_id,
+ 'exp' => $expires,
+ 'iat' => time(),
'token_type' => $this->config['token_type'],
'scope' => $scope
);
@@ -72,14 +75,14 @@ class CryptoToken extends AccessToken
/*
* Encode the token data into a single access_token string
*/
- $access_token = $this->encodeToken($cryptoToken, $client_id);
+ $access_token = $this->encodeToken($jwtAccessToken, $client_id);
/*
* Save the token to a secondary storage. This is implemented on the
- * OAuth2\Storage\CryptoToken side, and will not actually store anything,
+ * OAuth2\Storage\JwtAccessToken side, and will not actually store anything,
* if no secondary storage has been supplied
*/
- $token_to_store = $this->config['store_encrypted_token_string'] ? $access_token : $cryptoToken['id'];
+ $token_to_store = $this->config['store_encrypted_token_string'] ? $access_token : $jwtAccessToken['id'];
$this->tokenStorage->setAccessToken($token_to_store, $client_id, $user_id, $this->config['access_lifetime'] ? time() + $this->config['access_lifetime'] : null, $scope);
// token to return to the client
diff --git a/src/OAuth2/Server.php b/src/OAuth2/Server.php
index 3434a7b..0dd982d 100644
--- a/src/OAuth2/Server.php
+++ b/src/OAuth2/Server.php
@@ -19,9 +19,9 @@ use OAuth2\ClientAssertionType\HttpBasic;
use OAuth2\ResponseType\ResponseTypeInterface;
use OAuth2\ResponseType\AuthorizationCode as AuthorizationCodeResponseType;
use OAuth2\ResponseType\AccessToken;
-use OAuth2\ResponseType\CryptoToken;
+use OAuth2\ResponseType\JwtAccessToken;
use OAuth2\OpenID\ResponseType\IdToken;
-use OAuth2\OpenID\ResponseType\TokenIdToken;
+use OAuth2\OpenID\ResponseType\IdTokenToken;
use OAuth2\TokenType\TokenTypeInterface;
use OAuth2\TokenType\Bearer;
use OAuth2\GrantType\GrantTypeInterface;
@@ -29,8 +29,8 @@ use OAuth2\GrantType\UserCredentials;
use OAuth2\GrantType\ClientCredentials;
use OAuth2\GrantType\RefreshToken;
use OAuth2\GrantType\AuthorizationCode;
-use OAuth2\Storage\CryptoToken as CryptoTokenStorage;
-use OAuth2\Storage\CryptoTokenInterface;
+use OAuth2\Storage\JwtAccessToken as JwtAccessTokenStorage;
+use OAuth2\Storage\JwtAccessTokenInterface;
/**
* Server class for OAuth2
@@ -75,11 +75,12 @@ class Server implements ResourceControllerInterface,
'jwt_bearer' => 'OAuth2\Storage\JWTBearerInterface',
'scope' => 'OAuth2\Storage\ScopeInterface',
);
+
protected $responseTypeMap = array(
'token' => 'OAuth2\ResponseType\AccessTokenInterface',
'code' => 'OAuth2\ResponseType\AuthorizationCodeInterface',
'id_token' => 'OAuth2\OpenID\ResponseType\IdTokenInterface',
- 'token id_token' => 'OAuth2\OpenID\ResponseType\TokenIdTokenInterface',
+ 'id_token token' => 'OAuth2\OpenID\ResponseType\IdTokenTokenInterface',
);
/**
@@ -105,7 +106,7 @@ class Server implements ResourceControllerInterface,
// merge all config values. These get passed to our controller objects
$this->config = array_merge(array(
- 'use_crypto_tokens' => false,
+ 'use_jwt_access_tokens' => false,
'store_encrypted_token_string' => true,
'use_openid_connect' => false,
'id_lifetime' => 3600,
@@ -124,9 +125,11 @@ class Server implements ResourceControllerInterface,
foreach ($grantTypes as $key => $grantType) {
$this->addGrantType($grantType, $key);
}
+
foreach ($responseTypes as $key => $responseType) {
$this->addResponseType($responseType, $key);
}
+
$this->tokenType = $tokenType;
$this->scopeUtil = $scopeUtil;
$this->clientAssertionType = $clientAssertionType;
@@ -400,6 +403,8 @@ class Server implements ResourceControllerInterface,
public function addResponseType(ResponseTypeInterface $responseType, $key = null)
{
+ $key = $this->normalizeResponseType($key);
+
if (isset($this->responseTypeMap[$key])) {
if (!$responseType instanceof $this->responseTypeMap[$key]) {
throw new \InvalidArgumentException(sprintf('responseType of type "%s" must implement interface "%s"', $key, $this->responseTypeMap[$key]));
@@ -451,7 +456,7 @@ class Server implements ResourceControllerInterface,
if ($this->config['use_openid_connect'] && !isset($this->responseTypes['id_token'])) {
$this->responseTypes['id_token'] = $this->createDefaultIdTokenResponseType();
if ($this->config['allow_implicit']) {
- $this->responseTypes['token id_token'] = $this->createDefaultTokenIdTokenResponseType();
+ $this->responseTypes['id_token token'] = $this->createDefaultIdTokenTokenResponseType();
}
}
@@ -495,13 +500,13 @@ class Server implements ResourceControllerInterface,
protected function createDefaultResourceController()
{
- if ($this->config['use_crypto_tokens']) {
- // overwrites access token storage with crypto token storage if "use_crypto_tokens" is set
- if (!isset($this->storages['access_token']) || !$this->storages['access_token'] instanceof CryptoTokenInterface) {
- $this->storages['access_token'] = $this->createDefaultCryptoTokenStorage();
+ if ($this->config['use_jwt_access_tokens']) {
+ // overwrites access token storage with crypto token storage if "use_jwt_access_tokens" is set
+ if (!isset($this->storages['access_token']) || !$this->storages['access_token'] instanceof JwtAccessTokenInterface) {
+ $this->storages['access_token'] = $this->createDefaultJwtAccessTokenStorage();
}
} elseif (!isset($this->storages['access_token'])) {
- throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use CryptoTokens 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) {
@@ -515,13 +520,13 @@ class Server implements ResourceControllerInterface,
protected function createDefaultUserInfoController()
{
- if ($this->config['use_crypto_tokens']) {
- // overwrites access token storage with crypto token storage if "use_crypto_tokens" is set
- if (!isset($this->storages['access_token']) || !$this->storages['access_token'] instanceof CryptoTokenInterface) {
- $this->storages['access_token'] = $this->createDefaultCryptoTokenStorage();
+ if ($this->config['use_jwt_access_tokens']) {
+ // overwrites access token storage with crypto token storage if "use_jwt_access_tokens" is set
+ if (!isset($this->storages['access_token']) || !$this->storages['access_token'] instanceof JwtAccessTokenInterface) {
+ $this->storages['access_token'] = $this->createDefaultJwtAccessTokenStorage();
}
} elseif (!isset($this->storages['access_token'])) {
- throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\AccessTokenInterface or use CryptoTokens 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'])) {
@@ -555,7 +560,7 @@ class Server implements ResourceControllerInterface,
if ($this->config['use_openid_connect']) {
$responseTypes['id_token'] = $this->getIdTokenResponseType();
if ($this->config['allow_implicit']) {
- $responseTypes['token id_token'] = $this->getTokenIdTokenResponseType();
+ $responseTypes['id_token token'] = $this->getIdTokenTokenResponseType();
}
}
@@ -620,8 +625,8 @@ class Server implements ResourceControllerInterface,
return $this->responseTypes['token'];
}
- if ($this->config['use_crypto_tokens']) {
- return $this->createDefaultCryptoTokenResponseType();
+ if ($this->config['use_jwt_access_tokens']) {
+ return $this->createDefaultJwtAccessTokenResponseType();
}
return $this->createDefaultAccessTokenResponseType();
@@ -636,19 +641,19 @@ class Server implements ResourceControllerInterface,
return $this->createDefaultIdTokenResponseType();
}
- protected function getTokenIdTokenResponseType()
+ protected function getIdTokenTokenResponseType()
{
- if (isset($this->responseTypes['token id_token'])) {
- return $this->responseTypes['token id_token'];
+ if (isset($this->responseTypes['id_token token'])) {
+ return $this->responseTypes['id_token token'];
}
- return $this->createDefaultTokenIdTokenResponseType();
+ return $this->createDefaultIdTokenTokenResponseType();
}
/**
* For Resource Controller
*/
- protected function createDefaultCryptoTokenStorage()
+ protected function createDefaultJwtAccessTokenStorage()
{
if (!isset($this->storages['public_key'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens");
@@ -658,13 +663,13 @@ class Server implements ResourceControllerInterface,
$tokenStorage = $this->storages['access_token'];
}
// wrap the access token storage as required.
- return new CryptoTokenStorage($this->storages['public_key'], $tokenStorage);
+ return new JwtAccessTokenStorage($this->storages['public_key'], $tokenStorage);
}
/**
* For Authorize and Token Controllers
*/
- protected function createDefaultCryptoTokenResponseType()
+ protected function createDefaultJwtAccessTokenResponseType()
{
if (!isset($this->storages['public_key'])) {
throw new \LogicException("You must supply a storage object implementing OAuth2\Storage\PublicKeyInterface to use crypto tokens");
@@ -682,7 +687,7 @@ class Server implements ResourceControllerInterface,
$config = array_intersect_key($this->config, array_flip(explode(' ', 'store_encrypted_token_string')));
- return new CryptoToken($this->storages['public_key'], $tokenStorage, $refreshStorage, $config);
+ return new JwtAccessToken($this->storages['public_key'], $tokenStorage, $refreshStorage, $config);
}
protected function createDefaultAccessTokenResponseType()
@@ -716,9 +721,21 @@ class Server implements ResourceControllerInterface,
return new IdToken($this->storages['user_claims'], $this->storages['public_key'], $config);
}
- protected function createDefaultTokenIdTokenResponseType()
+ protected function createDefaultIdTokenTokenResponseType()
+ {
+ return new IdTokenToken($this->getAccessTokenResponseType(), $this->getIdTokenResponseType());
+ }
+
+ protected function normalizeResponseType($name)
{
- return new TokenIdToken($this->getAccessTokenResponseType(), $this->getIdTokenResponseType());
+ // for multiple-valued response types - make them alphabetical
+ if (!empty($name) && false !== strpos($name, ' ')) {
+ $types = explode(' ', $name);
+ sort($types);
+ $name = implode(' ', $types);
+ }
+
+ return $name;
}
public function getResponse()
@@ -753,6 +770,9 @@ class Server implements ResourceControllerInterface,
public function getResponseType($name)
{
+ // for multiple-valued response types - make them alphabetical
+ $name = $this->normalizeResponseType($name);
+
return isset($this->responseTypes[$name]) ? $this->responseTypes[$name] : null;
}
diff --git a/src/OAuth2/Storage/CouchbaseDB.php b/src/OAuth2/Storage/CouchbaseDB.php
new file mode 100755
index 0000000..29226e0
--- /dev/null
+++ b/src/OAuth2/Storage/CouchbaseDB.php
@@ -0,0 +1,334 @@
+<?php
+
+namespace OAuth2\Storage;
+
+use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface;
+
+/**
+ * Simple Couchbase storage for all storage types
+ *
+ * This class should be extended or overridden as required
+ *
+ * NOTE: Passwords are stored in plaintext, which is never
+ * a good idea. Be sure to override this for your application
+ *
+ * @author Tom Park <tom@raucter.com>
+ */
+class CouchbaseDB implements AuthorizationCodeInterface,
+ AccessTokenInterface,
+ ClientCredentialsInterface,
+ UserCredentialsInterface,
+ RefreshTokenInterface,
+ JwtBearerInterface,
+ OpenIDAuthorizationCodeInterface
+{
+ protected $db;
+ protected $config;
+
+ public function __construct($connection, $config = array())
+ {
+ if ($connection instanceof \Couchbase) {
+ $this->db = $connection;
+ } else {
+ if (!is_array($connection) || !is_array($connection['servers'])) {
+ throw new \InvalidArgumentException('First argument to OAuth2\Storage\CouchbaseDB must be an instance of Couchbase or a configuration array containing a server array');
+ }
+
+ $this->db = new \Couchbase($connection['servers'], (!isset($connection['username'])) ? '' : $connection['username'], (!isset($connection['password'])) ? '' : $connection['password'], $connection['bucket'], false);
+ }
+
+ $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',
+ ), $config);
+ }
+
+ // Helper function to access couchbase item by type:
+ protected function getObjectByType($name,$id)
+ {
+ return json_decode($this->db->get($this->config[$name].'-'.$id),true);
+ }
+
+ // Helper function to set couchbase item by type:
+ protected function setObjectByType($name,$id,$array)
+ {
+ $array['type'] = $name;
+ return $this->db->set($this->config[$name].'-'.$id,json_encode($array));
+ }
+
+ // Helper function to delete couchbase item by type, wait for persist to at least 1 node
+ protected function deleteObjectByType($name,$id)
+ {
+ $this->db->delete($this->config[$name].'-'.$id,"",1);
+ }
+
+ /* ClientCredentialsInterface */
+ public function checkClientCredentials($client_id, $client_secret = null)
+ {
+ if ($result = $this->getObjectByType('client_table',$client_id)) {
+ return $result['client_secret'] == $client_secret;
+ }
+
+ return false;
+ }
+
+ public function isPublicClient($client_id)
+ {
+ if (!$result = $this->getObjectByType('client_table',$client_id)) {
+ return false;
+ }
+
+ return empty($result['client_secret']);
+ }
+
+ /* ClientInterface */
+ public function getClientDetails($client_id)
+ {
+ $result = $this->getObjectByType('client_table',$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)) {
+
+ $this->setObjectByType('client_table',$client_id, array(
+ 'client_id' => $client_id,
+ 'client_secret' => $client_secret,
+ 'redirect_uri' => $redirect_uri,
+ 'grant_types' => $grant_types,
+ 'scope' => $scope,
+ 'user_id' => $user_id,
+ ));
+ } else {
+ $this->setObjectByType('client_table',$client_id, array(
+ 'client_id' => $client_id,
+ 'client_secret' => $client_secret,
+ 'redirect_uri' => $redirect_uri,
+ 'grant_types' => $grant_types,
+ 'scope' => $scope,
+ 'user_id' => $user_id,
+ ));
+ }
+
+ return true;
+ }
+
+ 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->getObjectByType('access_token_table',$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)) {
+ $this->setObjectByType('access_token_table',$access_token, array(
+ 'access_token' => $access_token,
+ 'client_id' => $client_id,
+ 'expires' => $expires,
+ 'user_id' => $user_id,
+ 'scope' => $scope
+ ));
+ } else {
+ $this->setObjectByType('access_token_table',$access_token, array(
+ 'access_token' => $access_token,
+ 'client_id' => $client_id,
+ 'expires' => $expires,
+ 'user_id' => $user_id,
+ 'scope' => $scope
+ ));
+ }
+
+ return true;
+ }
+
+
+ /* AuthorizationCodeInterface */
+ public function getAuthorizationCode($code)
+ {
+ $code = $this->getObjectByType('code_table',$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)) {
+ $this->setObjectByType('code_table',$code, array(
+ 'authorization_code' => $code,
+ 'client_id' => $client_id,
+ 'user_id' => $user_id,
+ 'redirect_uri' => $redirect_uri,
+ 'expires' => $expires,
+ 'scope' => $scope,
+ 'id_token' => $id_token,
+ ));
+ } else {
+ $this->setObjectByType('code_table',$code,array(
+ 'authorization_code' => $code,
+ 'client_id' => $client_id,
+ 'user_id' => $user_id,
+ 'redirect_uri' => $redirect_uri,
+ 'expires' => $expires,
+ 'scope' => $scope,
+ 'id_token' => $id_token,
+ ));
+ }
+
+ return true;
+ }
+
+ public function expireAuthorizationCode($code)
+ {
+ $this->deleteObjectByType('code_table',$code);
+
+ return true;
+ }
+
+
+ /* 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->getObjectByType('refresh_token_table',$refresh_token);
+
+ return is_null($token) ? false : $token;
+ }
+
+ public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null)
+ {
+ $this->setObjectByType('refresh_token_table',$refresh_token, array(
+ 'refresh_token' => $refresh_token,
+ 'client_id' => $client_id,
+ 'user_id' => $user_id,
+ 'expires' => $expires,
+ 'scope' => $scope
+ ));
+
+ return true;
+ }
+
+ public function unsetRefreshToken($refresh_token)
+ {
+ $this->deleteObjectByType('refresh_token_table',$refresh_token);
+
+ return true;
+ }
+
+
+ // plaintext passwords are bad! Override this for your application
+ protected function checkPassword($user, $password)
+ {
+ return $user['password'] == $password;
+ }
+
+ public function getUser($username)
+ {
+ $result = $this->getObjectByType('user_table',$username);
+
+ return is_null($result) ? false : $result;
+ }
+
+ public function setUser($username, $password, $firstName = null, $lastName = null)
+ {
+ if ($this->getUser($username)) {
+ $this->setObjectByType('user_table',$username, array(
+ 'username' => $username,
+ 'password' => $password,
+ 'first_name' => $firstName,
+ 'last_name' => $lastName
+ ));
+
+ } else {
+ $this->setObjectByType('user_table',$username, array(
+ 'username' => $username,
+ 'password' => $password,
+ 'first_name' => $firstName,
+ 'last_name' => $lastName
+ ));
+
+ }
+
+ return true;
+ }
+
+ public function getClientKey($client_id, $subject)
+ {
+ if (!$jwt = $this->getObjectByType('jwt_table',$client_id)) {
+ return false;
+ }
+
+ if (isset($jwt['subject']) && $jwt['subject'] == $subject) {
+ return $jwt['key'];
+ }
+
+ return false;
+ }
+
+ 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, $expiration, $jti)
+ {
+ //TODO: Needs couchbase implementation.
+ throw new \Exception('getJti() for the Couchbase driver is currently unimplemented.');
+ }
+
+ public function setJti($client_id, $subject, $audience, $expiration, $jti)
+ {
+ //TODO: Needs couchbase implementation.
+ throw new \Exception('setJti() for the Couchbase driver is currently unimplemented.');
+ }
+}
+
diff --git a/src/OAuth2/Storage/CryptoToken.php b/src/OAuth2/Storage/JwtAccessToken.php
index 31ebb65..74b1151 100644
--- a/src/OAuth2/Storage/CryptoToken.php
+++ b/src/OAuth2/Storage/JwtAccessToken.php
@@ -9,7 +9,7 @@ use OAuth2\Encryption\Jwt;
*
* @author Brent Shaffer <bshafs at gmail dot com>
*/
-class CryptoToken implements CryptoTokenInterface
+class JwtAccessToken implements JwtAccessTokenInterface
{
protected $publicKeyStorage;
protected $tokenStorage;
@@ -39,7 +39,7 @@ class CryptoToken implements CryptoTokenInterface
return false;
}
- $client_id = isset($tokenData['client_id']) ? $tokenData['client_id'] : null;
+ $client_id = isset($tokenData['aud']) ? $tokenData['aud'] : null;
$public_key = $this->publicKeyStorage->getPublicKey($client_id);
$algorithm = $this->publicKeyStorage->getEncryptionAlgorithm($client_id);
@@ -48,7 +48,8 @@ class CryptoToken implements CryptoTokenInterface
return false;
}
- return $tokenData;
+ // normalize the JWT claims to the format expected by other components in this library
+ return $this->convertJwtToOAuth2($tokenData);
}
public function setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope = null)
@@ -57,4 +58,23 @@ class CryptoToken implements CryptoTokenInterface
return $this->tokenStorage->setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope);
}
}
+
+ // converts a JWT access token into an OAuth2-friendly format
+ protected function convertJwtToOAuth2($tokenData)
+ {
+ $keyMapping = array(
+ 'aud' => 'client_id',
+ 'exp' => 'expires',
+ 'sub' => 'user_id'
+ );
+
+ foreach ($keyMapping as $jwtKey => $oauth2Key) {
+ if (isset($tokenData[$jwtKey])) {
+ $tokenData[$oauth2Key] = $tokenData[$jwtKey];
+ unset($tokenData[$jwtKey]);
+ }
+ }
+
+ return $tokenData;
+ }
}
diff --git a/src/OAuth2/Storage/CryptoTokenInterface.php b/src/OAuth2/Storage/JwtAccessTokenInterface.php
index 867710c..3abb2aa 100644
--- a/src/OAuth2/Storage/CryptoTokenInterface.php
+++ b/src/OAuth2/Storage/JwtAccessTokenInterface.php
@@ -8,7 +8,7 @@ namespace OAuth2\Storage;
*
* @author Brent Shaffer <bshafs at gmail dot com>
*/
-interface CryptoTokenInterface extends AccessTokenInterface
+interface JwtAccessTokenInterface extends AccessTokenInterface
{
}
diff --git a/src/OAuth2/Storage/Mongo.php b/src/OAuth2/Storage/Mongo.php
index 87935fe..8a7ef10 100644
--- a/src/OAuth2/Storage/Mongo.php
+++ b/src/OAuth2/Storage/Mongo.php
@@ -40,9 +40,6 @@ class Mongo implements AuthorizationCodeInterface,
$this->db = $m->{$connection['database']};
}
- // Unix timestamps might get larger than 32 bits,
- // so let's add native support for 64 bit ints.
- ini_set('mongo.native_long', 1);
$this->config = array_merge(array(
'client_table' => 'oauth_clients',
diff --git a/src/OAuth2/Storage/Pdo.php b/src/OAuth2/Storage/Pdo.php
index 17016e7..cb6b3cd 100644
--- a/src/OAuth2/Storage/Pdo.php
+++ b/src/OAuth2/Storage/Pdo.php
@@ -64,6 +64,7 @@ class Pdo implements
'code_table' => 'oauth_authorization_codes',
'user_table' => 'oauth_users',
'jwt_table' => 'oauth_jwt',
+ 'jti_table' => 'oauth_jti',
'scope_table' => 'oauth_scopes',
'public_key_table' => 'oauth_public_keys',
), $config);
@@ -74,7 +75,7 @@ class Pdo implements
{
$stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table']));
$stmt->execute(compact('client_id'));
- $result = $stmt->fetch();
+ $result = $stmt->fetch(\PDO::FETCH_BOTH);
// make this extensible
return $result && $result['client_secret'] == $client_secret;
@@ -85,11 +86,11 @@ class Pdo implements
$stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table']));
$stmt->execute(compact('client_id'));
- if (!$result = $stmt->fetch()) {
+ if (!$result = $stmt->fetch(\PDO::FETCH_BOTH)) {
return false;
}
- return empty($result['client_secret']);;
+ return empty($result['client_secret']);
}
/* OAuth2\Storage\ClientInterface */
@@ -98,7 +99,7 @@ class Pdo implements
$stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table']));
$stmt->execute(compact('client_id'));
- return $stmt->fetch();
+ return $stmt->fetch(\PDO::FETCH_BOTH);
}
public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null)
@@ -132,7 +133,7 @@ class Pdo implements
$stmt = $this->db->prepare(sprintf('SELECT * from %s where access_token = :access_token', $this->config['access_token_table']));
$token = $stmt->execute(compact('access_token'));
- if ($token = $stmt->fetch()) {
+ if ($token = $stmt->fetch(\PDO::FETCH_BOTH)) {
// convert date string back to timestamp
$token['expires'] = strtotime($token['expires']);
}
@@ -161,7 +162,7 @@ class Pdo implements
$stmt = $this->db->prepare(sprintf('SELECT * from %s where authorization_code = :code', $this->config['code_table']));
$stmt->execute(compact('code'));
- if ($code = $stmt->fetch()) {
+ if ($code = $stmt->fetch(\PDO::FETCH_BOTH)) {
// convert date string back to timestamp
$code['expires'] = strtotime($code['expires']);
}
@@ -271,7 +272,7 @@ class Pdo implements
$stmt = $this->db->prepare(sprintf('SELECT * FROM %s WHERE refresh_token = :refresh_token', $this->config['refresh_token_table']));
$token = $stmt->execute(compact('refresh_token'));
- if ($token = $stmt->fetch()) {
+ if ($token = $stmt->fetch(\PDO::FETCH_BOTH)) {
// convert expires to epoch time
$token['expires'] = strtotime($token['expires']);
}
@@ -307,7 +308,7 @@ class Pdo implements
$stmt = $this->db->prepare($sql = sprintf('SELECT * from %s where username=:username', $this->config['user_table']));
$stmt->execute(array('username' => $username));
- if (!$userInfo = $stmt->fetch()) {
+ if (!$userInfo = $stmt->fetch(\PDO::FETCH_BOTH)) {
return false;
}
@@ -340,7 +341,7 @@ class Pdo implements
$stmt = $this->db->prepare(sprintf('SELECT count(scope) as count FROM %s WHERE scope IN (%s)', $this->config['scope_table'], $whereIn));
$stmt->execute($scope);
- if ($result = $stmt->fetch()) {
+ if ($result = $stmt->fetch(\PDO::FETCH_BOTH)) {
return $result['count'] == count($scope);
}
@@ -352,7 +353,7 @@ class Pdo implements
$stmt = $this->db->prepare(sprintf('SELECT scope FROM %s WHERE is_default=:is_default', $this->config['scope_table']));
$stmt->execute(array('is_default' => true));
- if ($result = $stmt->fetchAll()) {
+ if ($result = $stmt->fetchAll(\PDO::FETCH_BOTH)) {
$defaultScope = array_map(function ($row) {
return $row['scope'];
}, $result);
@@ -392,7 +393,7 @@ class Pdo implements
$stmt->execute(compact('client_id', 'subject', 'audience', 'expires', 'jti'));
- if ($result = $stmt->fetch()) {
+ if ($result = $stmt->fetch(\PDO::FETCH_BOTH)) {
return array(
'issuer' => $result['issuer'],
'subject' => $result['subject'],
@@ -418,7 +419,7 @@ class Pdo implements
$stmt = $this->db->prepare($sql = sprintf('SELECT public_key FROM %s WHERE client_id=:client_id OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table']));
$stmt->execute(compact('client_id'));
- if ($result = $stmt->fetch()) {
+ if ($result = $stmt->fetch(\PDO::FETCH_BOTH)) {
return $result['public_key'];
}
}
@@ -428,7 +429,7 @@ class Pdo implements
$stmt = $this->db->prepare($sql = sprintf('SELECT private_key FROM %s WHERE client_id=:client_id OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table']));
$stmt->execute(compact('client_id'));
- if ($result = $stmt->fetch()) {
+ if ($result = $stmt->fetch(\PDO::FETCH_BOTH)) {
return $result['private_key'];
}
}
@@ -438,7 +439,7 @@ class Pdo implements
$stmt = $this->db->prepare($sql = sprintf('SELECT encryption_algorithm FROM %s WHERE client_id=:client_id OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table']));
$stmt->execute(compact('client_id'));
- if ($result = $stmt->fetch()) {
+ if ($result = $stmt->fetch(\PDO::FETCH_BOTH)) {
return $result['encryption_algorithm'];
}
@@ -513,6 +514,14 @@ class Pdo implements
subject VARCHAR(80),
public_key VARCHAR(2000) NOT NULL
);
+
+ CREATE TABLE {$this->config['jti_table']} (
+ issuer VARCHAR(80) NOT NULL,
+ subject VARCHAR(80),
+ audiance VARCHAR(80),
+ expires TIMESTAMP NOT NULL,
+ jti VARCHAR(2000) NOT NULL
+ );
CREATE TABLE {$this->config['public_key_table']} (
client_id VARCHAR(80),
diff --git a/test/OAuth2/OpenID/Controller/AuthorizeControllerTest.php b/test/OAuth2/OpenID/Controller/AuthorizeControllerTest.php
index 4b217c5..a0c9b57 100644
--- a/test/OAuth2/OpenID/Controller/AuthorizeControllerTest.php
+++ b/test/OAuth2/OpenID/Controller/AuthorizeControllerTest.php
@@ -4,7 +4,7 @@ namespace OAuth2\OpenID\Controller;
use OAuth2\OpenID\Controller\AuthorizeController;
use OAuth2\OpenID\ResponseType\IdToken;
-use OAuth2\OpenID\ResponseType\TokenIdToken;
+use OAuth2\OpenID\ResponseType\IdTokenToken;
use OAuth2\ResponseType\AccessToken;
use OAuth2\Storage\Bootstrap;
use OAuth2\Server;
@@ -42,6 +42,22 @@ class AuthorizeControllerTest extends \PHPUnit_Framework_TestCase
$this->assertArrayNotHasKey('token_type', $query);
// Test valid token id_token request
+ $request->query['response_type'] = 'id_token token';
+ $server->handleAuthorizeRequest($request, $response, true);
+
+ $parts = parse_url($response->getHttpHeader('Location'));
+ parse_str($parts['fragment'], $query);
+
+ $this->assertEquals('n-0S6_WzA2Mj', $server->getAuthorizeController()->getNonce());
+ $this->assertEquals($query['state'], 'af0ifjsldkj');
+
+ $this->assertArrayHasKey('access_token', $query);
+ $this->assertArrayHasKey('expires_in', $query);
+ $this->assertArrayHasKey('token_type', $query);
+ $this->assertArrayHasKey('state', $query);
+ $this->assertArrayHasKey('id_token', $query);
+
+ // assert that with multiple-valued response types, order does not matter
$request->query['response_type'] = 'token id_token';
$server->handleAuthorizeRequest($request, $response, true);
@@ -56,6 +72,22 @@ class AuthorizeControllerTest extends \PHPUnit_Framework_TestCase
$this->assertArrayHasKey('token_type', $query);
$this->assertArrayHasKey('state', $query);
$this->assertArrayHasKey('id_token', $query);
+
+ // assert that with multiple-valued response types with extra spaces do not matter
+ $request->query['response_type'] = ' token id_token ';
+ $server->handleAuthorizeRequest($request, $response, true);
+
+ $parts = parse_url($response->getHttpHeader('Location'));
+ parse_str($parts['fragment'], $query);
+
+ $this->assertEquals('n-0S6_WzA2Mj', $server->getAuthorizeController()->getNonce());
+ $this->assertEquals($query['state'], 'af0ifjsldkj');
+
+ $this->assertArrayHasKey('access_token', $query);
+ $this->assertArrayHasKey('expires_in', $query);
+ $this->assertArrayHasKey('token_type', $query);
+ $this->assertArrayHasKey('state', $query);
+ $this->assertArrayHasKey('id_token', $query);
}
public function testMissingNonce()
@@ -73,16 +105,14 @@ class AuthorizeControllerTest extends \PHPUnit_Framework_TestCase
// Test missing nonce for 'id_token' response type
$server->handleAuthorizeRequest($request, $response, true);
-
$params = $response->getParameters();
$this->assertEquals($params['error'], 'invalid_nonce');
$this->assertEquals($params['error_description'], 'This application requires you specify a nonce parameter');
- // Test missing nonce for 'id_token' response type
- $request->query['response_type'] = 'token id_token';
+ // Test missing nonce for 'id_token token' response type
+ $request->query['response_type'] = 'id_token token';
$server->handleAuthorizeRequest($request, $response, true);
-
$params = $response->getParameters();
$this->assertEquals($params['error'], 'invalid_nonce');
@@ -143,10 +173,10 @@ class AuthorizeControllerTest extends \PHPUnit_Framework_TestCase
private function getTestServer($config = array())
{
$config += array(
- 'use_openid_connect' => true,
- 'issuer' => 'anzev',
- 'allow_implicit' => true
- );
+ 'use_openid_connect' => true,
+ 'issuer' => 'phpunit',
+ 'allow_implicit' => true
+ );
$storage = Bootstrap::getInstance()->getMemoryStorage();
$server = new Server($storage, $config);
diff --git a/test/OAuth2/OpenID/ResponseType/TokenIdTokenTest.php b/test/OAuth2/OpenID/ResponseType/IdTokenTokenTest.php
index f687cff..bc564d3 100644
--- a/test/OAuth2/OpenID/ResponseType/TokenIdTokenTest.php
+++ b/test/OAuth2/OpenID/ResponseType/IdTokenTokenTest.php
@@ -9,15 +9,16 @@ use OAuth2\Storage\Bootstrap;
use OAuth2\GrantType\ClientCredentials;
use OAuth2\ResponseType\AccessToken;
-class TokenIdTokenTest extends \PHPUnit_Framework_TestCase
+class IdTokenTokenTest extends \PHPUnit_Framework_TestCase
{
public function testHandleAuthorizeRequest()
{
// add the test parameters in memory
$server = $this->getTestServer(array('allow_implicit' => true));
+
$request = new Request(array(
- 'response_type' => 'token id_token',
+ 'response_type' => 'id_token token',
'redirect_uri' => 'http://adobe.com',
'client_id' => 'Test Client ID',
'scope' => 'openid',
@@ -40,12 +41,9 @@ class TokenIdTokenTest extends \PHPUnit_Framework_TestCase
$this->assertNotNull($params);
$this->assertArrayHasKey('id_token', $params);
$this->assertArrayHasKey('access_token', $params);
- $this->validateIdToken($params['id_token']);
- }
- private function validateIdToken($id_token)
- {
- $parts = explode('.', $id_token);
+ // validate ID Token
+ $parts = explode('.', $params['id_token']);
foreach ($parts as &$part) {
// Each part is a base64url encoded json string.
$part = str_replace(array('-', '_'), array('+', '/'), $part);
@@ -80,10 +78,10 @@ class TokenIdTokenTest extends \PHPUnit_Framework_TestCase
$memoryStorage = Bootstrap::getInstance()->getMemoryStorage();
$responseTypes = array(
- 'token' => new AccessToken($memoryStorage, $memoryStorage),
- 'id_token' => new IdToken($memoryStorage, $memoryStorage, $config),
+ 'token' => $token = new AccessToken($memoryStorage, $memoryStorage),
+ 'id_token' => $idToken = new IdToken($memoryStorage, $memoryStorage, $config),
+ 'id_token token' => new IdTokenToken($token, $idToken),
);
- $responseTypes['token id_token'] = new TokenIdToken($responseTypes['token'], $responseTypes['id_token']);
$server = new Server($memoryStorage, $config, array(), $responseTypes);
$server->addGrantType(new ClientCredentials($memoryStorage));
diff --git a/test/OAuth2/RequestTest.php b/test/OAuth2/RequestTest.php
index a7bcc4e..10db321 100644
--- a/test/OAuth2/RequestTest.php
+++ b/test/OAuth2/RequestTest.php
@@ -75,6 +75,16 @@ class RequestTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('Basic secret', $request->headers('Authorization'));
}
+ public function testRequestReturnsPostParamIfNoQueryParamAvailable()
+ {
+ $request = new Request(
+ array(),
+ array('client_id' => 'correct')
+ );
+
+ $this->assertEquals('correct', $request->query('client_id', $request->request('client_id')));
+ }
+
private function getTestServer($config = array())
{
$storage = Bootstrap::getInstance()->getMemoryStorage();
diff --git a/test/OAuth2/ResponseType/CryptoTokenTest.php b/test/OAuth2/ResponseType/JwtAccessTokenTest.php
index d1ab89e..b326ec4 100644
--- a/test/OAuth2/ResponseType/CryptoTokenTest.php
+++ b/test/OAuth2/ResponseType/JwtAccessTokenTest.php
@@ -6,14 +6,39 @@ use OAuth2\Server;
use OAuth2\Response;
use OAuth2\Request\TestRequest;
use OAuth2\Storage\Bootstrap;
-use OAuth2\Storage\CryptoToken as CryptoTokenStorage;
+use OAuth2\Storage\JwtAccessToken as JwtAccessTokenStorage;
use OAuth2\GrantType\ClientCredentials;
use OAuth2\GrantType\UserCredentials;
use OAuth2\GrantType\RefreshToken;
+use OAuth2\Encryption\Jwt;
-class CryptoTokenTest extends \PHPUnit_Framework_TestCase
+class JwtAccessTokenTest extends \PHPUnit_Framework_TestCase
{
- public function testGrantCryptoToken()
+ public function testCreateAccessToken()
+ {
+ $server = $this->getTestServer();
+ $jwtResponseType = $server->getResponseType('token');
+
+ $accessToken = $jwtResponseType->createAccessToken('Test Client ID', 123, 'test', false);
+ $jwt = new Jwt;
+ $decodedAccessToken = $jwt->decode($accessToken['access_token'], null, false);
+
+ $this->assertArrayHasKey('id', $decodedAccessToken);
+ $this->assertArrayHasKey('iss', $decodedAccessToken);
+ $this->assertArrayHasKey('aud', $decodedAccessToken);
+ $this->assertArrayHasKey('exp', $decodedAccessToken);
+ $this->assertArrayHasKey('iat', $decodedAccessToken);
+ $this->assertArrayHasKey('token_type', $decodedAccessToken);
+ $this->assertArrayHasKey('scope', $decodedAccessToken);
+
+ $this->assertEquals('https://api.example.com', $decodedAccessToken['iss']);
+ $this->assertEquals('Test Client ID', $decodedAccessToken['aud']);
+ $this->assertEquals(123, $decodedAccessToken['sub']);
+ $delta = $decodedAccessToken['exp'] - $decodedAccessToken['iat'];
+ $this->assertEquals(3600, $delta);
+ }
+
+ public function testGrantJwtAccessToken()
{
// add the test parameters in memory
$server = $this->getTestServer();
@@ -28,7 +53,7 @@ class CryptoTokenTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(2, substr_count($response->getParameter('access_token'), '.'));
}
- public function testAccessResourceWithCryptoToken()
+ public function testAccessResourceWithJwtAccessToken()
{
// add the test parameters in memory
$server = $this->getTestServer();
@@ -38,17 +63,17 @@ class CryptoTokenTest extends \PHPUnit_Framework_TestCase
'client_secret' => 'TestSecret', // valid client secret
));
$server->handleTokenRequest($request, $response = new Response());
- $this->assertNotNull($cryptoToken = $response->getParameter('access_token'));
+ $this->assertNotNull($JwtAccessToken = $response->getParameter('access_token'));
// make a call to the resource server using the crypto token
$request = TestRequest::createPost(array(
- 'access_token' => $cryptoToken,
+ 'access_token' => $JwtAccessToken,
));
$this->assertTrue($server->verifyResourceRequest($request));
}
- public function testAccessResourceWithCryptoTokenUsingSecondaryStorage()
+ public function testAccessResourceWithJwtAccessTokenUsingSecondaryStorage()
{
// add the test parameters in memory
$server = $this->getTestServer();
@@ -58,11 +83,11 @@ class CryptoTokenTest extends \PHPUnit_Framework_TestCase
'client_secret' => 'TestSecret', // valid client secret
));
$server->handleTokenRequest($request, $response = new Response());
- $this->assertNotNull($cryptoToken = $response->getParameter('access_token'));
+ $this->assertNotNull($JwtAccessToken = $response->getParameter('access_token'));
// make a call to the resource server using the crypto token
$request = TestRequest::createPost(array(
- 'access_token' => $cryptoToken,
+ 'access_token' => $JwtAccessToken,
));
// create a resource server with the "memory" storage from the grant server
@@ -71,16 +96,16 @@ class CryptoTokenTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($resourceServer->verifyResourceRequest($request));
}
- public function testCryptoTokenWithRefreshToken()
+ public function testJwtAccessTokenWithRefreshToken()
{
$server = $this->getTestServer();
- // add "UserCredentials" grant type and "CryptoToken" response type
- // and ensure "CryptoToken" response type has "RefreshToken" storage
+ // add "UserCredentials" grant type and "JwtAccessToken" response type
+ // and ensure "JwtAccessToken" response type has "RefreshToken" storage
$memoryStorage = Bootstrap::getInstance()->getMemoryStorage();
$server->addGrantType(new UserCredentials($memoryStorage));
$server->addGrantType(new RefreshToken($memoryStorage));
- $server->addResponseType(new CryptoToken($memoryStorage, $memoryStorage, $memoryStorage), 'token');
+ $server->addResponseType(new JwtAccessToken($memoryStorage, $memoryStorage, $memoryStorage), 'token');
$request = TestRequest::createPost(array(
'grant_type' => 'password', // valid grant type
@@ -92,11 +117,11 @@ class CryptoTokenTest extends \PHPUnit_Framework_TestCase
// make the call to grant a crypto token
$server->handleTokenRequest($request, $response = new Response());
- $this->assertNotNull($cryptoToken = $response->getParameter('access_token'));
+ $this->assertNotNull($JwtAccessToken = $response->getParameter('access_token'));
$this->assertNotNull($refreshToken = $response->getParameter('refresh_token'));
// decode token and make sure refresh_token isn't set
- list($header, $payload, $signature) = explode('.', $cryptoToken);
+ list($header, $payload, $signature) = explode('.', $JwtAccessToken);
$decodedToken = json_decode(base64_decode($payload), true);
$this->assertFalse(array_key_exists('refresh_token', $decodedToken));
@@ -117,15 +142,16 @@ class CryptoTokenTest extends \PHPUnit_Framework_TestCase
$memoryStorage = Bootstrap::getInstance()->getMemoryStorage();
$storage = array(
- 'access_token' => new CryptoTokenStorage($memoryStorage),
+ 'access_token' => new JwtAccessTokenStorage($memoryStorage),
'client' => $memoryStorage,
'client_credentials' => $memoryStorage,
);
$server = new Server($storage);
$server->addGrantType(new ClientCredentials($memoryStorage));
- // make the "token" response type a CryptoToken
- $server->addResponseType(new CryptoToken($memoryStorage, $memoryStorage));
+ // make the "token" response type a JwtAccessToken
+ $config = array('issuer' => 'https://api.example.com');
+ $server->addResponseType(new JwtAccessToken($memoryStorage, $memoryStorage, null, $config));
return $server;
}
diff --git a/test/OAuth2/ServerTest.php b/test/OAuth2/ServerTest.php
index fd824fc..2d1460c 100644
--- a/test/OAuth2/ServerTest.php
+++ b/test/OAuth2/ServerTest.php
@@ -428,9 +428,9 @@ class ServerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException LogicException OAuth2\Storage\PublicKeyInterface
**/
- public function testUsingCryptoTokensWithoutPublicKeyStorageThrowsException()
+ public function testUsingJwtAccessTokensWithoutPublicKeyStorageThrowsException()
{
- $server = new Server(array(), array('use_crypto_tokens' => true));
+ $server = new Server(array(), array('use_jwt_access_tokens' => true));
$server->addGrantType($this->getMock('OAuth2\GrantType\GrantTypeInterface'));
$server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface'));
$server->addStorage($this->getMock('OAuth2\Storage\ClientCredentialsInterface'));
@@ -438,10 +438,10 @@ class ServerTest extends \PHPUnit_Framework_TestCase
$server->getTokenController();
}
- public function testUsingJustCryptoTokenStorageWithResourceControllerIsOkay()
+ public function testUsingJustJwtAccessTokenStorageWithResourceControllerIsOkay()
{
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface');
- $server = new Server(array($pubkey), array('use_crypto_tokens' => true));
+ $server = new Server(array($pubkey), array('use_jwt_access_tokens' => true));
$this->assertNotNull($server->getResourceController());
$this->assertInstanceOf('OAuth2\Storage\PublicKeyInterface', $server->getStorage('public_key'));
@@ -450,31 +450,31 @@ class ServerTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException LogicException OAuth2\Storage\ClientInterface
**/
- public function testUsingJustCryptoTokenStorageWithAuthorizeControllerThrowsException()
+ public function testUsingJustJwtAccessTokenStorageWithAuthorizeControllerThrowsException()
{
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface');
- $server = new Server(array($pubkey), array('use_crypto_tokens' => true));
+ $server = new Server(array($pubkey), array('use_jwt_access_tokens' => true));
$this->assertNotNull($server->getAuthorizeController());
}
/**
* @expectedException LogicException grant_types
**/
- public function testUsingJustCryptoTokenStorageWithTokenControllerThrowsException()
+ public function testUsingJustJwtAccessTokenStorageWithTokenControllerThrowsException()
{
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface');
- $server = new Server(array($pubkey), array('use_crypto_tokens' => true));
+ $server = new Server(array($pubkey), array('use_jwt_access_tokens' => true));
$server->getTokenController();
}
- public function testUsingCryptoTokenAndClientStorageWithAuthorizeControllerIsOkay()
+ public function testUsingJwtAccessTokenAndClientStorageWithAuthorizeControllerIsOkay()
{
$pubkey = $this->getMock('OAuth2\Storage\PublicKeyInterface');
$client = $this->getMock('OAuth2\Storage\ClientInterface');
- $server = new Server(array($pubkey, $client), array('use_crypto_tokens' => true, 'allow_implicit' => true));
+ $server = new Server(array($pubkey, $client), array('use_jwt_access_tokens' => true, 'allow_implicit' => true));
$this->assertNotNull($server->getAuthorizeController());
- $this->assertInstanceOf('OAuth2\ResponseType\CryptoToken', $server->getResponseType('token'));
+ $this->assertInstanceOf('OAuth2\ResponseType\JwtAccessToken', $server->getResponseType('token'));
}
/**
@@ -526,7 +526,7 @@ class ServerTest extends \PHPUnit_Framework_TestCase
$server->getAuthorizeController();
$this->assertInstanceOf('OAuth2\OpenID\ResponseType\IdTokenInterface', $server->getResponseType('id_token'));
- $this->assertNull($server->getResponseType('token id_token'));
+ $this->assertNull($server->getResponseType('id_token token'));
}
/**
@@ -546,7 +546,7 @@ class ServerTest extends \PHPUnit_Framework_TestCase
$server->getAuthorizeController();
}
- public function testUsingOpenIDConnectWithAllowImplicitAndUseCryptoTokensIsOkay()
+ public function testUsingOpenIDConnectWithAllowImplicitAndUseJwtAccessTokensIsOkay()
{
$client = $this->getMock('OAuth2\Storage\ClientInterface');
$userclaims = $this->getMock('OAuth2\OpenID\Storage\UserClaimsInterface');
@@ -555,13 +555,13 @@ class ServerTest extends \PHPUnit_Framework_TestCase
'use_openid_connect' => true,
'issuer' => 'someguy',
'allow_implicit' => true,
- 'use_crypto_tokens' => true,
+ 'use_jwt_access_tokens' => true,
));
$server->getAuthorizeController();
$this->assertInstanceOf('OAuth2\OpenID\ResponseType\IdTokenInterface', $server->getResponseType('id_token'));
- $this->assertInstanceOf('OAuth2\OpenID\ResponseType\TokenIdTokenInterface', $server->getResponseType('token id_token'));
+ $this->assertInstanceOf('OAuth2\OpenID\ResponseType\IdTokenTokenInterface', $server->getResponseType('id_token token'));
}
public function testUsingOpenIDConnectWithAllowImplicitAndAccessTokenStorageIsOkay()
@@ -579,7 +579,7 @@ class ServerTest extends \PHPUnit_Framework_TestCase
$server->getAuthorizeController();
$this->assertInstanceOf('OAuth2\OpenID\ResponseType\IdTokenInterface', $server->getResponseType('id_token'));
- $this->assertInstanceOf('OAuth2\OpenID\ResponseType\TokenIdTokenInterface', $server->getResponseType('token id_token'));
+ $this->assertInstanceOf('OAuth2\OpenID\ResponseType\IdTokenTokenInterface', $server->getResponseType('id_token token'));
}
public function testUsingOpenIDConnectWithAllowImplicitAndAccessTokenResponseTypeIsOkay()
@@ -600,7 +600,7 @@ class ServerTest extends \PHPUnit_Framework_TestCase
$server->getAuthorizeController();
$this->assertInstanceOf('OAuth2\OpenID\ResponseType\IdTokenInterface', $server->getResponseType('id_token'));
- $this->assertInstanceOf('OAuth2\OpenID\ResponseType\TokenIdTokenInterface', $server->getResponseType('token id_token'));
+ $this->assertInstanceOf('OAuth2\OpenID\ResponseType\IdTokenTokenInterface', $server->getResponseType('id_token token'));
}
/**
@@ -642,6 +642,16 @@ class ServerTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('OAuth2\OpenID\GrantType\AuthorizationCode', $server->getGrantType('authorization_code'));
}
+ public function testMultipleValuedResponseTypeOrderDoesntMatter()
+ {
+ $responseType = $this->getMock('OAuth2\OpenID\ResponseType\IdTokenTokenInterface');
+ $server = new Server(array(), array(), array(), array(
+ 'token id_token' => $responseType,
+ ));
+
+ $this->assertEquals($responseType, $server->getResponseType('id_token token'));
+ }
+
public function testAddGrantTypeWithoutKey()
{
$server = new Server();
diff --git a/test/OAuth2/Storage/CryptoTokenTest.php b/test/OAuth2/Storage/JwtAccessTokenTest.php
index e7f686d..38f8be7 100644
--- a/test/OAuth2/Storage/CryptoTokenTest.php
+++ b/test/OAuth2/Storage/JwtAccessTokenTest.php
@@ -4,7 +4,7 @@ namespace OAuth2\Storage;
use OAuth2\Encryption\Jwt;
-class CryptoTokenTest extends BaseTest
+class jwtAccessTokenTest extends BaseTest
{
/** @dataProvider provideStorage */
public function testSetAccessToken($storage)
@@ -14,18 +14,18 @@ class CryptoTokenTest extends BaseTest
return;
}
- $crypto = new CryptoToken($storage);
+ $crypto = new jwtAccessToken($storage);
$publicKeyStorage = Bootstrap::getInstance()->getMemoryStorage();
$encryptionUtil = new Jwt();
- $cryptoToken = array(
+ $jwtAccessToken = array(
'access_token' => rand(),
'expires' => time() + 100,
'scope' => 'foo',
);
- $token = $encryptionUtil->encode($cryptoToken, $storage->getPrivateKey(), $storage->getEncryptionAlgorithm());
+ $token = $encryptionUtil->encode($jwtAccessToken, $storage->getPrivateKey(), $storage->getEncryptionAlgorithm());
$this->assertNotNull($token);
@@ -34,8 +34,8 @@ class CryptoTokenTest extends BaseTest
$this->assertTrue(is_array($tokenData));
/* assert the decoded token is the same */
- $this->assertEquals($tokenData['access_token'], $cryptoToken['access_token']);
- $this->assertEquals($tokenData['expires'], $cryptoToken['expires']);
- $this->assertEquals($tokenData['scope'], $cryptoToken['scope']);
+ $this->assertEquals($tokenData['access_token'], $jwtAccessToken['access_token']);
+ $this->assertEquals($tokenData['expires'], $jwtAccessToken['expires']);
+ $this->assertEquals($tokenData['scope'], $jwtAccessToken['scope']);
}
}
diff --git a/test/lib/OAuth2/Storage/BaseTest.php b/test/lib/OAuth2/Storage/BaseTest.php
index 579997c..921d525 100644..100755
--- a/test/lib/OAuth2/Storage/BaseTest.php
+++ b/test/lib/OAuth2/Storage/BaseTest.php
@@ -14,6 +14,7 @@ abstract class BaseTest extends \PHPUnit_Framework_TestCase
$redis = Bootstrap::getInstance()->getRedisStorage();
$cassandra = Bootstrap::getInstance()->getCassandraStorage();
$dynamodb = Bootstrap::getInstance()->getDynamoDbStorage();
+ $couchbase = Bootstrap::getInstance()->getCouchbase();
/* hack until we can fix "default_scope" dependencies in other tests */
$memory->defaultScope = 'defaultscope1 defaultscope2';
@@ -27,6 +28,7 @@ abstract class BaseTest extends \PHPUnit_Framework_TestCase
array($redis),
array($cassandra),
array($dynamodb),
+ array($couchbase),
);
}
}
diff --git a/test/lib/OAuth2/Storage/Bootstrap.php b/test/lib/OAuth2/Storage/Bootstrap.php
index 129ca2f..f1af0ba 100644..100755
--- a/test/lib/OAuth2/Storage/Bootstrap.php
+++ b/test/lib/OAuth2/Storage/Bootstrap.php
@@ -15,6 +15,7 @@ class Bootstrap
private $cassandra;
private $configDir;
private $dynamodb;
+ private $couchbase;
public function __construct()
{
@@ -167,6 +168,43 @@ class Bootstrap
return true;
}
+ public function getCouchbase()
+ {
+ if (!$this->couchbase) {
+ $skipCouchbase = $this->getEnvVar('SKIP_COUCHBASE_TESTS');
+ if (!$skipCouchbase && class_exists('Couchbase')) {
+
+ $couchbase = new \Couchbase(array('localhost:8091'), '', '', 'auth', false);
+ if ($this->testCouchbaseConnection($couchbase)) {
+ $this->clearCouchbase($couchbase);
+ $this->createCouchbaseDB($couchbase);
+
+ $this->couchbase = new CouchbaseDB($couchbase);
+ } else {
+ $this->couchbase = new NullStorage('Couchbase', 'Unable to connect to Couchbase server on "localhost:8091"');
+ }
+ } else {
+ $this->couchbase = new NullStorage('Couchbase', 'Missing Couchbase php extension. Please install couchbase.so');
+ }
+ }
+
+ return $this->couchbase;
+ }
+
+ private function testCouchbaseConnection(\Couchbase $couchbase)
+ {
+ try {
+ if (count($couchbase->getServers()) > 0) {
+ return true;
+ }
+ } catch (\CouchbaseException $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+
public function getCassandraStorage()
{
if (!$this->cassandra) {
@@ -341,6 +379,45 @@ class Bootstrap
return $this->configDir;
}
+
+ private function createCouchbaseDB(\Couchbase $db) {
+
+ $db->set('oauth_clients-oauth_test_client',json_encode(array(
+ 'client_id' => "oauth_test_client",
+ 'client_secret' => "testpass",
+ 'redirect_uri' => "http://example.com",
+ 'grant_types' => 'implicit password'
+ )));
+
+ $db->set('oauth_access_tokens-testtoken',json_encode(array(
+ 'access_token' => "testtoken",
+ 'client_id' => "Some Client"
+ )));
+
+ $db->set('oauth_authorization_codes-testcode',json_encode(array(
+ 'access_token' => "testcode",
+ 'client_id' => "Some Client"
+ )));
+
+ $db->set('oauth_users-testuser',json_encode(array(
+ 'username' => "testuser",
+ 'password' => "password"
+ )));
+
+ $db->set('oauth_jwt-oauth_test_client',json_encode(array(
+ 'client_id' => 'oauth_test_client',
+ 'key' => $this->getTestPublicKey(),
+ 'subject' => 'test_subject',
+ )));
+ }
+
+ private function clearCouchbase(\Couchbase $cb) {
+ $cb->delete('oauth_authorization_codes-new-openid-code');
+ $cb->delete('oauth_access_tokens-newtoken');
+ $cb->delete('oauth_authorization_codes-newcode');
+ $cb->delete('oauth_refresh_tokens-refreshtoken');
+ }
+
private function createMongoDb(\MongoDB $db)
{
$db->oauth_clients->insert(array(