From 66bbc3bd7c196c6c447693a8b66be5199d4250ce Mon Sep 17 00:00:00 2001 From: Yvo Brevoort Date: Mon, 15 Dec 2025 20:24:09 +0100 Subject: [PATCH 1/3] add refresh token repository --- init.php | 6 ++ lib/Repositories/RefreshTokenRepository.php | 110 ++++++++++++++++++++ lib/Server.php | 12 ++- 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 lib/Repositories/RefreshTokenRepository.php diff --git a/init.php b/init.php index 52d3d89..965218d 100644 --- a/init.php +++ b/init.php @@ -45,6 +45,12 @@ function initDatabase() { type VARCHAR(255) NOT NULL, expires TEXT NOT NULL )', + 'CREATE TABLE IF NOT EXISTS oauth2Repository ( + type VARCHAR(255) NOT NULL, + key VARCHAR(255) NOT NULL, + value TEXT NOT NULL, + expires TEXT NOT NULL + )', ]; try { diff --git a/lib/Repositories/RefreshTokenRepository.php b/lib/Repositories/RefreshTokenRepository.php new file mode 100644 index 0000000..6c64b0b --- /dev/null +++ b/lib/Repositories/RefreshTokenRepository.php @@ -0,0 +1,110 @@ +getIdentifier() : string the linked access token’s identifier. + + JWT access tokens contain an expiry date and so will be rejected automatically when used. You can safely + clean up expired access tokens from your database. + /*/ + Db::connect(); + $query = Db::$pdo->prepare( + 'INSERT INTO oauth2Repository VALUES(:type, :key, :value, :expiry)' + ); + $query->execute([ + ':type' => 'refreshToken', + ':key' => $refreshTokenEntity->getIdentifier(), + ':value' => $refreshTokenEntity, + ':expires' => $refreshTokenEntit->getExpiryDateTime() + ]); + } + + /** + * Revoke the refresh token. + * + * @param string $tokenId + */ + public function revokeRefreshToken($tokenId) : void + { + /*/ + This method is called when a refresh token is used to reissue an access token. + + The original refresh token is revoked a new refresh token is issued. + /*/ + Db::connect(); + $now = new \DateTime(); + $query = Db::$pdo->prepare( + 'DELETE FROM oauth2Repository WHERE type=:type AND key = :key' + ); + $query->execute([ + ':type' => 'refreshToken', + ':key' => $tokenId + ]); + } + + /** + * Check if the refresh token has been revoked. + * + * @param string $tokenId + * + * @return bool Return true if this token has been revoked + */ + public function isRefreshTokenRevoked($tokenId) : bool + { + /*/ + This method is called when an refresh token is used to issue a new access token. + + Return true if the refresh token has been manually revoked before it expired. + If the token is still valid return false. + /*/ + return false; + } + + public static function cleanup() { + Db::connect(); + $now = new \DateTime(); + $query = Db::$pdo->prepare( + 'DELETE FROM oauth2Repository WHERE type=:type AND expires < :now' + ); + $query->execute([ + ':type' => 'refreshToken', + ':now' => $now->getTimestamp() + ]); + } +} \ No newline at end of file diff --git a/lib/Server.php b/lib/Server.php index 9913ece..37e5189 100644 --- a/lib/Server.php +++ b/lib/Server.php @@ -2,6 +2,7 @@ namespace Pdsinterop\PhpSolid; use Pdsinterop\Solid\Auth\Factory\AuthorizationServerFactory; + use Pdsinterop\Solid\Auth\Factory\RepositoryFactory; use Laminas\Diactoros\Response; use Pdsinterop\Solid\Auth\Server as SolidAuthServer; use Pdsinterop\Solid\Auth\Factory\ConfigFactory; @@ -13,6 +14,7 @@ use Pdsinterop\Solid\Auth\TokenGenerator; use Pdsinterop\PhpSolid\ClientRegistration; use Pdsinterop\PhpSolid\JtiStore; + use Pdsinterop\Solid\Auth\Enum\Repository; class Server { public static function generateKeySet() { @@ -40,6 +42,8 @@ public static function generateKeySet() { public static function getAuthServer() { $authServerConfig = self::getAuthServerConfig(); $authServerFactory = new AuthorizationServerFactory($authServerConfig); + $repositoryFactory = self::getAuthServerRepositoryFactory(); + $authServerFactory->setRepositoryFactory($repositoryFactory); $authServer = $authServerFactory->create(); $response = new Response(); $server = new SolidAuthServer($authServer, $authServerConfig, $response); @@ -60,7 +64,13 @@ public static function getAuthServerConfig() { $authServerConfig = $authServerConfigFactory->create(); return $authServerConfig; } - + + public static function getAuthServerRepositoryFactory() { + return new RepositoryFactory([ + Repository::REFRESH_TOKEN => new RefreshTokenRepository() + ]); + } + public static function getConfigClient() { $clientId = $_GET['client_id'] ?? $_POST['client_id'] ?? null; if ($clientId) { From 10cde5a9bac9ee9bc453ff58fa61f2398f5a2e33 Mon Sep 17 00:00:00 2001 From: Yvo Brevoort Date: Mon, 15 Dec 2025 20:50:50 +0100 Subject: [PATCH 2/3] fix paths and imports --- .../RefreshTokenRepository.php => Repository/RefreshToken.php} | 0 lib/Server.php | 1 + 2 files changed, 1 insertion(+) rename lib/{Repositories/RefreshTokenRepository.php => Repository/RefreshToken.php} (100%) diff --git a/lib/Repositories/RefreshTokenRepository.php b/lib/Repository/RefreshToken.php similarity index 100% rename from lib/Repositories/RefreshTokenRepository.php rename to lib/Repository/RefreshToken.php diff --git a/lib/Server.php b/lib/Server.php index 37e5189..bf03a9d 100644 --- a/lib/Server.php +++ b/lib/Server.php @@ -15,6 +15,7 @@ use Pdsinterop\PhpSolid\ClientRegistration; use Pdsinterop\PhpSolid\JtiStore; use Pdsinterop\Solid\Auth\Enum\Repository; + use Pdsinterop\PhpSolid\Repository\RefreshToken as RefreshTokenRepository; class Server { public static function generateKeySet() { From 4b436442444811defbf623ed73ffaa6148913aff Mon Sep 17 00:00:00 2001 From: Yvo Brevoort Date: Mon, 15 Dec 2025 21:40:50 +0100 Subject: [PATCH 3/3] typofix, working store now. just the wrong values --- composer.json | 2 +- lib/Repository/RefreshToken.php | 9 +++++---- lib/Server.php | 9 +-------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 1b4d9e0..2295147 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "pdsinterop/solid-auth": "v0.13.0", + "pdsinterop/solid-auth": "dev-feature/configurable-repositoryFactory", "pdsinterop/solid-crud": "v0.8.1", "phpmailer/phpmailer": "^6.10", "sweetrdf/easyrdf": "~1.15.0", diff --git a/lib/Repository/RefreshToken.php b/lib/Repository/RefreshToken.php index 6c64b0b..ce532e4 100644 --- a/lib/Repository/RefreshToken.php +++ b/lib/Repository/RefreshToken.php @@ -47,11 +47,12 @@ public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshToken $query = Db::$pdo->prepare( 'INSERT INTO oauth2Repository VALUES(:type, :key, :value, :expiry)' ); + // FIXME: value should not be the identifier of the refresh token, but the refresh token itself? $query->execute([ ':type' => 'refreshToken', ':key' => $refreshTokenEntity->getIdentifier(), - ':value' => $refreshTokenEntity, - ':expires' => $refreshTokenEntit->getExpiryDateTime() + ':value' => $refreshTokenEntity->getAccessToken()->getIdentifier(), + ':expiry' => $refreshTokenEntity->getExpiryDateTime()->getTimestamp() ]); } @@ -70,7 +71,7 @@ public function revokeRefreshToken($tokenId) : void Db::connect(); $now = new \DateTime(); $query = Db::$pdo->prepare( - 'DELETE FROM oauth2Repository WHERE type=:type AND key = :key' + 'DELETE FROM oauth2Repository WHERE type = :type AND key = :key' ); $query->execute([ ':type' => 'refreshToken', @@ -100,7 +101,7 @@ public static function cleanup() { Db::connect(); $now = new \DateTime(); $query = Db::$pdo->prepare( - 'DELETE FROM oauth2Repository WHERE type=:type AND expires < :now' + 'DELETE FROM oauth2Repository WHERE type = :type AND expires < :now' ); $query->execute([ ':type' => 'refreshToken', diff --git a/lib/Server.php b/lib/Server.php index bf03a9d..bd2393b 100644 --- a/lib/Server.php +++ b/lib/Server.php @@ -43,8 +43,7 @@ public static function generateKeySet() { public static function getAuthServer() { $authServerConfig = self::getAuthServerConfig(); $authServerFactory = new AuthorizationServerFactory($authServerConfig); - $repositoryFactory = self::getAuthServerRepositoryFactory(); - $authServerFactory->setRepositoryFactory($repositoryFactory); + $authServerFactory->setRepository(Repository::REFRESH_TOKEN, new RefreshTokenRepository()); $authServer = $authServerFactory->create(); $response = new Response(); $server = new SolidAuthServer($authServer, $authServerConfig, $response); @@ -66,12 +65,6 @@ public static function getAuthServerConfig() { return $authServerConfig; } - public static function getAuthServerRepositoryFactory() { - return new RepositoryFactory([ - Repository::REFRESH_TOKEN => new RefreshTokenRepository() - ]); - } - public static function getConfigClient() { $clientId = $_GET['client_id'] ?? $_POST['client_id'] ?? null; if ($clientId) {