diff --git a/sql/mysql.sql b/sql/mysql.sql
index e66b2205..552b5e02 100644
--- a/sql/mysql.sql
+++ b/sql/mysql.sql
@@ -38,12 +38,13 @@ CREATE TABLE `oauth_session_access_tokens` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `oauth_session_authcodes` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`session_id` int(10) unsigned NOT NULL,
`auth_code` char(40) NOT NULL,
`auth_code_expires` int(10) unsigned NOT NULL,
- `scope_ids` char(255) DEFAULT NULL,
- PRIMARY KEY (`session_id`),
- CONSTRAINT `f_oaseau_seid` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
+ PRIMARY KEY (`id`),
+ KEY `session_id` (`session_id`),
+ CONSTRAINT `oauth_session_authcodes_ibfk_1` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `oauth_session_redirects` (
@@ -70,7 +71,7 @@ CREATE TABLE `oauth_scopes` (
`name` varchar(255) NOT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
- UNIQUE KEY `u_oasc_sc` (`scope_key`)
+ UNIQUE KEY `u_oasc_sc` (`scope`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `oauth_session_token_scopes` (
@@ -82,4 +83,13 @@ CREATE TABLE `oauth_session_token_scopes` (
KEY `f_oasetosc_scid` (`scope_id`),
CONSTRAINT `f_oasetosc_scid` FOREIGN KEY (`scope_id`) REFERENCES `oauth_scopes` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
CONSTRAINT `f_oasetosc_setoid` FOREIGN KEY (`session_access_token_id`) REFERENCES `oauth_session_access_tokens` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE `oauth_session_authcode_scopes` (
+ `oauth_session_authcode_id` int(10) unsigned NOT NULL,
+ `scope_id` smallint(5) unsigned NOT NULL,
+ KEY `oauth_session_authcode_id` (`oauth_session_authcode_id`),
+ KEY `scope_id` (`scope_id`),
+ CONSTRAINT `oauth_session_authcode_scopes_ibfk_2` FOREIGN KEY (`scope_id`) REFERENCES `oauth_scopes` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `oauth_session_authcode_scopes_ibfk_1` FOREIGN KEY (`oauth_session_authcode_id`) REFERENCES `oauth_session_authcodes` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
\ No newline at end of file
diff --git a/src/League/OAuth2/Server/Grant/AuthCode.php b/src/League/OAuth2/Server/Grant/AuthCode.php
index b8837099..70447a41 100644
--- a/src/League/OAuth2/Server/Grant/AuthCode.php
+++ b/src/League/OAuth2/Server/Grant/AuthCode.php
@@ -193,13 +193,6 @@ class AuthCode implements GrantTypeInterface {
// Remove any old sessions the user might have
$this->authServer->getStorage('session')->deleteSession($authParams['client_id'], $type, $typeId);
- // List of scopes IDs
- $scopeIds = array();
- foreach ($authParams['scopes'] as $scope)
- {
- $scopeIds[] = $scope['id'];
- }
-
// Create a new session
$sessionId = $this->authServer->getStorage('session')->createSession($authParams['client_id'], $type, $typeId);
@@ -207,7 +200,12 @@ class AuthCode implements GrantTypeInterface {
$this->authServer->getStorage('session')->associateRedirectUri($sessionId, $authParams['redirect_uri']);
// Associate the auth code
- $this->authServer->getStorage('session')->associateAuthCode($sessionId, $authCode, time() + $this->authTokenTTL, implode(',', $scopeIds));
+ $authCodeId = $this->authServer->getStorage('session')->associateAuthCode($sessionId, $authCode, time() + $this->authTokenTTL);
+
+ // Associate the scopes to the auth code
+ foreach ($authParams['scopes'] as $scope) {
+ $this->authServer->getStorage('session')->associateAuthCodeScope($authCodeId, $scope['id']);
+ }
return $authCode;
}
@@ -249,30 +247,30 @@ class AuthCode implements GrantTypeInterface {
}
// Verify the authorization code matches the client_id and the request_uri
- $session = $this->authServer->getStorage('session')->validateAuthCode($authParams['client_id'], $authParams['redirect_uri'], $authParams['code']);
+ $authCodeDetails = $this->authServer->getStorage('session')->validateAuthCode($authParams['client_id'], $authParams['redirect_uri'], $authParams['code']);
- if ( ! $session) {
+ if ( ! $authCodeDetails) {
throw new Exception\ClientException(sprintf($this->authServer->getExceptionMessage('invalid_grant'), 'code'), 9);
}
- // A session ID was returned so update it with an access token and remove the authorisation code
+ // Get any associated scopes
+ $scopes = $this->authServer->getStorage('session')->getAuthCodeScopes($authCodeDetails['authcode_id']);
+ // A session ID was returned so update it with an access token and remove the authorisation code
$accessToken = SecureKey::make();
$accessTokenExpiresIn = ($this->accessTokenTTL !== null) ? $this->accessTokenTTL : $this->authServer->getAccessTokenTTL();
$accessTokenExpires = time() + $accessTokenExpiresIn;
// Remove the auth code
- $this->authServer->getStorage('session')->removeAuthCode($session['id']);
+ $this->authServer->getStorage('session')->removeAuthCode($authCodeDetails['session_id']);
// Create an access token
- $accessTokenId = $this->authServer->getStorage('session')->associateAccessToken($session['id'], $accessToken, $accessTokenExpires);
+ $accessTokenId = $this->authServer->getStorage('session')->associateAccessToken($authCodeDetails['session_id'], $accessToken, $accessTokenExpires);
// Associate scopes with the access token
- if ( ! is_null($session['scope_ids'])) {
- $scopeIds = explode(',', $session['scope_ids']);
-
- foreach ($scopeIds as $scopeId) {
- $this->authServer->getStorage('session')->associateScope($accessTokenId, $scopeId);
+ if (count($scopes) > 0) {
+ foreach ($scopes as $scope) {
+ $this->authServer->getStorage('session')->associateScope($accessTokenId, $scope['scope_id']);
}
}
diff --git a/src/League/OAuth2/Server/Storage/PDO/Session.php b/src/League/OAuth2/Server/Storage/PDO/Session.php
index 311ce3f1..abde8b2b 100644
--- a/src/League/OAuth2/Server/Storage/PDO/Session.php
+++ b/src/League/OAuth2/Server/Storage/PDO/Session.php
@@ -70,17 +70,18 @@ class Session implements SessionInterface
$stmt->execute();
}
- public function associateAuthCode($sessionId, $authCode, $expireTime, $scopeIds = null)
+ public function associateAuthCode($sessionId, $authCode, $expireTime)
{
$db = \ezcDbInstance::get();
- $stmt = $db->prepare('INSERT INTO oauth_session_authcodes (session_id, auth_code, auth_code_expires, scope_ids)
- VALUE (:sessionId, :authCode, :authCodeExpires, :scopeIds)');
+ $stmt = $db->prepare('INSERT INTO oauth_session_authcodes (session_id, auth_code, auth_code_expires)
+ VALUE (:sessionId, :authCode, :authCodeExpires)');
$stmt->bindValue(':sessionId', $sessionId);
$stmt->bindValue(':authCode', $authCode);
$stmt->bindValue(':authCodeExpires', $expireTime);
- $stmt->bindValue(':scopeIds', $scopeIds);
$stmt->execute();
+
+ return $db->lastInsertId();
}
public function removeAuthCode($sessionId)
@@ -96,12 +97,12 @@ class Session implements SessionInterface
{
$db = \ezcDbInstance::get();
- $stmt = $db->prepare('SELECT oauth_sessions.id, oauth_session_authcodes.scope_ids FROM oauth_sessions JOIN
- oauth_session_authcodes ON oauth_session_authcodes.`session_id` = oauth_sessions.id JOIN
- oauth_session_redirects ON oauth_session_redirects.`session_id` = oauth_sessions.id WHERE
- oauth_sessions.client_id = :clientId AND oauth_session_authcodes.`auth_code` = :authCode AND
- `oauth_session_authcodes`.`auth_code_expires` >= :time AND `oauth_session_redirects`.`redirect_uri`
- = :redirectUri');
+ $stmt = $db->prepare('SELECT oauth_sessions.id AS session_id, oauth_session_authcodes.id AS authcode_id
+ FROM oauth_sessions JOIN oauth_session_authcodes ON oauth_session_authcodes.`session_id`
+ = oauth_sessions.id JOIN oauth_session_redirects ON oauth_session_redirects.`session_id`
+ = oauth_sessions.id WHERE oauth_sessions.client_id = :clientId AND oauth_session_authcodes.`auth_code`
+ = :authCode AND `oauth_session_authcodes`.`auth_code_expires` >= :time AND
+ `oauth_session_redirects`.`redirect_uri` = :redirectUri');
$stmt->bindValue(':clientId', $clientId);
$stmt->bindValue(':redirectUri', $redirectUri);
$stmt->bindValue(':authCode', $authCode);
@@ -160,6 +161,27 @@ class Session implements SessionInterface
return ($result === false) ? false : (array) $result;
}
+ public function associateAuthCodeScope($authCodeId, $scopeId)
+ {
+ $db = \ezcDbInstance::get();
+
+ $stmt = $db->prepare('INSERT INTO `oauth_session_authcode_scopes` (`oauth_session_authcode_id`, `scope_id`) VALUES (:authCodeId, :scopeId)');
+ $stmt->bindValue(':authCodeId', $authCodeId);
+ $stmt->bindValue(':scopeId', $scopeId);
+ $stmt->execute();
+ }
+
+ public function getAuthCodeScopes($oauthSessionAuthCodeId)
+ {
+ $db = \ezcDbInstance::get();
+
+ $stmt = $db->prepare('SELECT scope_id FROM `oauth_session_authcode_scopes` WHERE oauth_session_authcode_id = :authCodeId');
+ $stmt->bindValue(':authCodeId', $oauthSessionAuthCodeId);
+ $stmt->execute();
+
+ return $stmt->fetchAll();
+ }
+
public function associateScope($accessTokenId, $scopeId)
{
$db = \ezcDbInstance::get();
diff --git a/src/League/OAuth2/Server/Storage/SessionInterface.php b/src/League/OAuth2/Server/Storage/SessionInterface.php
index 30b0a6e1..08cd4c53 100644
--- a/src/League/OAuth2/Server/Storage/SessionInterface.php
+++ b/src/League/OAuth2/Server/Storage/SessionInterface.php
@@ -102,17 +102,16 @@ interface SessionInterface
* Example SQL query:
*
*
- * INSERT INTO oauth_session_authcodes (session_id, auth_code, auth_code_expires, scope_ids)
- * VALUE (:sessionId, :authCode, :authCodeExpires, :scopeIds)
+ * INSERT INTO oauth_session_authcodes (session_id, auth_code, auth_code_expires)
+ * VALUE (:sessionId, :authCode, :authCodeExpires)
*
*
* @param int $sessionId The session ID
* @param string $authCode The authorization code
* @param int $expireTime Unix timestamp of the access token expiry time
- * @param string $scopeIds Comma seperated list of scope IDs to be later associated (default = null)
- * @return void
+ * @return int The auth code ID
*/
- public function associateAuthCode($sessionId, $authCode, $expireTime, $scopeIds = null);
+ public function associateAuthCode($sessionId, $authCode, $expireTime);
/**
* Remove an associated authorization token from a session
@@ -134,7 +133,7 @@ interface SessionInterface
* Example SQL query:
*
*
- * SELECT oauth_sessions.id, oauth_session_authcodes.scope_ids FROM oauth_sessions
+ * SELECT oauth_sessions.id AS session_id, oauth_session_authcodes.id AS authcode_id FROM oauth_sessions
* JOIN oauth_session_authcodes ON oauth_session_authcodes.`session_id` = oauth_sessions.id
* JOIN oauth_session_redirects ON oauth_session_redirects.`session_id` = oauth_sessions.id WHERE
* oauth_sessions.client_id = :clientId AND oauth_session_authcodes.`auth_code` = :authCode
@@ -146,8 +145,8 @@ interface SessionInterface
*
*
* array(
- * 'id' => (int), // the session ID
- * 'scope_ids' => (string)
+ * 'session_id' => (int)
+ * 'authcode_id' => (int)
* )
*
*
@@ -240,6 +239,50 @@ interface SessionInterface
*/
public function getAccessToken($accessTokenId);
+ /**
+ * Associate scopes with an auth code (bound to the session)
+ *
+ * Example SQL query:
+ *
+ *
+ * INSERT INTO `oauth_session_authcode_scopes` (`oauth_session_authcode_id`, `scope_id`) VALUES
+ * (:authCodeId, :scopeId)
+ *
+ *
+ * @param int $authCodeId The auth code ID
+ * @param int $scopeId The scope ID
+ * @return void
+ */
+ public function associateAuthCodeScope($authCodeId, $scopeId);
+
+ /**
+ * Get the scopes associated with an auth code
+ *
+ * Example SQL query:
+ *
+ *
+ * SELECT scope_id FROM `oauth_session_authcode_scopes` WHERE oauth_session_authcode_id = :authCodeId
+ *
+ *
+ * Expected response:
+ *
+ *
+ * array(
+ * array(
+ * 'scope_id' => (int)
+ * ),
+ * array(
+ * 'scope_id' => (int)
+ * ),
+ * ...
+ * )
+ *
+ *
+ * @param int $oauthSessionAuthCodeId The session ID
+ * @return array
+ */
+ public function getAuthCodeScopes($oauthSessionAuthCodeId);
+
/**
* Associate a scope with an access token
*
diff --git a/tests/authorization/AuthCodeGrantTest.php b/tests/authorization/AuthCodeGrantTest.php
index 7cec3ded..ee6a5af9 100644
--- a/tests/authorization/AuthCodeGrantTest.php
+++ b/tests/authorization/AuthCodeGrantTest.php
@@ -376,7 +376,8 @@ class Auth_Code_Grant_Test extends PHPUnit_Framework_TestCase
$this->session->shouldReceive('createSession')->andReturn(1);
$this->session->shouldReceive('associateScope')->andReturn(null);
$this->session->shouldReceive('associateRedirectUri')->andReturn(null);
- $this->session->shouldReceive('associateAuthCode')->andReturn(null);
+ $this->session->shouldReceive('associateAuthCode')->andReturn(1);
+ $this->session->shouldReceive('associateAuthCodeScope')->andReturn(null);
$a = $this->returnDefault();
$g = new League\OAuth2\Server\Grant\AuthCode($a);
diff --git a/tests/authorization/AuthServerTest.php b/tests/authorization/AuthServerTest.php
index baf0af00..f4bd2db4 100644
--- a/tests/authorization/AuthServerTest.php
+++ b/tests/authorization/AuthServerTest.php
@@ -358,13 +358,14 @@ class Authorization_Server_test extends PHPUnit_Framework_TestCase
));
$this->session->shouldReceive('validateAuthCode')->andReturn(array(
- 'id' => 1,
- 'scope_ids' => '1'
+ 'session_id' => 1,
+ 'authcode_id' => 1
));
$this->session->shouldReceive('updateSession')->andReturn(null);
$this->session->shouldReceive('removeAuthCode')->andReturn(null);
$this->session->shouldReceive('associateAccessToken')->andReturn(1);
$this->session->shouldReceive('associateScope')->andReturn(null);
+ $this->session->shouldReceive('getAuthCodeScopes')->andReturn(array('scope_id' => 1));
$a = $this->returnDefault();
$a->addGrantType(new League\OAuth2\Server\Grant\AuthCode($a));
@@ -399,6 +400,8 @@ class Authorization_Server_test extends PHPUnit_Framework_TestCase
$this->session->shouldReceive('updateSession')->andReturn(null);
$this->session->shouldReceive('removeAuthCode')->andReturn(null);
$this->session->shouldReceive('associateAccessToken')->andReturn(1);
+ $this->session->shouldReceive('getAuthCodeScopes')->andReturn(array('scope_id' => 1));
+ $this->session->shouldReceive('associateScope')->andReturn(null);
$a = $this->returnDefault();
$a->addGrantType(new League\OAuth2\Server\Grant\AuthCode($a));
@@ -436,6 +439,8 @@ class Authorization_Server_test extends PHPUnit_Framework_TestCase
$this->session->shouldReceive('updateSession')->andReturn(null);
$this->session->shouldReceive('removeAuthCode')->andReturn(null);
$this->session->shouldReceive('associateAccessToken')->andReturn(1);
+ $this->session->shouldReceive('getAuthCodeScopes')->andReturn(array('scope_id' => 1));
+ $this->session->shouldReceive('associateScope')->andReturn(null);
$a = $this->returnDefault();
$grant = new League\OAuth2\Server\Grant\AuthCode($a);
@@ -477,6 +482,8 @@ class Authorization_Server_test extends PHPUnit_Framework_TestCase
$this->session->shouldReceive('updateSession')->andReturn(null);
$this->session->shouldReceive('removeAuthCode')->andReturn(null);
$this->session->shouldReceive('associateAccessToken')->andReturn(1);
+ $this->session->shouldReceive('getAuthCodeScopes')->andReturn(array('scope_id' => 1));
+ $this->session->shouldReceive('associateScope')->andReturn(null);
$a = $this->returnDefault();
$a->addGrantType(new League\OAuth2\Server\Grant\AuthCode($a));
diff --git a/tests/authorization/RefreshTokenTest.php b/tests/authorization/RefreshTokenTest.php
index 12dad385..f4882454 100644
--- a/tests/authorization/RefreshTokenTest.php
+++ b/tests/authorization/RefreshTokenTest.php
@@ -42,6 +42,8 @@ class Refresh_Token_test extends PHPUnit_Framework_TestCase
$this->session->shouldReceive('removeAuthCode')->andReturn(null);
$this->session->shouldReceive('associateAccessToken')->andReturn(1);
$this->session->shouldReceive('associateRefreshToken')->andReturn(1);
+ $this->session->shouldReceive('associateScope')->andReturn(null);
+ $this->session->shouldReceive('getAuthCodeScopes')->andReturn(array('scope_id' => 1));
$a = $this->returnDefault();
$a->addGrantType(new League\OAuth2\Server\Grant\AuthCode($a));